diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 539b4cdd5c..0000000000 --- a/.eslintignore +++ /dev/null @@ -1 +0,0 @@ -submodules/ diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 3b909f8fd4..0000000000 --- a/.eslintrc +++ /dev/null @@ -1,25 +0,0 @@ -{ - "env": { - "browser": true, - "es2017": true - }, - "extends": [ - "standard" - ], - "globals": { - "__firefox__": "readonly", - "SECURITY_TOKEN": "readonly", - "$FEATURE_SETTINGS$": "readonly", - "$GPC_ENABLED$": "readonly", - "$BLOCKING_ENABLED$": "readonly", - "$TRACKER_DATA$": "readonly", - "$IS_DEBUG$": "readonly", - "webkit": "readonly" - }, - "parserOptions": { - "ecmaVersion": 7 - }, - "rules": { - "indent": ["error", 4] - } -} diff --git a/.maestro/onboarding_tests/01_control_group_onboarding.yaml b/.maestro/onboarding_tests/01_control_group_onboarding.yaml deleted file mode 100644 index 609254b45a..0000000000 --- a/.maestro/onboarding_tests/01_control_group_onboarding.yaml +++ /dev/null @@ -1,29 +0,0 @@ -appId: com.duckduckgo.mobile.ios -tags: - - onboarding - ---- - -# Set up -- runFlow: - file: ../shared/setup.yaml - env: - ONBOARDING_COMPLETED: "false" - APP_VARIANT: "ma" - -# Load Site -- assertVisible: - id: "searchEntry" -- tapOn: - id: "searchEntry" -- inputText: "https://www.duckduckgo.com" -- pressKey: Enter - -# Handle Onboarding -- assertVisible: "Got It" -- assertVisible: "Hide" -- tapOn: "Got It" -- assertVisible: "Close Tabs and Clear Data" -- tapOn: "Close Tabs and Clear Data" -- tapOn: "Close Tabs and Clear Data" -- assertVisible: "You’ve got this!\n\nRemember: Every time you browse with me, a creepy ad loses its wings. 👍" diff --git a/.maestro/onboarding_tests/03_experiment_group_linear_onboarding.yaml b/.maestro/onboarding_tests/01_onboarding_contextual.yaml similarity index 97% rename from .maestro/onboarding_tests/03_experiment_group_linear_onboarding.yaml rename to .maestro/onboarding_tests/01_onboarding_contextual.yaml index 5a86cdfd17..d5fe31dc01 100644 --- a/.maestro/onboarding_tests/03_experiment_group_linear_onboarding.yaml +++ b/.maestro/onboarding_tests/01_onboarding_contextual.yaml @@ -9,7 +9,7 @@ tags: file: ../shared/setup.yaml env: ONBOARDING_COMPLETED: "false" - APP_VARIANT: "mb" + APP_VARIANT: "mh" # Handle Search Suggestions - assertVisible: "Ready to get started?\nTry a search!" @@ -50,3 +50,4 @@ tags: - assertVisible: "You’ve got this!" - assertVisible: "High five!" - tapOn: "High five!" + diff --git a/.maestro/onboarding_tests/02_control_group_hide_onboarding.yaml b/.maestro/onboarding_tests/02_control_group_hide_onboarding.yaml deleted file mode 100644 index a717b35c3a..0000000000 --- a/.maestro/onboarding_tests/02_control_group_hide_onboarding.yaml +++ /dev/null @@ -1,33 +0,0 @@ -appId: com.duckduckgo.mobile.ios -tags: - - onboarding - ---- - -# Set up -- runFlow: - file: ../shared/setup.yaml - env: - ONBOARDING_COMPLETED: "false" - APP_VARIANT: "ma" - -# Load Site -- assertVisible: - id: "searchEntry" -- tapOn: - id: "searchEntry" -- inputText: "https://www.duckduckgo.com" -- pressKey: Enter - -# Handle Onboarding -- assertVisible: "Got It" -- assertVisible: "Hide" -- tapOn: "Hide" -- assertVisible: "Hide Tips Forever" -- tapOn: "Hide Tips Forever" - -# Handle Fire Button -- assertVisible: "Close Tabs and Clear Data" -- tapOn: "Close Tabs and Clear Data" -- tapOn: "Close Tabs and Clear Data" -- assertNotVisible: "You’ve got this!\n\nRemember: Every time you browse with me, a creepy ad loses its wings. 👍" diff --git a/.maestro/onboarding_tests/02_onboarding_add_to_dock_intro.yaml b/.maestro/onboarding_tests/02_onboarding_add_to_dock_intro.yaml new file mode 100644 index 0000000000..8a354f5707 --- /dev/null +++ b/.maestro/onboarding_tests/02_onboarding_add_to_dock_intro.yaml @@ -0,0 +1,54 @@ +appId: com.duckduckgo.mobile.ios +tags: + - onboarding + +--- + +# Set up +- runFlow: + file: ../shared/setup.yaml + env: + ONBOARDING_COMPLETED: "false" + APP_VARIANT: "mk" + +# Handle Search Suggestions +- assertVisible: "Ready to get started?\nTry a search!" +- assertVisible: "Surprise Me!" +- tapOn: "Surprise Me!" + +# Handle First Dax Dialog +- assertVisible: "That’s DuckDuckGo Search. Private. Fast. Fewer ads." +- assertVisible: "Got It!" +- tapOn: "Got It!" + +# Handle Site Suggestions +- assertVisible: "Next, try visiting a site!" +- assertVisible: "Surprise Me!" +- tapOn: "Surprise Me!" + +# Handle Privacy Dashboard +- assertVisible: "Got It!" +- tapOn: + point: "6%,10%" # Shield icon. +- assertVisible: + text: "View Tracker Companies" +- assertVisible: + text: "Done" +- tapOn: "Done" + +# Handle Fire Message +- assertVisible: "Got It!" +- tapOn: "Got It!" +- assertVisible: "Instantly clear your browsing activity with the Fire Button.\n\nGive it a try! 🔥" + +# Handle Fire Button +- assertVisible: "Close Tabs and Clear Data" +- tapOn: "Close Tabs and Clear Data" +- tapOn: "Close Tabs and Clear Data" + +# Handle End of Journey Dialog +- assertVisible: "You’ve got this!" +- assertVisible: "High five!" +- tapOn: "High five!" + + diff --git a/.maestro/onboarding_tests/03_onboarding_add_to_dock_contextual.yaml b/.maestro/onboarding_tests/03_onboarding_add_to_dock_contextual.yaml new file mode 100644 index 0000000000..967ad2d11d --- /dev/null +++ b/.maestro/onboarding_tests/03_onboarding_add_to_dock_contextual.yaml @@ -0,0 +1,54 @@ +appId: com.duckduckgo.mobile.ios +tags: + - onboarding + +--- + +# Set up +- runFlow: + file: ../shared/setup.yaml + env: + ONBOARDING_COMPLETED: "false" + APP_VARIANT: "mo" + +# Handle Search Suggestions +- assertVisible: "Ready to get started?\nTry a search!" +- assertVisible: "Surprise Me!" +- tapOn: "Surprise Me!" + +# Handle First Dax Dialog +- assertVisible: "That’s DuckDuckGo Search. Private. Fast. Fewer ads." +- assertVisible: "Got It!" +- tapOn: "Got It!" + +# Handle Site Suggestions +- assertVisible: "Next, try visiting a site!" +- assertVisible: "Surprise Me!" +- tapOn: "Surprise Me!" + +# Handle Privacy Dashboard +- assertVisible: "Got It!" +- tapOn: + point: "6%,10%" # Shield icon. +- assertVisible: + text: "View Tracker Companies" +- assertVisible: + text: "Done" +- tapOn: "Done" + +# Handle Fire Message +- assertVisible: "Got It!" +- tapOn: "Got It!" +- assertVisible: "Instantly clear your browsing activity with the Fire Button.\n\nGive it a try! 🔥" + +# Handle Fire Button +- assertVisible: "Close Tabs and Clear Data" +- tapOn: "Close Tabs and Clear Data" +- tapOn: "Close Tabs and Clear Data" + +# Handle End of Journey Dialog +- assertVisible: "Add me to your Dock!" +- assertVisible: "Show Me How" +- tapOn: "Start Browsing" + + diff --git a/.maestro/release_tests/widgets.yaml b/.maestro/release_tests/widgets.yaml index 491c82b079..0c3ba823c4 100644 --- a/.maestro/release_tests/widgets.yaml +++ b/.maestro/release_tests/widgets.yaml @@ -30,10 +30,14 @@ appId: com.duckduckgo.mobile.ios # Validate search widget - longPressOn: point: 50%,50% + +# iOS 18 now has an edit button first +- tapOn: "Edit" - tapOn: "Add Widget" - tapOn: "Search Widgets" -- inputText: "DuckDuck" -- tapOn: "DuckDuckGo" +- inputText: "DuckDuckGo" +- tapOn: + point: 30%,30% - tapOn: " Add Widget" - tapOn: "Done" - tapOn: "DuckDuckGo" @@ -48,10 +52,12 @@ appId: com.duckduckgo.mobile.ios - pressKey: HOME - longPressOn: point: 50%,50% +- tapOn: "Edit" - tapOn: "Add Widget" - tapOn: "Search Widgets" -- inputText: "DuckDuck" -- tapOn: "DuckDuckGo" +- inputText: "DuckDuckGo" +- tapOn: + point: 30%,30% - assertVisible: "Search" - swipe: start: 90%, 50% diff --git a/.maestro/shared/onboarding.yaml b/.maestro/shared/onboarding.yaml index 2117d4205b..154954b742 100644 --- a/.maestro/shared/onboarding.yaml +++ b/.maestro/shared/onboarding.yaml @@ -10,17 +10,24 @@ appId: com.duckduckgo.mobile.ios text: "Let’s Do It!" index: 0 -# Disabled while UI testing is happening -# - assertVisible: "Make DuckDuckGo your default browser." +# Browser comparison chart +# - assertVisible: "Protections activated!" - tapOn: text: "Skip" +# Add To Dock Flow - runFlow: when: - visible: "Which color looks best on me?" + visible: "Add me to your Dock!" commands: - - assertVisible: "Next" - - tapOn: "Next" - - assertVisible: "Where should I put your address bar?" - - assertVisible: "Next" - - tapOn: "Next" + - assertVisible: "Show Me How" + - tapOn: "Skip" + +# Customization Flow + +- assertVisible: "Which color looks best on me?" +- assertVisible: "Next" +- tapOn: "Next" +- assertVisible: "Where should I put your address bar?" +- assertVisible: "Next" +- tapOn: "Next" diff --git a/AutofillCredentialProvider/AutofillCredentialProvider.entitlements b/AutofillCredentialProvider/AutofillCredentialProvider.entitlements new file mode 100644 index 0000000000..f7959669d1 --- /dev/null +++ b/AutofillCredentialProvider/AutofillCredentialProvider.entitlements @@ -0,0 +1,18 @@ + + + + + com.apple.developer.authentication-services.autofill-credential-provider + + com.apple.security.application-groups + + $(GROUP_ID_PREFIX).vault + $(GROUP_ID_PREFIX).bookmarks + + keychain-access-groups + + $(AppIdentifierPrefix)$(APP_ID) + $(AppIdentifierPrefix)$(VAULT_APP_GROUP) + + + diff --git a/AutofillCredentialProvider/AutofillCredentialProviderAlpha.entitlements b/AutofillCredentialProvider/AutofillCredentialProviderAlpha.entitlements new file mode 100644 index 0000000000..f7959669d1 --- /dev/null +++ b/AutofillCredentialProvider/AutofillCredentialProviderAlpha.entitlements @@ -0,0 +1,18 @@ + + + + + com.apple.developer.authentication-services.autofill-credential-provider + + com.apple.security.application-groups + + $(GROUP_ID_PREFIX).vault + $(GROUP_ID_PREFIX).bookmarks + + keychain-access-groups + + $(AppIdentifierPrefix)$(APP_ID) + $(AppIdentifierPrefix)$(VAULT_APP_GROUP) + + + diff --git a/AutofillCredentialProvider/CredentialProvider/CredentialProviderActivation/CredentialProviderActivatedView.swift b/AutofillCredentialProvider/CredentialProvider/CredentialProviderActivation/CredentialProviderActivatedView.swift new file mode 100644 index 0000000000..fe36f9e3b2 --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/CredentialProviderActivation/CredentialProviderActivatedView.swift @@ -0,0 +1,74 @@ +// +// CredentialProviderActivatedView.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import SwiftUI +import DesignResourcesKit +import DuckUI + +struct CredentialProviderActivatedView: View { + + let viewModel: CredentialProviderActivatedViewModel + @State private var imageAppeared = false + + var body: some View { + NavigationView { + + VStack(spacing: 0) { + + Image(.passwordsDDG96X96) + .padding(.top, 48) + .scaleEffect(imageAppeared ? 1 : 0.7) + .animation( + .interpolatingSpring(stiffness: 170, damping: 10) + .delay(0.1), + value: imageAppeared + ) + .onAppear { + imageAppeared = true + } + + Text(UserText.credentialProviderActivatedTitle) + .daxTitle2() + .foregroundColor(Color(designSystemColor: .textPrimary)) + .padding(.top, 16) + .multilineTextAlignment(.center) + + Spacer() + + Button { + viewModel.launchDDGApp() + } label: { + Text(UserText.credentialProviderActivatedButton) + } + .buttonStyle(PrimaryButtonStyle()) + .padding(.bottom, 12) + + } + .padding(.horizontal, 24) + .navigationBarItems(trailing: Button(UserText.actionDone) { + viewModel.dismiss() + }) + } + } + +} + +#Preview { + CredentialProviderActivatedView(viewModel: CredentialProviderActivatedViewModel()) +} diff --git a/AutofillCredentialProvider/CredentialProvider/CredentialProviderActivation/CredentialProviderActivatedViewModel.swift b/AutofillCredentialProvider/CredentialProvider/CredentialProviderActivation/CredentialProviderActivatedViewModel.swift new file mode 100644 index 0000000000..bdf4eba455 --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/CredentialProviderActivation/CredentialProviderActivatedViewModel.swift @@ -0,0 +1,42 @@ +// +// CredentialProviderActivatedViewModel.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import Core + +struct CredentialProviderActivatedViewModel { + + typealias LaunchAppCompletion = (_ shouldLaunchApp: Bool) -> Void + + let completion: LaunchAppCompletion? + + init(completion: LaunchAppCompletion? = nil) { + self.completion = completion + } + + func dismiss() { + Pixel.fire(pixel: .autofillExtensionWelcomeDismiss) + completion?(false) + } + + func launchDDGApp() { + Pixel.fire(pixel: .autofillExtensionWelcomeLaunchApp) + completion?(true) + } +} diff --git a/AutofillCredentialProvider/CredentialProvider/CredentialProviderList/CredentialProviderListItemTableViewCell.swift b/AutofillCredentialProvider/CredentialProvider/CredentialProviderList/CredentialProviderListItemTableViewCell.swift new file mode 100644 index 0000000000..a0701ec178 --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/CredentialProviderList/CredentialProviderListItemTableViewCell.swift @@ -0,0 +1,151 @@ +// +// CredentialProviderListItemTableViewCell.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import Core +import DesignResourcesKit + +class CredentialProviderListItemTableViewCell: UITableViewCell { + + static var reuseIdentifier = "CredentialProviderListItemTableViewCell" + + var disclosureButtonTapped: (() -> Void)? + + private lazy var titleLabel: UILabel = { + let label = UILabel(frame: CGRect.zero) + label.font = .preferredFont(forTextStyle: .callout) + label.textColor = .init(designSystemColor: .textPrimary) + label.lineBreakMode = .byTruncatingMiddle + return label + }() + + private lazy var subtitleLabel: UILabel = { + let label = UILabel(frame: CGRect.zero) + label.font = .preferredFont(forTextStyle: .footnote) + label.textColor = .init(designSystemColor: .textPrimary) + label.lineBreakMode = .byTruncatingMiddle + return label + }() + + private lazy var iconImageView: UIImageView = { + let imageView = UIImageView() + imageView.contentMode = .scaleAspectFill + imageView.layer.masksToBounds = true + imageView.layer.cornerRadius = 4 + return imageView + }() + + private lazy var textStackView: UIStackView = { + let stackView = UIStackView(arrangedSubviews: [titleLabel, subtitleLabel]) + stackView.axis = .vertical + stackView.distribution = .fill + stackView.spacing = 3 + return stackView + }() + + private lazy var contentStackView: UIStackView = { + let stackView = UIStackView(arrangedSubviews: [iconImageView, textStackView]) + stackView.axis = .horizontal + stackView.spacing = 12 + stackView.alignment = .center + return stackView + }() + + private lazy var disclosureButton: UIButton = { + let button = UIButton(type: .system) + let image = UIImage(systemName: "chevron.forward") + let boldImage = image?.withConfiguration(UIImage.SymbolConfiguration(pointSize: 11, weight: .bold)) + button.setImage(boldImage, for: .normal) + button.tintColor = UIColor.tertiaryLabel + button.addTarget(self, action: #selector(handleDisclosureButtonTap), for: .touchUpInside) + + let buttonSize: CGFloat = 44 + button.frame = CGRect(x: 0, y: 0, width: buttonSize, height: buttonSize) + button.contentHorizontalAlignment = .center + button.contentVerticalAlignment = .center + + return button + }() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + installSubviews() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + var item: AutofillLoginItem? { + didSet { + guard let item = item else { + return + } + setupContentView(with: item) + } + } + + private func installSubviews() { + contentView.addSubview(contentStackView) + contentView.addSubview(disclosureButton) + installConstraints() + } + + private func installConstraints() { + contentStackView.translatesAutoresizingMaskIntoConstraints = false + iconImageView.translatesAutoresizingMaskIntoConstraints = false + disclosureButton.translatesAutoresizingMaskIntoConstraints = false + + let imageSize: CGFloat = 32 + let margins = contentView.layoutMarginsGuide + + NSLayoutConstraint.activate([ + iconImageView.widthAnchor.constraint(equalToConstant: imageSize), + iconImageView.heightAnchor.constraint(equalToConstant: imageSize), + + disclosureButton.widthAnchor.constraint(equalToConstant: 44), + disclosureButton.heightAnchor.constraint(equalToConstant: 44), + disclosureButton.centerYAnchor.constraint(equalTo: margins.centerYAnchor), + disclosureButton.trailingAnchor.constraint(equalTo: margins.trailingAnchor, constant: 16), + + contentStackView.leadingAnchor.constraint(equalTo: margins.leadingAnchor), + contentStackView.trailingAnchor.constraint(equalTo: disclosureButton.leadingAnchor, constant: -12), + contentStackView.topAnchor.constraint(equalTo: margins.topAnchor), + contentStackView.bottomAnchor.constraint(equalTo: margins.bottomAnchor) + ]) + } + + private func setupContentView(with item: AutofillLoginItem) { + titleLabel.text = item.title + subtitleLabel.text = item.subtitle + iconImageView.image = FaviconHelper.loadImageFromCache(forDomain: item.account.domain, preferredFakeFaviconLetters: item.preferredFaviconLetters) + } + + override func layoutSubviews() { + super.layoutSubviews() + contentStackView.frame = contentView.bounds + + separatorInset = UIEdgeInsets(top: 0, left: contentView.layoutMargins.left + textStackView.frame.origin.x, bottom: 0, right: 0) + } + + @objc private func handleDisclosureButtonTap() { + disclosureButtonTapped?() + } + +} diff --git a/AutofillCredentialProvider/CredentialProvider/CredentialProviderList/CredentialProviderListViewController.swift b/AutofillCredentialProvider/CredentialProvider/CredentialProviderList/CredentialProviderListViewController.swift new file mode 100644 index 0000000000..6094773e0c --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/CredentialProviderList/CredentialProviderListViewController.swift @@ -0,0 +1,430 @@ +// +// CredentialProviderListViewController.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import AuthenticationServices +import BrowserServicesKit +import Combine +import Common +import Core +import SwiftUI + +final class CredentialProviderListViewController: UIViewController { + + private let viewModel: CredentialProviderListViewModel + private let shouldProvideTextToInsert: Bool + private let tld: TLD + private let onRowSelected: (AutofillLoginItem) -> Void + private let onTextProvided: (String) -> Void + private let onDismiss: () -> Void + private var cancellables: Set = [] + + private lazy var tableView: UITableView = { + let tableView = UITableView(frame: .zero, style: .insetGrouped) + tableView.delegate = self + tableView.dataSource = self + tableView.estimatedRowHeight = 60 + tableView.register(CredentialProviderListItemTableViewCell.self, forCellReuseIdentifier: CredentialProviderListItemTableViewCell.reuseIdentifier) + return tableView + }() + + private lazy var searchController: UISearchController = { + let searchController = UISearchController(searchResultsController: nil) + searchController.searchResultsUpdater = self + searchController.searchBar.delegate = self + searchController.obscuresBackgroundDuringPresentation = false + searchController.searchBar.placeholder = UserText.credentialProviderListSearchPlaceholder + navigationItem.hidesSearchBarWhenScrolling = false + definesPresentationContext = true + + return searchController + }() + + private lazy var lockedView = { [weak self] in + let view = LockScreenView() + let hostingController = UIHostingController(rootView: view) + self?.installChildViewController(hostingController) + return hostingController.view ?? UIView() + }() + + private let emptySearchView = EmptySearchView() + + private lazy var emptyView: UIView = { [weak self] in + let emptyView = EmptyView() + + let hostingController = UIHostingController(rootView: emptyView) + self?.installChildViewController(hostingController) + hostingController.view.backgroundColor = .clear + return hostingController.view + }() + + private lazy var emptySearchViewCenterYConstraint: NSLayoutConstraint = { + NSLayoutConstraint(item: emptySearchView, + attribute: .centerY, + relatedBy: .equal, + toItem: tableView, + attribute: .top, + multiplier: 1, + constant: (tableView.frame.height / 2)) + }() + + init(serviceIdentifiers: [ASCredentialServiceIdentifier], + secureVault: (any AutofillSecureVault)?, + credentialIdentityStoreManager: AutofillCredentialIdentityStoreManaging, + shouldProvideTextToInsert: Bool, + tld: TLD, + onRowSelected: @escaping (AutofillLoginItem) -> Void, + onTextProvided: @escaping (String) -> Void, + onDismiss: @escaping () -> Void) { + self.viewModel = CredentialProviderListViewModel(serviceIdentifiers: serviceIdentifiers, + secureVault: secureVault, + credentialIdentityStoreManager: credentialIdentityStoreManager, + tld: tld) + self.shouldProvideTextToInsert = shouldProvideTextToInsert + self.tld = tld + self.onRowSelected = onRowSelected + self.onTextProvided = onTextProvided + self.onDismiss = onDismiss + + super.init(nibName: nil, bundle: nil) + + if #available(iOS 18.0, *) { + authenticate() + } else { + // pre-iOS 18.0 authentication can fail silently if extension is loaded twice in quick succession + // if authenticate is called without a slight delay + DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) { [weak self] in + self?.authenticate() + } + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + + title = UserText.credentialProviderListTitle + + if let itemPrompt = viewModel.serviceIdentifierPromptLabel { + navigationItem.prompt = itemPrompt + } + + let doneItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneTapped)) + navigationItem.rightBarButtonItem = doneItem + + setupCancellables() + installSubviews() + installConstraints() + decorate() + updateViewState() + registerForKeyboardNotifications() + + navigationItem.searchController = searchController + + Pixel.fire(pixel: .autofillExtensionPasswordsOpened) + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + viewModel.authenticateInvalidateContext() + } + + override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { + super.viewWillTransition(to: size, with: coordinator) + + coordinator.animate(alongsideTransition: { _ in + if !self.searchController.isActive { + self.navigationItem.searchController = nil + } + }, completion: { _ in + self.updateSearchController() + }) + } + + private func decorate() { + view.backgroundColor = UIColor(designSystemColor: .background) + tableView.backgroundColor = UIColor(designSystemColor: .background) + tableView.separatorColor = UIColor(designSystemColor: .lines) + tableView.sectionIndexColor = UIColor(designSystemColor: .accent) + + navigationController?.navigationBar.barTintColor = UIColor(designSystemColor: .panel) + navigationController?.navigationBar.tintColor = UIColor(designSystemColor: .textPrimary) + + let appearance = UINavigationBarAppearance() + appearance.shadowColor = .clear + appearance.backgroundColor = UIColor(designSystemColor: .background) + + navigationController?.navigationBar.standardAppearance = appearance + navigationController?.navigationBar.scrollEdgeAppearance = appearance + + tableView.reloadData() + } + + private func authenticate() { + viewModel.authenticate {[weak self] error in + guard let self = self else { return } + + if error != nil { + if error != .noAuthAvailable { + self.onDismiss() + } else { + let alert = UIAlertController.makeDeviceAuthenticationAlert { [weak self] in + self?.onDismiss() + } + present(alert, animated: true) + } + } + } + } + + private func installSubviews() { + view.addSubview(tableView) + tableView.addSubview(emptySearchView) + view.addSubview(lockedView) + } + + private func installConstraints() { + tableView.translatesAutoresizingMaskIntoConstraints = false + emptySearchView.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + tableView.leftAnchor.constraint(equalTo: view.leftAnchor), + tableView.rightAnchor.constraint(equalTo: view.rightAnchor), + tableView.topAnchor.constraint(equalTo: view.topAnchor), + tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor), + + emptySearchView.centerXAnchor.constraint(equalTo: tableView.centerXAnchor), + emptySearchViewCenterYConstraint, + emptySearchView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16), + emptySearchView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16), + ]) + } + + private func setupCancellables() { + viewModel.$viewState + .receive(on: DispatchQueue.main) + .sink { [weak self] _ in + self?.updateViewState() + } + .store(in: &cancellables) + + viewModel.$sections + .receive(on: DispatchQueue.main) + .sink { [weak self] _ in + self?.tableView.reloadData() + } + .store(in: &cancellables) + } + + private func updateViewState() { + + switch viewModel.viewState { + case .showItems: + tableView.isHidden = false + lockedView.isHidden = true + emptySearchView.isHidden = true + emptyView.isHidden = true + case .noAuthAvailable: + tableView.isHidden = true + lockedView.isHidden = true + emptySearchView.isHidden = true + emptyView.isHidden = true + case .authLocked: + tableView.isHidden = true + lockedView.isHidden = false + emptySearchView.isHidden = true + emptyView.isHidden = true + case .empty: + tableView.isHidden = true + lockedView.isHidden = true + emptySearchView.isHidden = true + emptyView.isHidden = false + case .searching: + tableView.isHidden = false + lockedView.isHidden = true + emptySearchView.isHidden = true + emptyView.isHidden = true + case .searchingNoResults: + tableView.isHidden = false + lockedView.isHidden = true + emptySearchView.isHidden = false + emptyView.isHidden = true + } + updateSearchController() + tableView.reloadData() + } + + private func updateSearchController() { + switch viewModel.viewState { + case .showItems: + if tableView.isEditing { + navigationItem.searchController = nil + } else { + navigationItem.searchController = searchController + } + case .searching, .searchingNoResults: + navigationItem.searchController = searchController + case .authLocked: + navigationItem.searchController = viewModel.hasAccountsSaved ? searchController : nil + case .empty, .noAuthAvailable: + navigationItem.searchController = nil + } + } + + @objc private func doneTapped() { + onDismiss() + Pixel.fire(pixel: .autofillExtensionPasswordsDismissed) + } + +} + +extension CredentialProviderListViewController: UITableViewDataSource { + + func numberOfSections(in tableView: UITableView) -> Int { + viewModel.sections.count + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + viewModel.rowsInSection(section) + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + switch viewModel.sections[indexPath.section] { + case .suggestions(_, items: let items), .credentials(_, let items): + guard let cell = tableView.dequeueReusableCell(withIdentifier: CredentialProviderListItemTableViewCell.reuseIdentifier, + for: indexPath) as? CredentialProviderListItemTableViewCell else { + fatalError("Could not dequeue cell") + } + cell.item = items[indexPath.row] + cell.backgroundColor = UIColor(designSystemColor: .surface) + + cell.disclosureButtonTapped = { [weak self] in + let item = items[indexPath.row] + self?.presentDetailsForCredentials(item: item) + } + return cell + default: + return UITableViewCell() + } + } + + func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + switch viewModel.sections[section] { + case .suggestions(let title, _), .credentials(let title, _): + return title + default: + return nil + } + } + + func sectionIndexTitles(for tableView: UITableView) -> [String]? { + viewModel.viewState == .showItems ? UILocalizedIndexedCollation.current().sectionIndexTitles : [] + } + + private func presentDetailsForCredentials(item: AutofillLoginItem) { + let detailViewController = CredentialProviderListDetailsViewController(account: item.account, + tld: tld, + shouldProvideTextToInsert: self.shouldProvideTextToInsert) + detailViewController.delegate = self + + self.navigationController?.pushViewController(detailViewController, animated: true) + } +} + +extension CredentialProviderListViewController: UITableViewDelegate { + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + tableView.deselectRow(at: indexPath, animated: true) + switch viewModel.sections[indexPath.section] { + case .suggestions(_, items: let items), .credentials(_, let items): + let item = items[indexPath.row] + if shouldProvideTextToInsert { + presentDetailsForCredentials(item: item) + } else { + onRowSelected(item) + Pixel.fire(pixel: .autofillExtensionPasswordSelected) + } + default: + return + } + } + +} + +extension CredentialProviderListViewController: UISearchResultsUpdating { + + func updateSearchResults(for searchController: UISearchController) { + viewModel.isSearching = searchController.isActive + + if let query = searchController.searchBar.text { + viewModel.filterData(with: query) + emptySearchView.query = query + tableView.reloadData() + } + } + +} + +extension CredentialProviderListViewController: UISearchBarDelegate { + + func searchBarCancelButtonClicked(_ searchBar: UISearchBar) { + viewModel.isSearching = false + + viewModel.filterData(with: "") + tableView.reloadData() + } + +} + +// MARK: Keyboard + +extension CredentialProviderListViewController { + + private func registerForKeyboardNotifications() { + NotificationCenter.default.addObserver(self, + selector: #selector(adjustForKeyboard), + name: UIResponder.keyboardWillChangeFrameNotification, + object: nil) + } + + @objc private func adjustForKeyboard(notification: NSNotification) { + guard let keyboardValue = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else { + return + } + + let keyboardScreenEndFrame = keyboardValue.cgRectValue + let keyboardViewEndFrame = view.convert(keyboardScreenEndFrame, from: view.window) + + emptySearchViewCenterYConstraint.constant = min( + (keyboardViewEndFrame.minY + emptySearchView.frame.height) / 2 - searchController.searchBar.frame.height, + (tableView.frame.height / 2) - searchController.searchBar.frame.height + ) + } +} + +extension CredentialProviderListViewController: CredentialProviderListDetailsViewControllerDelegate { + + func credentialProviderListDetailsViewControllerDidProvideText(_ controller: CredentialProviderListDetailsViewController, text: String) { + onTextProvided(text) + } + +} diff --git a/AutofillCredentialProvider/CredentialProvider/CredentialProviderList/CredentialProviderListViewModel.swift b/AutofillCredentialProvider/CredentialProvider/CredentialProviderList/CredentialProviderListViewModel.swift new file mode 100644 index 0000000000..1b0ab0c601 --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/CredentialProviderList/CredentialProviderListViewModel.swift @@ -0,0 +1,253 @@ +// +// CredentialProviderListViewModel.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import AuthenticationServices +import BrowserServicesKit +import Combine +import Common +import Core +import os.log + +final class CredentialProviderListViewModel: ObservableObject { + + enum ViewState { + case authLocked + case noAuthAvailable + case empty + case showItems + case searching + case searchingNoResults + } + + var isSearching: Bool = false { + didSet { + if oldValue != isSearching, isSearching { + Pixel.fire(pixel: .autofillExtensionPasswordsSearch) + } + } + } + var authenticationNotRequired = false + + private let serviceIdentifiers: [ASCredentialServiceIdentifier] + private let secureVault: (any AutofillSecureVault)? + private let credentialIdentityStoreManager: AutofillCredentialIdentityStoreManaging + private var accounts = [SecureVaultModels.WebsiteAccount]() + private var accountsToSuggest = [SecureVaultModels.WebsiteAccount]() + private var cancellables: Set = [] + private let tld: TLD + private let autofillDomainNameUrlMatcher = AutofillDomainNameUrlMatcher() + private let autofillDomainNameUrlSort = AutofillDomainNameUrlSort() + + let authenticator = UserAuthenticator(reason: UserText.credentialProviderListAuthenticationReason, + cancelTitle: UserText.credentialProviderListAuthenticationCancelButton) + var hasAccountsSaved: Bool { + return !accounts.isEmpty + } + + var serviceIdentifierPromptLabel: String? { + guard let identifier = serviceIdentifiers.first?.identifier else { + return nil + } + return String(format: UserText.credentialProviderListPrompt, autofillDomainNameUrlMatcher.normalizeUrlForWeb(identifier)) + } + + @Published private(set) var viewState: CredentialProviderListViewModel.ViewState = .authLocked + @Published private(set) var sections = [AutofillLoginListSectionType]() { + didSet { + updateViewState() + } + } + + init(serviceIdentifiers: [ASCredentialServiceIdentifier], + secureVault: (any AutofillSecureVault)?, + credentialIdentityStoreManager: AutofillCredentialIdentityStoreManaging, + tld: TLD) { + self.serviceIdentifiers = serviceIdentifiers + self.secureVault = secureVault + self.credentialIdentityStoreManager = credentialIdentityStoreManager + self.tld = tld + + if let count = getAccountsCount() { + authenticationNotRequired = count == 0 + } + updateData() + setupCancellables() + } + + private func getAccountsCount() -> Int? { + guard let secureVault = secureVault else { + return nil + } + + do { + return try secureVault.accountsCount() + } catch { + return nil + } + } + + private func fetchAccounts() -> [SecureVaultModels.WebsiteAccount] { + guard let secureVault = secureVault else { + return [] + } + + do { + let allAccounts = try secureVault.accounts() + return allAccounts + } catch { + Logger.autofill.error("Failed to fetch accounts \(error.localizedDescription, privacy: .public)") + return [] + } + } + + func updateData() { + self.accounts = fetchAccounts() + self.accountsToSuggest = fetchSuggestedAccounts() + self.sections = makeSections(with: accounts) + + Task { + await credentialIdentityStoreManager.replaceCredentialStore(with: accounts) + } + } + + private func setupCancellables() { + authenticator.$state + .receive(on: DispatchQueue.main) + .sink { [weak self] _ in + self?.updateViewState() + } + .store(in: &cancellables) + } + + func authenticate(completion: @escaping (UserAuthenticator.AuthError?) -> Void) { + if !authenticator.canAuthenticate() { + viewState = .noAuthAvailable + completion(.noAuthAvailable) + return + } + + if viewState != .authLocked { + completion(nil) + return + } + + authenticator.authenticate(completion: completion) + } + + func authenticateInvalidateContext() { + authenticator.invalidateContext() + } + + private func fetchSuggestedAccounts() -> [SecureVaultModels.WebsiteAccount] { + + var suggestedAccounts = [SecureVaultModels.WebsiteAccount]() + + serviceIdentifiers.compactMap { URL(string: $0.identifier) }.forEach { url in + suggestedAccounts += accounts.filter { account in + return autofillDomainNameUrlMatcher.isMatchingForAutofill( + currentSite: url.absoluteString, + savedSite: account.domain ?? "", + tld: tld + ) + } + } + + let sortedSuggestions = suggestedAccounts.sorted(by: { + autofillDomainNameUrlSort.compareAccountsForSortingAutofill(lhs: $0, rhs: $1, tld: tld) == .orderedAscending + }) + + return sortedSuggestions + } + + func filterData(with query: String? = nil) { + var filteredAccounts = self.accounts + + if let query = query, query.count > 0 { + filteredAccounts = filteredAccounts.filter { account in + if !account.name(tld: tld, autofillDomainNameUrlMatcher: autofillDomainNameUrlMatcher).lowercased().contains(query.lowercased()) && + !(account.domain ?? "").lowercased().contains(query.lowercased()) && + !(account.username ?? "").lowercased().contains(query.lowercased()) { + return false + } + return true + } + } + self.sections = makeSections(with: filteredAccounts) + } + + func rowsInSection(_ section: Int) -> Int { + switch self.sections[section] { + case .suggestions(_, let items): + return items.count + case .credentials(_, let items): + return items.count + default: + return 0 + } + } + + private func makeSections(with accounts: [SecureVaultModels.WebsiteAccount]) -> [AutofillLoginListSectionType] { + var newSections = [AutofillLoginListSectionType]() + + if !isSearching && !accountsToSuggest.isEmpty { + let accountItems = accountsToSuggest.map { AutofillLoginItem(account: $0, + tld: tld, + autofillDomainNameUrlMatcher: autofillDomainNameUrlMatcher, + autofillDomainNameUrlSort: autofillDomainNameUrlSort) + } + newSections.append(.suggestions(title: UserText.credentialProviderListSuggested, items: accountItems)) + } + + let viewModelsGroupedByFirstLetter = accounts.groupedByFirstLetter( + tld: tld, + autofillDomainNameUrlMatcher: autofillDomainNameUrlMatcher, + autofillDomainNameUrlSort: autofillDomainNameUrlSort) + let accountSections = viewModelsGroupedByFirstLetter.sortedIntoSections(autofillDomainNameUrlSort, + tld: tld) + + newSections.append(contentsOf: accountSections) + return newSections + } + + private func updateViewState() { + var newViewState: CredentialProviderListViewModel.ViewState + + if authenticator.state == .loggedOut && !authenticationNotRequired { + newViewState = .authLocked + } else if authenticator.state == .notAvailable { + newViewState = .noAuthAvailable + } else if isSearching { + if sections.count == 0 { + newViewState = .searchingNoResults + } else { + newViewState = .searching + } + } else { + newViewState = sections.count > 0 ? .showItems : .empty + } + + + // Avoid unnecessary updates + if newViewState != viewState { + viewState = newViewState + } + } + +} diff --git a/AutofillCredentialProvider/CredentialProvider/CredentialProviderList/EmptySearchView.swift b/AutofillCredentialProvider/CredentialProvider/CredentialProviderList/EmptySearchView.swift new file mode 100644 index 0000000000..b61c976612 --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/CredentialProviderList/EmptySearchView.swift @@ -0,0 +1,92 @@ +// +// EmptySearchView.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import DesignResourcesKit + +final class EmptySearchView: UIView { + + private lazy var title: UILabel = { + let label = UILabel(frame: CGRect.zero) + + label.font = .systemFont(ofSize: UIFont.preferredFont(forTextStyle: .title2).pointSize * 1.091, weight: .regular) + label.text = UserText.credentialProviderListSearchNoResultTitle + label.numberOfLines = 0 + label.textAlignment = .center + label.lineBreakMode = .byWordWrapping + label.textColor = UIColor(designSystemColor: .textPrimary) + return label + }() + + private lazy var subtitle: UILabel = { + let label = UILabel(frame: CGRect.zero) + + label.font = .preferredFont(forTextStyle: .callout) + label.text = "" + label.numberOfLines = 0 + label.textAlignment = .center + label.lineBreakMode = .byWordWrapping + label.textColor = UIColor(designSystemColor: .textPrimary) + + return label + }() + + private lazy var stackContentView: UIStackView = { + let stackView = UIStackView(arrangedSubviews: [title, subtitle]) + stackView.axis = .vertical + stackView.spacing = 3 + return stackView + }() + + var query: String = "" { + didSet { + if query.count > 0 { + subtitle.text = UserText.credentialProviderListSearchNoResultSubtitle(for: query) + } else { + subtitle.text = "" + } + } + } + + override init(frame: CGRect) { + super.init(frame: frame) + installSubviews() + installConstraints() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func installSubviews() { + addSubview(stackContentView) + } + + private func installConstraints() { + stackContentView.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + stackContentView.centerXAnchor.constraint(equalTo: centerXAnchor), + stackContentView.centerYAnchor.constraint(equalTo: centerYAnchor), + heightAnchor.constraint(equalTo: stackContentView.heightAnchor), + widthAnchor.constraint(equalTo: stackContentView.widthAnchor) + ]) + } + +} diff --git a/AutofillCredentialProvider/CredentialProvider/CredentialProviderList/EmptyView.swift b/AutofillCredentialProvider/CredentialProvider/CredentialProviderList/EmptyView.swift new file mode 100644 index 0000000000..7cce2d7329 --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/CredentialProviderList/EmptyView.swift @@ -0,0 +1,55 @@ +// +// EmptyView.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import SwiftUI +import DesignResourcesKit + +struct EmptyView: View { + + var body: some View { + + VStack(spacing: 0) { + + Image(.passwordsAdd96X96) + .resizable() + .aspectRatio(contentMode: .fit) + .frame(width: 96, height: 96) + + Text(UserText.credentialProviderListEmptyViewTitle) + .daxTitle3() + .foregroundColor(Color(designSystemColor: .textPrimary)) + .padding(.top, 16) + .multilineTextAlignment(.center) + .lineLimit(nil) + + Text(UserText.credentialProviderListEmptyViewSubtitle) + .daxBodyRegular() + .foregroundColor(Color.init(designSystemColor: .textSecondary)) + .multilineTextAlignment(.center) + .padding(.top, 8) + .lineLimit(nil) + + } + .frame(maxWidth: 300.0) + .padding(.bottom, 60) + }} + +#Preview { + EmptyView() +} diff --git a/AutofillCredentialProvider/CredentialProvider/CredentialProviderListDetails/CredentialProviderListDetailsView.swift b/AutofillCredentialProvider/CredentialProvider/CredentialProviderListDetails/CredentialProviderListDetailsView.swift new file mode 100644 index 0000000000..ae2a9113ce --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/CredentialProviderListDetails/CredentialProviderListDetailsView.swift @@ -0,0 +1,359 @@ +// +// CredentialProviderListDetailsView.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import SwiftUI +import DuckUI +import DesignResourcesKit + +struct CredentialProviderListDetailsView: View { + + @ObservedObject var viewModel: CredentialProviderListDetailsViewModel + + var body: some View { + listWithBackground + } + + @ViewBuilder + private var listWithBackground: some View { + if #available(iOS 16.0, *) { + list + .scrollContentBackground(.hidden) + .background(Color(designSystemColor: .background)) + } else { + list + .background(Color(designSystemColor: .background)) + } + } + + private var list: some View { + List { + viewingContentView + } + .simultaneousGesture( + DragGesture().onChanged({_ in + viewModel.selectedCell = nil + })) + .listStyle(.insetGrouped) + } + + private var viewingContentView: some View { + Group { + Section { + Header(viewModel: viewModel.headerViewModel) + } + + Section { + usernameCell() + .onTapGesture { + if viewModel.shouldProvideTextToInsert { + viewModel.textToReturn(.username) + } + } + + passwordCell() + .onTapGesture { + if viewModel.shouldProvideTextToInsert { + viewModel.textToReturn(.password) + } + } + } + + Section { + addressCell() + } + + Section { + notesCell() + } + } + } + + private func usernameCell() -> some View { + CopyableCell(title: UserText.credentialProviderDetailsUsername, + subtitle: viewModel.usernameDisplayString, + selectedCell: $viewModel.selectedCell, + buttonImageName: "Copy-24", + buttonAccessibilityLabel: UserText.credentialProviderDetailsCopyPrompt(for: UserText.credentialProviderDetailsUsername), + buttonAction: { viewModel.copyToPasteboard(.username) }) + } + + private func passwordCell() -> some View { + CopyableCell(title: UserText.credentialProviderDetailsPassword, + subtitle: viewModel.userVisiblePassword, + selectedCell: $viewModel.selectedCell, + isMonospaced: true, + buttonImageName: viewModel.isPasswordHidden ? "Eye-24" : "Eye-Closed-24", + buttonAccessibilityLabel: viewModel.isPasswordHidden ? UserText.credentialProviderDetailsShowPassword : UserText.credentialProviderDetailsHidePassword, + buttonAction: { viewModel.isPasswordHidden.toggle() }, + secondaryButtonImageName: "Copy-24", + secondaryButtonAccessibilityLabel: UserText.credentialProviderDetailsCopyPrompt(for: UserText.credentialProviderDetailsPassword), + secondaryButtonAction: { viewModel.copyToPasteboard(.password) }) + } + + private func addressCell() -> some View { + CopyableCell(title: UserText.credentialProviderDetailsAddress, + subtitle: viewModel.address, + selectedCell: $viewModel.selectedCell, + truncationMode: .middle, + buttonImageName: "Copy-24", + buttonAccessibilityLabel: UserText.credentialProviderDetailsCopyPrompt(for: UserText.credentialProviderDetailsAddress), + buttonAction: { viewModel.copyToPasteboard(.address) }) + } + + private func notesCell() -> some View { + CopyableCell(title: UserText.credentialProviderDetailsNotes, + subtitle: viewModel.notes, + selectedCell: $viewModel.selectedCell, + truncationMode: .middle, + multiLine: true) + } + +} + +private struct Header: View { + + @Environment(\.colorScheme) private var colorScheme + + private struct Constants { + static let imageSize: CGFloat = 32 + static let horizontalStackSpacing: CGFloat = 12 + static let verticalStackSpacing: CGFloat = 1 + static let viewHeight: CGFloat = 60 + static let insets = EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16) + } + + var viewModel: CredentialProviderListDetailsHeaderViewModel + + var body: some View { + HStack(spacing: Constants.horizontalStackSpacing) { + Image(uiImage: viewModel.favicon) + .resizable() + .cornerRadius(4) + .scaledToFit() + .frame(width: Constants.imageSize, height: Constants.imageSize) + .accessibilityHidden(true) + + VStack(alignment: .leading, spacing: Constants.verticalStackSpacing) { + Text(viewModel.title) + .font(.callout) + .foregroundColor(colorScheme == .light ? .gray90 : .white) + .truncationMode(.middle) + .lineLimit(1) + + Text(viewModel.subtitle) + .font(.footnote) + .foregroundColor(colorScheme == .light ? .gray50 : .gray20) + } + + Spacer() + } + .frame(minHeight: Constants.viewHeight) + .frame(maxWidth: .infinity) + .contentShape(Rectangle()) + .listRowBackground(Color(designSystemColor: .surface)) + .listRowInsets(Constants.insets) + } +} + +private struct CopyableCell: View { + @State private var id = UUID() + let title: String + let subtitle: String + @Binding var selectedCell: UUID? + var truncationMode: Text.TruncationMode = .tail + var multiLine: Bool = false + var isMonospaced: Bool = false + + var buttonImageName: String? + var buttonAccessibilityLabel: String? + var buttonAction: (() -> Void)? + + var secondaryButtonImageName: String? + var secondaryButtonAccessibilityLabel: String? + var secondaryButtonAction: (() -> Void)? + + var shouldProvideTextToInsertAction: (() -> Void)? + + var body: some View { + ZStack { + HStack { + VStack(alignment: .leading, spacing: Constants.verticalPadding) { + Text(title) + .label4Style() + HStack { + if multiLine { + Text(subtitle) + .label4Style(design: isMonospaced ? .monospaced : .default, + foregroundColorLight: ForegroundColor(isSelected: selectedCell == id).color, + foregroundColorDark: .gray30) + .truncationMode(truncationMode) + .frame(maxHeight: .greatestFiniteMagnitude) + .textSelection(.enabled) + } else { + Text(subtitle) + .label4Style(design: isMonospaced ? .monospaced : .default, + foregroundColorLight: ForegroundColor(isSelected: selectedCell == id).color, + foregroundColorDark: .gray30) + .truncationMode(truncationMode) + } + } + } + .padding(EdgeInsets(top: 8, leading: 0, bottom: 8, trailing: 8)) + + if secondaryButtonImageName != nil { + Spacer(minLength: Constants.textFieldImageSize * 2 + 8) + } else { + Spacer(minLength: buttonImageName != nil ? Constants.textFieldImageSize : 8) + } + } + + if let buttonImageName = buttonImageName, let buttonAccessibilityLabel = buttonAccessibilityLabel { + let differenceBetweenImageSizeAndTapAreaPerEdge = (Constants.textFieldTapSize - Constants.textFieldImageSize) / 2.0 + HStack(alignment: .center, spacing: 0) { + Spacer() + + Button { + buttonAction?() + self.selectedCell = nil + } label: { + VStack(alignment: .trailing) { + Spacer() + HStack { + Spacer() + Image(buttonImageName) + .resizable() + .frame(width: Constants.textFieldImageSize, height: Constants.textFieldImageSize) + .foregroundColor(Color(UIColor.label).opacity(Constants.textFieldImageOpacity)) + .opacity(subtitle.isEmpty ? 0 : 1) + Spacer() + } + Spacer() + } + } + .buttonStyle(.plain) // Prevent taps from being forwarded to the container view + // can't use .clear here or else both button padded area and container both respond to tap events + .background(BackgroundColor(isSelected: selectedCell == id).color.opacity(0)) + .accessibilityLabel(buttonAccessibilityLabel) + .contentShape(Rectangle()) + .frame(width: Constants.textFieldTapSize, height: Constants.textFieldTapSize) + + if let secondaryButtonImageName = secondaryButtonImageName, + let secondaryButtonAccessibilityLabel = secondaryButtonAccessibilityLabel { + Button { + secondaryButtonAction?() + self.selectedCell = nil + } label: { + VStack(alignment: .trailing) { + Spacer() + HStack { + Spacer() + Image(secondaryButtonImageName) + .resizable() + .frame(width: Constants.textFieldImageSize, height: Constants.textFieldImageSize) + .foregroundColor(Color(UIColor.label).opacity(Constants.textFieldImageOpacity)) + .opacity(subtitle.isEmpty ? 0 : 1) + Spacer() + } + Spacer() + } + } + .buttonStyle(.plain) // Prevent taps from being forwarded to the container view + .background(BackgroundColor(isSelected: selectedCell == id).color.opacity(0)) + .accessibilityLabel(secondaryButtonAccessibilityLabel) + .contentShape(Rectangle()) + .frame(width: Constants.textFieldTapSize, height: Constants.textFieldTapSize) + } + + } + .padding(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: -differenceBetweenImageSizeAndTapAreaPerEdge)) + } + } + .selectableBackground(isSelected: selectedCell == id) + } +} + +private extension View { + func copyable(isSelected: Bool) -> some View { + modifier(Copyable(isSelected: isSelected)) + } + + func selectableBackground(isSelected: Bool) -> some View { + modifier(SelectableBackground(isSelected: isSelected)) + } +} + +private struct Copyable: ViewModifier { + var isSelected: Bool + + public func body(content: Content) -> some View { + ZStack { + Rectangle() + .foregroundColor(.clear) + + content + .allowsHitTesting(false) + .contentShape(Rectangle()) + .frame(maxWidth: .infinity) + .frame(minHeight: Constants.minRowHeight) + } + } +} + +private struct SelectableBackground: ViewModifier { + var isSelected: Bool + + public func body(content: Content) -> some View { + content + .listRowBackground(BackgroundColor(isSelected: isSelected).color) + .listRowInsets(.init(top: 0, leading: 16, bottom: 0, trailing: 16)) + } +} + +private struct ForegroundColor { + let isSelected: Bool + + var color: Color { + if isSelected { + return .gray90 + } else { + return .gray50 + } + } +} + +private struct BackgroundColor { + let isSelected: Bool + + var color: Color { + if isSelected { + return Color("AutofillCellSelectedBackground") + } else { + return Color(designSystemColor: .surface) + } + } +} + +private struct Constants { + static let verticalPadding: CGFloat = 4 + static let minRowHeight: CGFloat = 60 + static let textFieldImageOpacity: CGFloat = 0.84 + static let textFieldImageSize: CGFloat = 24 + static let textFieldTapSize: CGFloat = 36 + static let insets = EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16) +} diff --git a/AutofillCredentialProvider/CredentialProvider/CredentialProviderListDetails/CredentialProviderListDetailsViewController.swift b/AutofillCredentialProvider/CredentialProvider/CredentialProviderListDetails/CredentialProviderListDetailsViewController.swift new file mode 100644 index 0000000000..cb4a37c95c --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/CredentialProviderListDetails/CredentialProviderListDetailsViewController.swift @@ -0,0 +1,125 @@ +// +// CredentialProviderListDetailsViewController.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import SwiftUI +import BrowserServicesKit +import Common +import Combine +import Core + +protocol CredentialProviderListDetailsViewControllerDelegate: AnyObject { + func credentialProviderListDetailsViewControllerDidProvideText(_ controller: CredentialProviderListDetailsViewController, text: String) +} + +class CredentialProviderListDetailsViewController: UIViewController { + + private enum Constants { + static let padding: CGFloat = 16 + } + + weak var delegate: CredentialProviderListDetailsViewControllerDelegate? + private let viewModel: CredentialProviderListDetailsViewModel + private var cancellables: Set = [] + private var contentView: UIView? + + init(account: SecureVaultModels.WebsiteAccount? = nil, tld: TLD, shouldProvideTextToInsert: Bool = false) { + self.viewModel = CredentialProviderListDetailsViewModel(account: account, + tld: tld, + shouldProvideTextToInsert: shouldProvideTextToInsert) + super.init(nibName: nil, bundle: nil) + self.viewModel.delegate = self + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + var account: SecureVaultModels.WebsiteAccount? { + get { + viewModel.account + } + set { + if let newValue { + viewModel.updateData(with: newValue) + } + } + } + + override func viewDidLoad() { + super.viewDidLoad() + + installSubviews() + setupCancellables() + setupNavigationBar() + } + + private func installSubviews() { + installContentView() + } + + private func setupCancellables() { + Publishers.MergeMany( + viewModel.$title, + viewModel.$username, + viewModel.$password, + viewModel.$address, + viewModel.$notes) + .receive(on: DispatchQueue.main) + .sink { [weak self] _ in + self?.setupNavigationBar() + } + .store(in: &cancellables) + + } + + private func installContentView() { + let contentView = CredentialProviderListDetailsView(viewModel: viewModel) + let hostingController = UIHostingController(rootView: contentView) + addChild(hostingController) + hostingController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight] + hostingController.view.frame = view.bounds + view.addSubview(hostingController.view) + hostingController.didMove(toParent: self) + self.contentView = hostingController.view + } + + private func setupNavigationBar() { + title = viewModel.navigationTitle + } + + func showActionMessage(_ message: String) { + ActionMessageView.present( + message: message, + actionTitle: "", + onAction: {}, + inView: self.view + ) + } +} + +extension CredentialProviderListDetailsViewController: CredentialProviderListDetailsViewModelDelegate { + func credentialProviderListDetailsViewModelDidProvideText(text: String) { + delegate?.credentialProviderListDetailsViewControllerDidProvideText(self, text: text) + } + + func credentialProviderListDetailsViewModelShowActionMessage(message: String) { + showActionMessage(message) + } +} diff --git a/AutofillCredentialProvider/CredentialProvider/CredentialProviderListDetails/CredentialProviderListDetailsViewModel.swift b/AutofillCredentialProvider/CredentialProvider/CredentialProviderListDetails/CredentialProviderListDetailsViewModel.swift new file mode 100644 index 0000000000..33fac86061 --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/CredentialProviderListDetails/CredentialProviderListDetailsViewModel.swift @@ -0,0 +1,192 @@ +// +// CredentialProviderListDetailsViewModel.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import SwiftUI +import BrowserServicesKit +import Common +import Combine +import Core + +protocol CredentialProviderListDetailsViewModelDelegate: AnyObject { + func credentialProviderListDetailsViewModelShowActionMessage(message: String) + func credentialProviderListDetailsViewModelDidProvideText(text: String) +} + +final class CredentialProviderListDetailsViewModel: ObservableObject { + enum ViewMode { + case view + } + + enum PasteboardCopyAction { + case username + case password + case address + case notes + } + + weak var delegate: CredentialProviderListDetailsViewModelDelegate? + var account: SecureVaultModels.WebsiteAccount? + + private let tld: TLD + private let autofillDomainNameUrlMatcher = AutofillDomainNameUrlMatcher() + private let autofillDomainNameUrlSort = AutofillDomainNameUrlSort() + + @ObservedObject var headerViewModel: CredentialProviderListDetailsHeaderViewModel + @Published var isPasswordHidden = true + @Published var username = "" + @Published var password = "" + @Published var address = "" + @Published var notes = "" + @Published var title = "" + @Published var selectedCell: UUID? + + private var passwordData: Data { + password.data(using: .utf8)! + } + + var navigationTitle: String { + return title.isEmpty ? address : title + } + + var websiteIsValidUrl: Bool { + account?.domain?.toTrimmedURL != nil + } + + var userVisiblePassword: String { + let passwordHider = PasswordHider(password: password) + return isPasswordHidden ? passwordHider.hiddenPassword : passwordHider.password + } + + var usernameDisplayString: String { + AutofillInterfaceEmailTruncator.truncateEmail(username, maxLength: 36) + } + + let shouldProvideTextToInsert: Bool + + internal init(account: SecureVaultModels.WebsiteAccount? = nil, + tld: TLD, + emailManager: EmailManager = EmailManager(), + shouldProvideTextToInsert: Bool) { + self.account = account + self.tld = tld + self.headerViewModel = CredentialProviderListDetailsHeaderViewModel() + self.shouldProvideTextToInsert = shouldProvideTextToInsert + if let account = account { + self.updateData(with: account) + } + } + + func updateData(with account: SecureVaultModels.WebsiteAccount) { + self.account = account + username = account.username ?? "" + address = account.domain ?? "" + title = account.title ?? "" + notes = account.notes ?? "" + headerViewModel.updateData(with: account, + tld: tld, + autofillDomainNameUrlMatcher: autofillDomainNameUrlMatcher, + autofillDomainNameUrlSort: autofillDomainNameUrlSort) + setupPassword(with: account) + } + + func copyToPasteboard(_ action: PasteboardCopyAction) { + var message = "" + switch action { + case .username: + message = UserText.credentialProviderDetailsCopyToastUsernameCopied + UIPasteboard.general.string = username + Pixel.fire(pixel: .autofillManagementCopyUsername) + case .password: + message = UserText.credentialProviderDetailsCopyToastPasswordCopied + UIPasteboard.general.string = password + Pixel.fire(pixel: .autofillManagementCopyPassword) + case .address: + message = UserText.credentialProviderDetailsCopyToastAddressCopied + UIPasteboard.general.string = address + case .notes: + message = UserText.credentialProviderDetailsCopyToastNotesCopied + UIPasteboard.general.string = notes + } + + delegate?.credentialProviderListDetailsViewModelShowActionMessage(message: message) + } + + func textToReturn(_ action: PasteboardCopyAction) { + var text = "" + switch action { + case .username: + text = username + case .password: + text = password + default: + return + } + + delegate?.credentialProviderListDetailsViewModelDidProvideText(text: text) + } + + private func setupPassword(with account: SecureVaultModels.WebsiteAccount) { + do { + if let accountID = account.id, let accountIdInt = Int64(accountID) { + let vault = try AutofillSecureVaultFactory.makeVault(reporter: nil) + + if let credential = try + vault.websiteCredentialsFor(accountId: accountIdInt) { + self.password = credential.password.flatMap { String(data: $0, encoding: .utf8) } ?? "" + } + } + } catch { + Pixel.fire(pixel: .secureVaultError, error: error) + } + } + + private func handleSecureVaultError(_ error: Error) { + Pixel.fire(pixel: .secureVaultError, error: error) + } +} + +final class CredentialProviderListDetailsHeaderViewModel: ObservableObject { + private var dateFormatter: DateFormatter = { + let dateFormatter = DateFormatter() + dateFormatter.dateStyle = .medium + dateFormatter.timeStyle = .short + return dateFormatter + }() + + @Published var title: String = "" + @Published var subtitle: String = "" + @Published var domain: String = "" + @Published var favicon: UIImage = UIImage(named: "Logo")! + + func updateData(with account: SecureVaultModels.WebsiteAccount, tld: TLD, autofillDomainNameUrlMatcher: AutofillDomainNameUrlMatcher, autofillDomainNameUrlSort: AutofillDomainNameUrlSort) { + self.title = account.name(tld: tld, autofillDomainNameUrlMatcher: autofillDomainNameUrlMatcher) + self.subtitle = UserText.credentialProviderDetailsLastUpdated(for: (dateFormatter.string(from: account.lastUpdated))) + self.domain = account.domain ?? "" + + // Update favicon + let accountName = account.name(tld: tld, autofillDomainNameUrlMatcher: autofillDomainNameUrlMatcher) + let accountTitle = (account.title?.isEmpty == false) ? account.title! : "#" + let preferredFakeFaviconLetters = tld.eTLDplus1(accountName) ?? accountTitle + if let image = FaviconHelper.loadImageFromCache(forDomain: domain, preferredFakeFaviconLetters: preferredFakeFaviconLetters) { + self.favicon = image + } + } + +} diff --git a/AutofillCredentialProvider/CredentialProvider/CredentialProviderViewController.swift b/AutofillCredentialProvider/CredentialProvider/CredentialProviderViewController.swift new file mode 100644 index 0000000000..557b7292e7 --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/CredentialProviderViewController.swift @@ -0,0 +1,240 @@ +// +// CredentialProviderViewController.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import AuthenticationServices +import SwiftUI +import BrowserServicesKit +import Core +import Common +import os.log + +class CredentialProviderViewController: ASCredentialProviderViewController { + + private struct Constants { + static let openPasswords = AppDeepLinkSchemes.openPasswords.url + } + + private lazy var authenticator = UserAuthenticator(reason: UserText.credentialProviderListAuthenticationReason, + cancelTitle: UserText.credentialProviderListAuthenticationCancelButton) + + private lazy var credentialIdentityStoreManager: AutofillCredentialIdentityStoreManaging = AutofillCredentialIdentityStoreManager(credentialStore: ASCredentialIdentityStore.shared, + vault: secureVault, + tld: tld) + + private lazy var secureVault: (any AutofillSecureVault)? = { + if findKeychainItemsWithV4() { + return try? AutofillSecureVaultFactory.makeVault(reporter: SecureVaultReporter()) + } else { + return nil + } + }() + + private lazy var tld: TLD = TLD() + + private lazy var vaultCredentialManager: VaultCredentialManaging = VaultCredentialManager(secureVault: secureVault, + credentialIdentityStoreManager: credentialIdentityStoreManager) + + // MARK: - ASCredentialProviderViewController Overrides + + override func prepareCredentialList(for serviceIdentifiers: [ASCredentialServiceIdentifier]) { + loadCredentialsList(for: serviceIdentifiers) + } + + override func provideCredentialWithoutUserInteraction(for credentialIdentity: ASPasswordCredentialIdentity) { + // A quirk here is calling .canAuthenticate in this one scenario actually triggers the prompt to authentication + // Calling .authenticate here results in the extension attempting to present a non-existent view controller causing weird UI + if authenticator.canAuthenticateViaBiometrics() { + provideCredential(for: credentialIdentity) + } else { + self.extensionContext.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, + code: ASExtensionError.userInteractionRequired.rawValue)) + } + } + + @available(iOS 17.0, *) + override func provideCredentialWithoutUserInteraction(for credentialRequest: any ASCredentialRequest) { + guard credentialRequest.type == .password else { + self.extensionContext.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, + code: ASExtensionError.credentialIdentityNotFound.rawValue)) + return + } + + if authenticator.canAuthenticateViaBiometrics() { + provideCredential(for: credentialRequest.credentialIdentity) + } else { + self.extensionContext.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, + code: ASExtensionError.userInteractionRequired.rawValue)) + } + } + + override func prepareInterfaceToProvideCredential(for credentialIdentity: ASPasswordCredentialIdentity) { + let hostingController = UIHostingController(rootView: LockScreenView()) + installChildViewController(hostingController) + + authenticateAndHandleCredential { + self.provideCredential(for: credentialIdentity) + } + } + + @available(iOS 17.0, *) + override func prepareInterfaceToProvideCredential(for credentialRequest: any ASCredentialRequest) { + let hostingController = UIHostingController(rootView: LockScreenView()) + installChildViewController(hostingController) + + authenticateAndHandleCredential { + self.provideCredential(for: credentialRequest.credentialIdentity) + } + } + + override func prepareInterfaceForExtensionConfiguration() { + let viewModel = CredentialProviderActivatedViewModel { [weak self] shouldLaunchApp in + if shouldLaunchApp { + self?.openUrl(Constants.openPasswords) + } + self?.extensionContext.completeExtensionConfigurationRequest() + } + + let view = CredentialProviderActivatedView(viewModel: viewModel) + let hostingController = UIHostingController(rootView: view) + installChildViewController(hostingController) + + Task { + if findKeychainItemsWithV4() { + await credentialIdentityStoreManager.populateCredentialStore() + } + } + + Pixel.fire(pixel: .autofillExtensionEnabled) + } + + @available(iOSApplicationExtension 18.0, *) + override func prepareInterfaceForUserChoosingTextToInsert() { + loadCredentialsList(for: [], shouldProvideTextToInsert: true) + } + + // MARK: - Private + + private func loadCredentialsList(for serviceIdentifiers: [ASCredentialServiceIdentifier], shouldProvideTextToInsert: Bool = false) { + let credentialProviderListViewController = CredentialProviderListViewController(serviceIdentifiers: serviceIdentifiers, + secureVault: secureVault, + credentialIdentityStoreManager: credentialIdentityStoreManager, + shouldProvideTextToInsert: shouldProvideTextToInsert, + tld: tld, + onRowSelected: { [weak self] item in + guard let self = self else { + self?.extensionContext.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, + code: ASExtensionError.failed.rawValue)) + return + } + + let credential = self.vaultCredentialManager.fetchCredential(for: item.account) + + self.extensionContext.completeRequest(withSelectedCredential: credential, completionHandler: nil) + + }, onTextProvided: { [weak self] text in + if #available(iOSApplicationExtension 18.0, *) { + self?.extensionContext.completeRequest(withTextToInsert: text) + } + }, onDismiss: { + self.extensionContext.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, + code: ASExtensionError.userCanceled.rawValue)) + }) + + let navigationController = UINavigationController(rootViewController: credentialProviderListViewController) + self.view.subviews.forEach { $0.removeFromSuperview() } + addChild(navigationController) + navigationController.view.frame = self.view.bounds + navigationController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight] + self.view.addSubview(navigationController.view) + navigationController.didMove(toParent: self) + } + + @available(iOS 17.0, *) + private func provideCredential(for credentialIdentity: ASCredentialIdentity) { + guard let passwordCredential = vaultCredentialManager.fetchCredential(for: credentialIdentity) else { + self.extensionContext.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, + code: ASExtensionError.credentialIdentityNotFound.rawValue)) + Pixel.fire(pixel: .autofillExtensionQuickTypeCancelled) + return + } + + self.extensionContext.completeRequest(withSelectedCredential: passwordCredential) + Pixel.fire(pixel: .autofillExtensionQuickTypeConfirmed) + } + + private func provideCredential(for credentialIdentity: ASPasswordCredentialIdentity) { + guard let passwordCredential = vaultCredentialManager.fetchCredential(for: credentialIdentity) else { + self.extensionContext.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, + code: ASExtensionError.credentialIdentityNotFound.rawValue)) + Pixel.fire(pixel: .autofillExtensionQuickTypeCancelled) + return + } + + self.extensionContext.completeRequest(withSelectedCredential: passwordCredential) + Pixel.fire(pixel: .autofillExtensionQuickTypeConfirmed) + } + + private func authenticateAndHandleCredential(provideCredential: @escaping () -> Void) { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in + self?.authenticator.authenticate { error in + if error != nil { + if error != .noAuthAvailable { + self?.extensionContext.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, + code: ASExtensionError.userInteractionRequired.rawValue)) + } else { + let alert = UIAlertController.makeDeviceAuthenticationAlert { [weak self] in + self?.extensionContext.cancelRequest(withError: NSError(domain: ASExtensionErrorDomain, + code: ASExtensionError.userInteractionRequired.rawValue)) + } + self?.present(alert, animated: true) + } + } else { + provideCredential() + } + } + } + } + + private func findKeychainItemsWithV4() -> Bool { + var itemsWithV4: [String] = [] + + let query: [String: Any] = [ + kSecClass as String: kSecClassGenericPassword, + kSecReturnAttributes as String: kCFBooleanTrue!, + kSecMatchLimit as String: kSecMatchLimitAll + ] + + var result: AnyObject? + + let status = SecItemCopyMatching(query as CFDictionary, &result) + + if status == errSecSuccess, let items = result as? [[String: Any]] { + for item in items { + if let service = item[kSecAttrService as String] as? String, + service.contains("v4") { + itemsWithV4.append(service) + } + } + } else { + Logger.autofill.debug("No items found or error: \(status)") + } + + return !itemsWithV4.isEmpty + } +} diff --git a/AutofillCredentialProvider/CredentialProvider/Extensions/UIAlertControllerExtension.swift b/AutofillCredentialProvider/CredentialProvider/Extensions/UIAlertControllerExtension.swift new file mode 100644 index 0000000000..2908e6e5d9 --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/Extensions/UIAlertControllerExtension.swift @@ -0,0 +1,51 @@ +// +// UIAlertControllerExtension.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +extension UIAlertController { + + static func makeDeviceAuthenticationAlert(completion: @escaping () -> Void) -> UIAlertController { + + let deviceType: String + + switch UIDevice.current.userInterfaceIdiom { + case .pad: + deviceType = UserText.deviceTypeiPad + case .phone: + deviceType = UserText.deviceTypeiPhone + default: + deviceType = UserText.deviceTypeDefault + } + + let alertController = UIAlertController( + title: UserText.credentialProviderNoDeviceAuthSetTitle, + message: String(format: UserText.credentialProviderNoDeviceAuthSetMessage, deviceType), + preferredStyle: .alert + ) + + let closeButton = UIAlertAction(title: UserText.actionClose, style: .default) { _ in + completion() + } + + alertController.addAction(closeButton) + return alertController + } + +} diff --git a/AutofillCredentialProvider/CredentialProvider/Extensions/UIColorExtension.swift b/AutofillCredentialProvider/CredentialProvider/Extensions/UIColorExtension.swift new file mode 100644 index 0000000000..e1fe4656f2 --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/Extensions/UIColorExtension.swift @@ -0,0 +1,67 @@ +// +// UIColorExtension.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +extension UIColor { + + convenience init(hex: String) { + var rgbValue: UInt64 = 0 + Scanner(string: hex).scanHexInt64(&rgbValue) + + self.init( + red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0, + green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0, + blue: CGFloat(rgbValue & 0x0000FF) / 255.0, + alpha: CGFloat(1.0) + ) + } + + static func forDomain(_ domain: String) -> UIColor { + var consistentHash: Int { + return domain.utf8 + .map { return $0 } + .reduce(5381) { ($0 << 5) &+ $0 &+ Int($1) } + } + + let palette = [ + UIColor(hex: "94B3AF"), + UIColor(hex: "727998"), + UIColor(hex: "645468"), + UIColor(hex: "4D5F7F"), + UIColor(hex: "855DB6"), + UIColor(hex: "5E5ADB"), + UIColor(hex: "678FFF"), + UIColor(hex: "6BB4EF"), + UIColor(hex: "4A9BAE"), + UIColor(hex: "66C4C6"), + UIColor(hex: "55D388"), + UIColor(hex: "99DB7A"), + UIColor(hex: "ECCC7B"), + UIColor(hex: "E7A538"), + UIColor(hex: "DD6B4C"), + UIColor(hex: "D65D62") + ] + + let hash = consistentHash + let index = hash % palette.count + return palette[abs(index)] + } + +} diff --git a/AutofillCredentialProvider/CredentialProvider/Extensions/UIResponderExtension.swift b/AutofillCredentialProvider/CredentialProvider/Extensions/UIResponderExtension.swift new file mode 100644 index 0000000000..65aa041a9c --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/Extensions/UIResponderExtension.swift @@ -0,0 +1,42 @@ +// +// UIResponderExtension.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +extension UIResponder { + + /// Attempts to open a URL using the UIApplication instance in the responder chain. + /// This is required as the CredentialProvider extension context cannot directly launch the host app. + /// - Returns: True if the URL was opened successfully, false otherwise. + @discardableResult + func openUrl(_ url: URL?) -> Bool { + guard let url = url else { return false } + + var responder: UIResponder? = self + while let r = responder { + if let application = r as? UIApplication { + application.open(url, options: [:], completionHandler: nil) + return true + } + responder = r.next + } + + return false + } +} diff --git a/AutofillCredentialProvider/CredentialProvider/Extensions/UIViewControllerExtension.swift b/AutofillCredentialProvider/CredentialProvider/Extensions/UIViewControllerExtension.swift new file mode 100644 index 0000000000..0bba578770 --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/Extensions/UIViewControllerExtension.swift @@ -0,0 +1,39 @@ +// +// UIViewControllerExtension.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +extension UIViewController { + + public func installChildViewController(_ childController: UIViewController) { + addChild(childController) + view.addSubview(childController.view) + + childController.view.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + childController.view.topAnchor.constraint(equalTo: view.topAnchor), + childController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor), + childController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), + childController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor) + ]) + + childController.didMove(toParent: self) + } + +} diff --git a/AutofillCredentialProvider/CredentialProvider/Extensions/UImageExtension.swift b/AutofillCredentialProvider/CredentialProvider/Extensions/UImageExtension.swift new file mode 100644 index 0000000000..56cda53ea2 --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/Extensions/UImageExtension.swift @@ -0,0 +1,30 @@ +// +// UImageExtension.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +extension UIImage { + + func toSRGB() -> UIImage { + UIGraphicsImageRenderer(size: size).image { _ in + draw(in: CGRect(origin: .zero, size: size)) + } + } + +} diff --git a/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/AutofillCellSelectedBackground.colorset/Contents.json b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/AutofillCellSelectedBackground.colorset/Contents.json new file mode 100644 index 0000000000..cc91bfda6f --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/AutofillCellSelectedBackground.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "0.800", + "green" : "0.800", + "red" : "0.800" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "display-p3", + "components" : { + "alpha" : "1.000", + "blue" : "0.267", + "green" : "0.267", + "red" : "0.267" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/AutofillLock.imageset/AutofillLock.pdf b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/AutofillLock.imageset/AutofillLock.pdf new file mode 100644 index 0000000000..606eb7cbd1 Binary files /dev/null and b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/AutofillLock.imageset/AutofillLock.pdf differ diff --git a/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/AutofillLock.imageset/Contents.json b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/AutofillLock.imageset/Contents.json new file mode 100644 index 0000000000..3ed495a79d --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/AutofillLock.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "AutofillLock.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Contents.json b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Contents.json new file mode 100644 index 0000000000..73c00596a7 --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Copy-24.imageset/Contents.json b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Copy-24.imageset/Contents.json new file mode 100644 index 0000000000..1a533a908e --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Copy-24.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "Copy-24.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Copy-24.imageset/Copy-24.pdf b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Copy-24.imageset/Copy-24.pdf new file mode 100644 index 0000000000..56407ef84b Binary files /dev/null and b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Copy-24.imageset/Copy-24.pdf differ diff --git a/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Eye-24.imageset/Contents.json b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Eye-24.imageset/Contents.json new file mode 100644 index 0000000000..d46a04ef5b --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Eye-24.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "Eye-24.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Eye-24.imageset/Eye-24.pdf b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Eye-24.imageset/Eye-24.pdf new file mode 100644 index 0000000000..ac69e29aa6 Binary files /dev/null and b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Eye-24.imageset/Eye-24.pdf differ diff --git a/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Eye-Closed-24.imageset/Contents.json b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Eye-Closed-24.imageset/Contents.json new file mode 100644 index 0000000000..ac2612ee62 --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Eye-Closed-24.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "Eye-Closed-24.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Eye-Closed-24.imageset/Eye-Closed-24.pdf b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Eye-Closed-24.imageset/Eye-Closed-24.pdf new file mode 100644 index 0000000000..78b200d65b Binary files /dev/null and b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Eye-Closed-24.imageset/Eye-Closed-24.pdf differ diff --git a/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Logo.imageset/Contents.json b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Logo.imageset/Contents.json new file mode 100644 index 0000000000..5cf237b7f3 --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Logo.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Dax_default.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Logo.imageset/Dax_default.pdf b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Logo.imageset/Dax_default.pdf new file mode 100644 index 0000000000..46ebb1057c Binary files /dev/null and b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Logo.imageset/Dax_default.pdf differ diff --git a/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Passwords-Add-96x96.imageset/Contents.json b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Passwords-Add-96x96.imageset/Contents.json new file mode 100644 index 0000000000..9c63920d3e --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Passwords-Add-96x96.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Passwords-Add-96x96.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Passwords-Add-96x96.imageset/Passwords-Add-96x96.svg b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Passwords-Add-96x96.imageset/Passwords-Add-96x96.svg new file mode 100644 index 0000000000..657f2681fc --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Passwords-Add-96x96.imageset/Passwords-Add-96x96.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Passwords-DDG-96x96.imageset/Contents.json b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Passwords-DDG-96x96.imageset/Contents.json new file mode 100644 index 0000000000..608aeb0457 --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Passwords-DDG-96x96.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Passwords-DDG-96x96.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Passwords-DDG-96x96.imageset/Passwords-DDG-96x96.svg b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Passwords-DDG-96x96.imageset/Passwords-DDG-96x96.svg new file mode 100644 index 0000000000..18521b98ac --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/Resources/Assets.xcassets/Passwords-DDG-96x96.imageset/Passwords-DDG-96x96.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AutofillCredentialProvider/CredentialProvider/Resources/UserText.swift b/AutofillCredentialProvider/CredentialProvider/Resources/UserText.swift new file mode 100644 index 0000000000..9068764c55 --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/Resources/UserText.swift @@ -0,0 +1,86 @@ +// +// UserText.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +final class UserText { + + static let credentialProviderActivatedTitle = NSLocalizedString("credential.provider.activated.title", value: "Autofill Passwords activated!", comment: "The title of the screen confirming DuckDuckGo can now be used for autofilling passwords") + + static let credentialProviderActivatedButton = NSLocalizedString("credential.provider.activated.button", value: "Open DuckDuckGo", comment: "Title of button to launch the DuckDuckGo app") + + static let actionClose = NSLocalizedString("action.button.close", value: "Close", comment: "Close button title") + + static let actionDone = NSLocalizedString("action.button.done", value: "Done", comment: "Done button title") + + static let credentialProviderListTitle = NSLocalizedString("credential.provider.list.title", value: "Passwords", comment: "Title for screen listing autofill logins") + + static let credentialProviderListPrompt = NSLocalizedString("credential.provider.list.prompt", value: "Choose a password to use for \"%@\"", comment: "Prompt above the title for screen listing autofill logins, example: Choose a password to use for \"website.com\"") + + static let credentialProviderListSearchPlaceholder = NSLocalizedString("credential.provider.list.search-placeholder", value: "Search passwords", comment: "Placeholder for search field on autofill login listing") + + static let credentialProviderListEmptyViewTitle = NSLocalizedString("credential.provider.list.empty-view.title", value: "No passwords saved yet", comment: "Title for view displayed when autofill has no items") + + static let credentialProviderListEmptyViewSubtitle = NSLocalizedString("credential.provider.list.empty-view.footer", value: "Passwords are stored securely on your device.", comment: "Footer label displayed below table section with option to enable autofill") + + static let credentialProviderListSuggested = NSLocalizedString("credential.provider.list.suggested", value: "Suggested", comment: "Section title for group of suggested saved logins") + + static let credentialProviderListSearchNoResultTitle = NSLocalizedString("credential.provider.list.search.no-results.title", value: "No Results", comment: "Title displayed when there are no results on Autofill search") + + static func credentialProviderListSearchNoResultSubtitle(for query: String) -> String { + let message = NSLocalizedString("credential.provider.list.search.no-results.subtitle", value: "for '%@'", comment: "Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle)") + return message.format(arguments: query) + } + + static let credentialProviderListAuthenticationReason = NSLocalizedString("credential.provider.list.auth.reason", value: "Unlock device to access passwords", comment: "Reason for auth when opening screen with list of saved passwords") + + static let credentialProviderListAuthenticationCancelButton = NSLocalizedString("credential.provider.list.auth.cancel", value: "Cancel", comment: "Cancel button for auth when opening login list") + + static let credentialProviderNoDeviceAuthSetTitle = NSLocalizedString("credential.provider.no-device-auth-set.title", value: "Device Passcode Required", comment: "Title for alert when device authentication is not set") + + static let credentialProviderNoDeviceAuthSetMessage = NSLocalizedString("credential.provider.no-device-auth-set.message", value: "Set a passcode on %@ to autofill your DuckDuckGo passwords.", comment: "Message for alert when device authentication is not set, where %@ is iPhone|iPad|device") + + static let deviceTypeiPhone = NSLocalizedString("credential.provider.device.type.iphone", value: "iPhone", comment: "Device type is iPhone") + static let deviceTypeiPad = NSLocalizedString("credential.provider.device.type.pad", value: "iPad", comment: "Device type is iPad") + static let deviceTypeDefault = NSLocalizedString("credential.provider.device.type.default", value: "device", comment: "Default string used if users device is not iPhone or iPad") + + public static let credentialProviderDetailsCopyToastUsernameCopied = NSLocalizedString("credential.provider.list.details.copy-toast.username-copied", value: "Username copied", comment: "Title for toast when copying username") + public static let credentialProviderDetailsCopyToastPasswordCopied = NSLocalizedString("credential.provider.list.details.copy-toast.password-copied", value: "Password copied", comment: "Title for toast when copying password") + public static let credentialProviderDetailsCopyToastAddressCopied = NSLocalizedString("credential.provider.list.details.copy-toast.address-copied", value: "Address copied", comment: "Title for toast when copying address") + public static let credentialProviderDetailsCopyToastNotesCopied = NSLocalizedString("credential.provider.list.details.copy-toast.notes-copied", value: "Notes copied", comment: "Title for toast when copying notes") + + public static func credentialProviderDetailsLastUpdated(for date: String) -> String { + let message = NSLocalizedString("credential.provider.list.details.last-updated", value: "Last updated %@", comment: "Message displaying when the login was last updated") + return message.format(arguments: date) + } + + public static let credentialProviderDetailsLoginName = NSLocalizedString("credential.provider.list.details.login-name", value: "Title", comment: "Login name label for login details on autofill") + public static let credentialProviderDetailsUsername = NSLocalizedString("credential.provider.list.details.username", value: "Username", comment: "Username label for login details on autofill") + public static let credentialProviderDetailsPassword = NSLocalizedString("credential.provider.list.details.password", value: "Password", comment: "Password label for login details on autofill") + public static let credentialProviderDetailsAddress = NSLocalizedString("credential.provider.list.details.address", value: "Website URL", comment: "Address label for login details on autofill") + public static let credentialProviderDetailsNotes = NSLocalizedString("credential.provider.list.details.notes", value: "Notes", comment: "Notes label for login details on autofill") + + + public static func credentialProviderDetailsCopyPrompt(for type: String) -> String { + let message = NSLocalizedString("credential.provider.list.details.copy-prompt", value: "Copy %@", comment: "Menu item text for copying autofill login details") + return message.format(arguments: type) + } + public static let credentialProviderDetailsShowPassword = NSLocalizedString("credential.provider.list.details.show-password", value: "Show Password", comment: "Accessibility title for a Show Password button displaying actial password instead of *****") + public static let credentialProviderDetailsHidePassword = NSLocalizedString("credential.provider.list.details.hide-password", value: "Hide Password", comment: "Accessibility title for a Hide Password button replacing displayed password with *****") +} diff --git a/AutofillCredentialProvider/CredentialProvider/Shared/ActionMessageView.swift b/AutofillCredentialProvider/CredentialProvider/Shared/ActionMessageView.swift new file mode 100644 index 0000000000..50a1b38a4e --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/Shared/ActionMessageView.swift @@ -0,0 +1,197 @@ +// +// ActionMessageView.swift +// DuckDuckGo +// +// Copyright © 2021 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +extension ActionMessageView: NibLoading {} + +class ActionMessageView: UIView { + + enum PresentationLocation { + case withBottomBar(andAddressBarBottom: Bool) + case withoutBottomBar + } + + private static var presentedMessages = [ActionMessageView]() + + private enum Constants { + static var maxWidth: CGFloat = 346 + static var minimumHorizontalPadding: CGFloat = 20 + static var cornerRadius: CGFloat = 10 + + static var animationDuration: TimeInterval = 0.2 + static var duration: TimeInterval = 3.0 + + static var windowBottomPaddingWithBottomBar: CGFloat { + if UIDevice.current.userInterfaceIdiom == .phone && !UIDevice.current.orientation.isPortrait { + return 40 + } + + return 70 + } + + static var windowBottomPaddingWithAddressBar: CGFloat { + return windowBottomPaddingWithBottomBar + 52 + } + + static var windowBottomPaddingWithoutBottomBar: CGFloat { + return 0 + } + + } + + private static func bottomPadding(for location: PresentationLocation) -> CGFloat { + switch location { + case .withBottomBar(let isAddressBarBottom): + return isAddressBarBottom ? Constants.windowBottomPaddingWithAddressBar : Constants.windowBottomPaddingWithBottomBar + case .withoutBottomBar: + return Constants.windowBottomPaddingWithoutBottomBar + } + } + + @IBOutlet weak var message: UILabel! + @IBOutlet weak var actionButton: UIButton! + + @IBOutlet var labelToButton: NSLayoutConstraint! + @IBOutlet var labelToTrailing: NSLayoutConstraint! + + private var action: () -> Void = {} + private var onDidDismiss: () -> Void = {} + + private var dismissWorkItem: DispatchWorkItem? + + static func loadFromXib() -> ActionMessageView { + return ActionMessageView.load(nibName: "ActionMessageView") + } + + override func awakeFromNib() { + super.awakeFromNib() + + layer.cornerRadius = Constants.cornerRadius + } + + static func present(message: NSAttributedString, + numberOfLines: Int = 0, + actionTitle: String? = nil, + presentationLocation: PresentationLocation = .withBottomBar(andAddressBarBottom: false), + duration: TimeInterval = Constants.duration, + onAction: @escaping () -> Void = {}, + onDidDismiss: @escaping () -> Void = {}, + inView: UIView) { + let messageView = loadFromXib() + messageView.message.attributedText = message + messageView.message.numberOfLines = numberOfLines + ActionMessageView.present(messageView: messageView, + message: message.string, + actionTitle: actionTitle, + presentationLocation: presentationLocation, + duration: duration, + onAction: onAction, + onDidDismiss: onDidDismiss, + inView: inView) + } + + static func present(message: String, + actionTitle: String? = nil, + presentationLocation: PresentationLocation = .withBottomBar(andAddressBarBottom: false), + duration: TimeInterval = Constants.duration, + onAction: @escaping () -> Void = {}, + onDidDismiss: @escaping () -> Void = {}, + inView: UIView) { + let messageView = loadFromXib() + messageView.message.text = message + ActionMessageView.present(messageView: messageView, + message: message, + actionTitle: actionTitle, + presentationLocation: presentationLocation, + duration: duration, + onAction: onAction, + onDidDismiss: onDidDismiss, + inView: inView) + } + + private static func present(messageView: ActionMessageView, + message: String, + actionTitle: String? = nil, + presentationLocation: PresentationLocation = .withBottomBar(andAddressBarBottom: false), + duration: TimeInterval = Constants.duration, + onAction: @escaping () -> Void = {}, + onDidDismiss: @escaping () -> Void = {}, + inView: UIView) { + dismissAllMessages() + + if let actionTitle = actionTitle, let title = messageView.actionButton.attributedTitle(for: .normal) { + messageView.actionButton.setAttributedTitle(title.withText(actionTitle), for: .normal) + messageView.action = onAction + } else { + messageView.labelToButton.isActive = false + messageView.labelToTrailing.isActive = true + messageView.actionButton.isHidden = true + } + messageView.onDidDismiss = onDidDismiss + + inView.addSubview(messageView) + inView.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: messageView.bottomAnchor, + constant: bottomPadding(for: presentationLocation)).isActive = true + + let messageViewWidth = inView.frame.width <= Constants.maxWidth ? inView.frame.width - Constants.minimumHorizontalPadding : Constants.maxWidth + messageView.widthAnchor.constraint(equalToConstant: messageViewWidth).isActive = true + messageView.centerXAnchor.constraint(equalTo: inView.centerXAnchor).isActive = true + + inView.layoutIfNeeded() + + messageView.alpha = 0 + UIView.animate(withDuration: Constants.animationDuration) { + messageView.alpha = 1 + } completion: { _ in + UIAccessibility.post(notification: .announcement, argument: message) + } + + let workItem = DispatchWorkItem { [weak messageView] in + messageView?.dismissAndFadeOut() + } + messageView.dismissWorkItem = workItem + DispatchQueue.main.asyncAfter(deadline: .now() + duration, execute: workItem) + presentedMessages.append(messageView) + } + + static func dismissAllMessages() { + presentedMessages.forEach { $0.dismissAndFadeOut() } + } + + func dismissAndFadeOut() { + dismissWorkItem?.cancel() + dismissWorkItem = nil + + UIView.animate(withDuration: Constants.animationDuration, animations: { + self.alpha = 0 + }, completion: { _ in + self.removeFromSuperview() + if let position = Self.presentedMessages.firstIndex(of: self) { + Self.presentedMessages.remove(at: position) + } + self.onDidDismiss() + }) + } + + @IBAction func onButtonTap() { + action() + dismissAndFadeOut() + } +} diff --git a/AutofillCredentialProvider/CredentialProvider/Shared/ActionMessageView.xib b/AutofillCredentialProvider/CredentialProvider/Shared/ActionMessageView.xib new file mode 100644 index 0000000000..2c532dc85f --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/Shared/ActionMessageView.xib @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AutofillCredentialProvider/CredentialProvider/Shared/FaviconHelper.swift b/AutofillCredentialProvider/CredentialProvider/Shared/FaviconHelper.swift new file mode 100644 index 0000000000..b1d95ef7a8 --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/Shared/FaviconHelper.swift @@ -0,0 +1,87 @@ +// +// FaviconHelper.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import Common +import Core +import UIKit + +struct FaviconHelper { + + static func loadImageFromCache(forDomain domain: String?, preferredFakeFaviconLetters: String) -> UIImage? { + guard let domain = domain, + let cacheUrl = FaviconsCacheType.fireproof.cacheLocation() else { return nil } + + let key = FaviconHasher.createHash(ofDomain: domain) + + // Slight leap here to avoid loading Kingfisher as a library for the widgets. + // Once dependency management is fixed, link it and use Favicons directly. + let imageUrl = cacheUrl.appendingPathComponent("com.onevcat.Kingfisher.ImageCache.fireproof").appendingPathComponent(key) + + guard let data = (try? Data(contentsOf: imageUrl)) else { + let image = Self.createFakeFavicon(forDomain: domain, size: 32, backgroundColor: UIColor.forDomain(domain), preferredFakeFaviconLetters: preferredFakeFaviconLetters) + return image + } + + return UIImage(data: data)?.toSRGB() + } + + private static func createFakeFavicon(forDomain domain: String, + size: CGFloat = 192, + backgroundColor: UIColor = UIColor.red, + bold: Bool = true, + preferredFakeFaviconLetters: String? = nil, + letterCount: Int = 2) -> UIImage? { + + let cornerRadius = size * 0.125 + let imageRect = CGRect(x: 0, y: 0, width: size, height: size) + let padding = size * 0.16 + let labelFrame = CGRect(x: padding, y: padding, width: imageRect.width - (2 * padding), height: imageRect.height - (2 * padding)) + + let renderer = UIGraphicsImageRenderer(size: imageRect.size) + let icon = renderer.image { imageContext in + let context = imageContext.cgContext + + context.setFillColor(backgroundColor.cgColor) + context.addPath(CGPath(roundedRect: imageRect, cornerWidth: cornerRadius, cornerHeight: cornerRadius, transform: nil)) + context.fillPath() + + let label = UILabel(frame: labelFrame) + label.numberOfLines = 1 + label.adjustsFontSizeToFitWidth = true + label.minimumScaleFactor = 0.1 + label.baselineAdjustment = .alignCenters + label.font = bold ? UIFont.boldSystemFont(ofSize: size) : UIFont.systemFont(ofSize: size) + label.textColor = .white + label.textAlignment = .center + + if let preferedPrefix = preferredFakeFaviconLetters?.droppingWwwPrefix().prefix(letterCount).capitalized { + label.text = preferedPrefix + } else { + label.text = preferredFakeFaviconLetters?.capitalized ?? "#" + } + + context.translateBy(x: padding, y: padding) + + label.layer.draw(in: context) + } + + return icon.withRenderingMode(.alwaysOriginal) + } +} diff --git a/AutofillCredentialProvider/CredentialProvider/Shared/LockScreenView.swift b/AutofillCredentialProvider/CredentialProvider/Shared/LockScreenView.swift new file mode 100644 index 0000000000..05db061e8d --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/Shared/LockScreenView.swift @@ -0,0 +1,43 @@ +// +// LockScreenView.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import SwiftUI +import DesignResourcesKit + +struct LockScreenView: View { + var body: some View { + GeometryReader { geometry in + VStack { + Spacer() + Image(.autofillLock) + .position(x: geometry.size.width / 2, + y: shouldCenterVerticallyInLandscape(on: geometry) ? geometry.size.height / 2 : geometry.size.height * 0.8) + } + } + .background(Color(designSystemColor: .background)) + } + + private func shouldCenterVerticallyInLandscape(on geometry: GeometryProxy) -> Bool { + return UIDevice.current.userInterfaceIdiom == .phone && geometry.size.width > geometry.size.height + } +} + +#Preview { + LockScreenView() +} diff --git a/AutofillCredentialProvider/CredentialProvider/Shared/NibLoading.swift b/AutofillCredentialProvider/CredentialProvider/Shared/NibLoading.swift new file mode 100644 index 0000000000..af24b38f7c --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/Shared/NibLoading.swift @@ -0,0 +1,34 @@ +// +// NibLoading.swift +// DuckDuckGo +// +// Copyright © 2017 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +protocol NibLoading { +} + +extension NibLoading where Self: UIView { + static func load(nibName: String) -> Self { + let nib = UINib(nibName: nibName, bundle: nil) + guard let view = nib.instantiate(withOwner: self, options: nil).first as? Self else { + fatalError("Error instantiating view") + } + view.autoresizingMask = [UIView.AutoresizingMask.flexibleWidth, UIView.AutoresizingMask.flexibleHeight] + return view + } +} diff --git a/AutofillCredentialProvider/CredentialProvider/Shared/SecureVaultReporter.swift b/AutofillCredentialProvider/CredentialProvider/Shared/SecureVaultReporter.swift new file mode 100644 index 0000000000..2a001a714f --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/Shared/SecureVaultReporter.swift @@ -0,0 +1,44 @@ +// +// SecureVaultReporter.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Common +import Core +import Foundation +import SecureStorage + +final class SecureVaultReporter: SecureVaultReporting { + + func secureVaultError(_ error: SecureStorage.SecureStorageError) { +#if DEBUG + guard !ProcessInfo().arguments.contains("testing") else { return } +#endif + let pixelParams = [PixelParameters.isBackgrounded: "false", + PixelParameters.appVersion: AppVersion.shared.versionAndBuildNumber] + + switch error { + case .initFailed(let error): + DailyPixel.fire(pixel: .secureVaultInitFailedError, error: error, withAdditionalParameters: pixelParams) + case .failedToOpenDatabase(let error): + DailyPixel.fire(pixel: .secureVaultFailedToOpenDatabaseError, error: error, withAdditionalParameters: pixelParams) + default: + DailyPixel.fire(pixel: .secureVaultError, error: error) + } + } + +} diff --git a/AutofillCredentialProvider/CredentialProvider/Shared/VaultCredentialManager.swift b/AutofillCredentialProvider/CredentialProvider/Shared/VaultCredentialManager.swift new file mode 100644 index 0000000000..945d58456a --- /dev/null +++ b/AutofillCredentialProvider/CredentialProvider/Shared/VaultCredentialManager.swift @@ -0,0 +1,111 @@ +// +// VaultCredentialManager.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import AuthenticationServices +import BrowserServicesKit +import Core +import SecureStorage + +protocol VaultCredentialManaging: AnyObject { + func fetchCredential(for account: SecureVaultModels.WebsiteAccount) -> ASPasswordCredential + func fetchCredential(for identity: ASPasswordCredentialIdentity) -> ASPasswordCredential? + @available(iOS 17.0, *) + func fetchCredential(for identity: ASCredentialIdentity) -> ASPasswordCredential? +} + +final class VaultCredentialManager: VaultCredentialManaging { + + private let secureVault: (any AutofillSecureVault)? + private let credentialIdentityStoreManager: AutofillCredentialIdentityStoreManaging + + init(secureVault: (any AutofillSecureVault)?, + credentialIdentityStoreManager: AutofillCredentialIdentityStoreManaging) { + self.secureVault = secureVault + self.credentialIdentityStoreManager = credentialIdentityStoreManager + } + + func fetchCredential(for account: SecureVaultModels.WebsiteAccount) -> ASPasswordCredential { + let password = retrievePassword(for: account) + + updateLastUsed(for: account) + + return ASPasswordCredential(user: account.username ?? "", password: password) + } + + func fetchCredential(for identity: ASPasswordCredentialIdentity) -> ASPasswordCredential? { + return fetchCredentialHelper(for: identity.user, recordIdentifier: identity.recordIdentifier) + } + + @available(iOS 17.0, *) + func fetchCredential(for identity: ASCredentialIdentity) -> ASPasswordCredential? { + return fetchCredentialHelper(for: identity.user, recordIdentifier: identity.recordIdentifier) + } + + // MARK: - Private + + private func retrievePassword(for account: SecureVaultModels.WebsiteAccount) -> String { + guard let accountID = account.id, let accountIdInt = Int64(accountID), let credentials = retrieveCredentials(for: accountIdInt) else { + return "" + } + + return credentials.password.flatMap { String(data: $0, encoding: .utf8) } ?? "" + } + + private func fetchCredentialHelper(for user: String, recordIdentifier: String?) -> ASPasswordCredential? { + guard let recordIdentifier = recordIdentifier, + let accountIdInt = Int64(recordIdentifier), + let credentials = retrieveCredentials(for: accountIdInt) else { + return nil + } + + let passwordCredential = ASPasswordCredential(user: user, + password: credentials.password.flatMap { String(data: $0, encoding: .utf8) } ?? "") + + updateLastUsed(for: credentials.account) + + return passwordCredential + } + + private func retrieveCredentials(for accountId: Int64) -> SecureVaultModels.WebsiteCredentials? { + guard let vault = secureVault else { return nil } + do { + return try vault.websiteCredentialsFor(accountId: accountId) + } catch { + Pixel.fire(pixel: .secureVaultError, error: error) + return nil + } + } + + private func updateLastUsed(for account: SecureVaultModels.WebsiteAccount) { + if let accountID = account.id, let accountIdInt = Int64(accountID), let vault = secureVault { + do { + try vault.updateLastUsedFor(accountId: accountIdInt) + + Task { + if let domain = account.domain { + await credentialIdentityStoreManager.updateCredentialStore(for: domain) + } + } + } catch { + Pixel.fire(pixel: .secureVaultError, error: error) + } + } + } +} diff --git a/AutofillCredentialProvider/Info.plist b/AutofillCredentialProvider/Info.plist new file mode 100644 index 0000000000..c00abcf6dc --- /dev/null +++ b/AutofillCredentialProvider/Info.plist @@ -0,0 +1,31 @@ + + + + + DuckDuckGoGroupIdentifierPrefix + $(GROUP_ID_PREFIX) + NSExtension + + NSExtensionAttributes + + ASCredentialProviderViewControllerClass + $(PRODUCT_MODULE_NAME).CredentialProviderViewController + ASCredentialProviderExtensionShowsConfigurationUI + + ASCredentialProviderExtensionCapabilities + + ProvidesPasswords + + ProvidesTextToInsert + + + + NSExtensionPointIdentifier + com.apple.authentication-services-credential-provider-ui + NSExtensionPrincipalClass + $(PRODUCT_MODULE_NAME).CredentialProviderViewController + + VAULT_APP_GROUP + $(AppIdentifierPrefix)$(VAULT_APP_GROUP) + + diff --git a/AutofillCredentialProvider/bg.lproj/InfoPlist.strings b/AutofillCredentialProvider/bg.lproj/InfoPlist.strings new file mode 100644 index 0000000000..9809aa19f5 --- /dev/null +++ b/AutofillCredentialProvider/bg.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "AutofillCredentialProvider"; + +/* Bundle name */ +"CFBundleName" = "AutofillCredentialProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 DuckDuckGo. All rights reserved."; + diff --git a/AutofillCredentialProvider/bg.lproj/Localizable.strings b/AutofillCredentialProvider/bg.lproj/Localizable.strings new file mode 100644 index 0000000000..82a8dc2b8c --- /dev/null +++ b/AutofillCredentialProvider/bg.lproj/Localizable.strings @@ -0,0 +1,96 @@ +/* Close button title */ +"action.button.close" = "Затваряне"; + +/* Done button title */ +"action.button.done" = "Готово"; + +/* Title of button to launch the DuckDuckGo app */ +"credential.provider.activated.button" = "Отваряне на DuckDuckGo"; + +/* The title of the screen confirming DuckDuckGo can now be used for autofilling passwords */ +"credential.provider.activated.title" = "Автоматичното попълване на пароли е активирано!"; + +/* Default string used if users device is not iPhone or iPad */ +"credential.provider.device.type.default" = "устройство"; + +/* Device type is iPhone */ +"credential.provider.device.type.iphone" = "iPhone"; + +/* Device type is iPad */ +"credential.provider.device.type.pad" = "iPad"; + +/* Cancel button for auth when opening login list */ +"credential.provider.list.auth.cancel" = "Отмени"; + +/* Reason for auth when opening screen with list of saved passwords */ +"credential.provider.list.auth.reason" = "Отключете устройството, за да получите достъп до паролите"; + +/* Address label for login details on autofill */ +"credential.provider.list.details.address" = "Адрес на уебсайта"; + +/* Menu item text for copying autofill login details */ +"credential.provider.list.details.copy-prompt" = "Копиране на %@"; + +/* Title for toast when copying address */ +"credential.provider.list.details.copy-toast.address-copied" = "Адресът е копиран"; + +/* Title for toast when copying notes */ +"credential.provider.list.details.copy-toast.notes-copied" = "Бележките са копирани"; + +/* Title for toast when copying password */ +"credential.provider.list.details.copy-toast.password-copied" = "Паролата е копирана"; + +/* Title for toast when copying username */ +"credential.provider.list.details.copy-toast.username-copied" = "Потребителското име е копирано"; + +/* Accessibility title for a Hide Password button replacing displayed password with ***** */ +"credential.provider.list.details.hide-password" = "Скриване на паролата"; + +/* Message displaying when the login was last updated */ +"credential.provider.list.details.last-updated" = "Последна актуализация %@"; + +/* Login name label for login details on autofill */ +"credential.provider.list.details.login-name" = "Заглавие"; + +/* Notes label for login details on autofill */ +"credential.provider.list.details.notes" = "Бележки"; + +/* Password label for login details on autofill */ +"credential.provider.list.details.password" = "Парола"; + +/* Accessibility title for a Show Password button displaying actial password instead of ***** */ +"credential.provider.list.details.show-password" = "Показване на паролата"; + +/* Username label for login details on autofill */ +"credential.provider.list.details.username" = "Потребителско име"; + +/* Footer label displayed below table section with option to enable autofill */ +"credential.provider.list.empty-view.footer" = "Паролите се съхраняват сигурно на Вашето устройство."; + +/* Title for view displayed when autofill has no items */ +"credential.provider.list.empty-view.title" = "Все още няма запазени пароли"; + +/* Prompt above the title for screen listing autofill logins, example: Choose a password to use for "website.com" */ +"credential.provider.list.prompt" = "Избиране на парола, която да бъде използвана за „%@“"; + +/* Placeholder for search field on autofill login listing */ +"credential.provider.list.search-placeholder" = "Търсене на пароли"; + +/* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ +"credential.provider.list.search.no-results.subtitle" = "за '%@'"; + +/* Title displayed when there are no results on Autofill search */ +"credential.provider.list.search.no-results.title" = "Няма резултати"; + +/* Section title for group of suggested saved logins */ +"credential.provider.list.suggested" = "Предложения"; + +/* Title for screen listing autofill logins */ +"credential.provider.list.title" = "Пароли"; + +/* Message for alert when device authentication is not set, where %@ is iPhone|iPad|device */ +"credential.provider.no-device-auth-set.message" = "Задаване на парола на %@ за автоматично попълване на паролите за DuckDuckGo."; + +/* Title for alert when device authentication is not set */ +"credential.provider.no-device-auth-set.title" = "Изисква се парола на устройството"; + diff --git a/AutofillCredentialProvider/cs.lproj/InfoPlist.strings b/AutofillCredentialProvider/cs.lproj/InfoPlist.strings new file mode 100644 index 0000000000..9809aa19f5 --- /dev/null +++ b/AutofillCredentialProvider/cs.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "AutofillCredentialProvider"; + +/* Bundle name */ +"CFBundleName" = "AutofillCredentialProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 DuckDuckGo. All rights reserved."; + diff --git a/AutofillCredentialProvider/cs.lproj/Localizable.strings b/AutofillCredentialProvider/cs.lproj/Localizable.strings new file mode 100644 index 0000000000..230c19ae1c --- /dev/null +++ b/AutofillCredentialProvider/cs.lproj/Localizable.strings @@ -0,0 +1,96 @@ +/* Close button title */ +"action.button.close" = "Zavřít"; + +/* Done button title */ +"action.button.done" = "Hotovo"; + +/* Title of button to launch the DuckDuckGo app */ +"credential.provider.activated.button" = "Otevřít DuckDuckGo"; + +/* The title of the screen confirming DuckDuckGo can now be used for autofilling passwords */ +"credential.provider.activated.title" = "Automatické vyplňování hesel je zapnuté."; + +/* Default string used if users device is not iPhone or iPad */ +"credential.provider.device.type.default" = "zařízení"; + +/* Device type is iPhone */ +"credential.provider.device.type.iphone" = "iPhonu"; + +/* Device type is iPad */ +"credential.provider.device.type.pad" = "iPadu"; + +/* Cancel button for auth when opening login list */ +"credential.provider.list.auth.cancel" = "Zrušit"; + +/* Reason for auth when opening screen with list of saved passwords */ +"credential.provider.list.auth.reason" = "Pro přístup k heslům zařízení odemkni"; + +/* Address label for login details on autofill */ +"credential.provider.list.details.address" = "URL webové stránky"; + +/* Menu item text for copying autofill login details */ +"credential.provider.list.details.copy-prompt" = "Kopírovat %@"; + +/* Title for toast when copying address */ +"credential.provider.list.details.copy-toast.address-copied" = "Adresa zkopírovaná"; + +/* Title for toast when copying notes */ +"credential.provider.list.details.copy-toast.notes-copied" = "Poznámky zkopírované"; + +/* Title for toast when copying password */ +"credential.provider.list.details.copy-toast.password-copied" = "Heslo zkopírované"; + +/* Title for toast when copying username */ +"credential.provider.list.details.copy-toast.username-copied" = "Uživatelské jméno zkopírované"; + +/* Accessibility title for a Hide Password button replacing displayed password with ***** */ +"credential.provider.list.details.hide-password" = "Skrýt heslo"; + +/* Message displaying when the login was last updated */ +"credential.provider.list.details.last-updated" = "Poslední aktualizace %@"; + +/* Login name label for login details on autofill */ +"credential.provider.list.details.login-name" = "Název"; + +/* Notes label for login details on autofill */ +"credential.provider.list.details.notes" = "Poznámky"; + +/* Password label for login details on autofill */ +"credential.provider.list.details.password" = "Heslo"; + +/* Accessibility title for a Show Password button displaying actial password instead of ***** */ +"credential.provider.list.details.show-password" = "Zobrazit heslo"; + +/* Username label for login details on autofill */ +"credential.provider.list.details.username" = "Uživatelské jméno"; + +/* Footer label displayed below table section with option to enable autofill */ +"credential.provider.list.empty-view.footer" = "Hesla se bezpečně ukládají do tvého zařízení."; + +/* Title for view displayed when autofill has no items */ +"credential.provider.list.empty-view.title" = "Zatím nemáš uložená žádná hesla"; + +/* Prompt above the title for screen listing autofill logins, example: Choose a password to use for "website.com" */ +"credential.provider.list.prompt" = "Vyber si, které heslo použiješ pro účet %@"; + +/* Placeholder for search field on autofill login listing */ +"credential.provider.list.search-placeholder" = "Prohledat hesla"; + +/* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ +"credential.provider.list.search.no-results.subtitle" = "pro „%@“"; + +/* Title displayed when there are no results on Autofill search */ +"credential.provider.list.search.no-results.title" = "Žádné výsledky"; + +/* Section title for group of suggested saved logins */ +"credential.provider.list.suggested" = "Navrhované"; + +/* Title for screen listing autofill logins */ +"credential.provider.list.title" = "Hesla"; + +/* Message for alert when device authentication is not set, where %@ is iPhone|iPad|device */ +"credential.provider.no-device-auth-set.message" = "Nastav si přístupový kód pro zařízení %@ a DuckDuckGo bude automaticky vyplňovat hesla."; + +/* Title for alert when device authentication is not set */ +"credential.provider.no-device-auth-set.title" = "Přístupový kód pro zařízení je povinný"; + diff --git a/AutofillCredentialProvider/da.lproj/InfoPlist.strings b/AutofillCredentialProvider/da.lproj/InfoPlist.strings new file mode 100644 index 0000000000..9809aa19f5 --- /dev/null +++ b/AutofillCredentialProvider/da.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "AutofillCredentialProvider"; + +/* Bundle name */ +"CFBundleName" = "AutofillCredentialProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 DuckDuckGo. All rights reserved."; + diff --git a/AutofillCredentialProvider/da.lproj/Localizable.strings b/AutofillCredentialProvider/da.lproj/Localizable.strings new file mode 100644 index 0000000000..49ea766df6 --- /dev/null +++ b/AutofillCredentialProvider/da.lproj/Localizable.strings @@ -0,0 +1,96 @@ +/* Close button title */ +"action.button.close" = "Luk"; + +/* Done button title */ +"action.button.done" = "Færdig"; + +/* Title of button to launch the DuckDuckGo app */ +"credential.provider.activated.button" = "Åbn DuckDuckGo"; + +/* The title of the screen confirming DuckDuckGo can now be used for autofilling passwords */ +"credential.provider.activated.title" = "Automatisk udfyldning af adgangskoder er aktiveret!"; + +/* Default string used if users device is not iPhone or iPad */ +"credential.provider.device.type.default" = "enhed"; + +/* Device type is iPhone */ +"credential.provider.device.type.iphone" = "iPhone"; + +/* Device type is iPad */ +"credential.provider.device.type.pad" = "iPad"; + +/* Cancel button for auth when opening login list */ +"credential.provider.list.auth.cancel" = "Annullér"; + +/* Reason for auth when opening screen with list of saved passwords */ +"credential.provider.list.auth.reason" = "Lås enheden op for at få adgang til adgangskoder"; + +/* Address label for login details on autofill */ +"credential.provider.list.details.address" = "URL til webstedet"; + +/* Menu item text for copying autofill login details */ +"credential.provider.list.details.copy-prompt" = "Kopiér %@"; + +/* Title for toast when copying address */ +"credential.provider.list.details.copy-toast.address-copied" = "Adresse kopieret"; + +/* Title for toast when copying notes */ +"credential.provider.list.details.copy-toast.notes-copied" = "Noter kopieret"; + +/* Title for toast when copying password */ +"credential.provider.list.details.copy-toast.password-copied" = "Adgangskode kopieret"; + +/* Title for toast when copying username */ +"credential.provider.list.details.copy-toast.username-copied" = "Brugernavn kopieret"; + +/* Accessibility title for a Hide Password button replacing displayed password with ***** */ +"credential.provider.list.details.hide-password" = "Skjul adgangskode"; + +/* Message displaying when the login was last updated */ +"credential.provider.list.details.last-updated" = "Sidst opdateret %@"; + +/* Login name label for login details on autofill */ +"credential.provider.list.details.login-name" = "Titel"; + +/* Notes label for login details on autofill */ +"credential.provider.list.details.notes" = "Noter"; + +/* Password label for login details on autofill */ +"credential.provider.list.details.password" = "Adgangskode"; + +/* Accessibility title for a Show Password button displaying actial password instead of ***** */ +"credential.provider.list.details.show-password" = "Vis adgangskode"; + +/* Username label for login details on autofill */ +"credential.provider.list.details.username" = "Brugernavn"; + +/* Footer label displayed below table section with option to enable autofill */ +"credential.provider.list.empty-view.footer" = "Adgangskoder gemmes sikkert på din enhed."; + +/* Title for view displayed when autofill has no items */ +"credential.provider.list.empty-view.title" = "Ingen adgangskoder gemt endnu"; + +/* Prompt above the title for screen listing autofill logins, example: Choose a password to use for "website.com" */ +"credential.provider.list.prompt" = "Vælg en adgangskode til \"%@\""; + +/* Placeholder for search field on autofill login listing */ +"credential.provider.list.search-placeholder" = "Søg adgangskoder"; + +/* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ +"credential.provider.list.search.no-results.subtitle" = "for '%@'"; + +/* Title displayed when there are no results on Autofill search */ +"credential.provider.list.search.no-results.title" = "Ingen resultater"; + +/* Section title for group of suggested saved logins */ +"credential.provider.list.suggested" = "Foreslået"; + +/* Title for screen listing autofill logins */ +"credential.provider.list.title" = "Adgangskoder"; + +/* Message for alert when device authentication is not set, where %@ is iPhone|iPad|device */ +"credential.provider.no-device-auth-set.message" = "Opret en adgangskode på %@ for automatisk at udfylde dine DuckDuckGo-adgangskoder."; + +/* Title for alert when device authentication is not set */ +"credential.provider.no-device-auth-set.title" = "Adgangskode til enheden påkrævet"; + diff --git a/AutofillCredentialProvider/de.lproj/InfoPlist.strings b/AutofillCredentialProvider/de.lproj/InfoPlist.strings new file mode 100644 index 0000000000..9809aa19f5 --- /dev/null +++ b/AutofillCredentialProvider/de.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "AutofillCredentialProvider"; + +/* Bundle name */ +"CFBundleName" = "AutofillCredentialProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 DuckDuckGo. All rights reserved."; + diff --git a/AutofillCredentialProvider/de.lproj/Localizable.strings b/AutofillCredentialProvider/de.lproj/Localizable.strings new file mode 100644 index 0000000000..e36875c16b --- /dev/null +++ b/AutofillCredentialProvider/de.lproj/Localizable.strings @@ -0,0 +1,96 @@ +/* Close button title */ +"action.button.close" = "Schließen"; + +/* Done button title */ +"action.button.done" = "Fertig"; + +/* Title of button to launch the DuckDuckGo app */ +"credential.provider.activated.button" = "DuckDuckGo öffnen"; + +/* The title of the screen confirming DuckDuckGo can now be used for autofilling passwords */ +"credential.provider.activated.title" = "Automatisches Ausfüllen von Passwörtern aktiviert!"; + +/* Default string used if users device is not iPhone or iPad */ +"credential.provider.device.type.default" = "Gerät"; + +/* Device type is iPhone */ +"credential.provider.device.type.iphone" = "iPhone"; + +/* Device type is iPad */ +"credential.provider.device.type.pad" = "iPad"; + +/* Cancel button for auth when opening login list */ +"credential.provider.list.auth.cancel" = "Abbrechen"; + +/* Reason for auth when opening screen with list of saved passwords */ +"credential.provider.list.auth.reason" = "Gerät entsperren, um auf Passwörter zuzugreifen"; + +/* Address label for login details on autofill */ +"credential.provider.list.details.address" = "URL der Website"; + +/* Menu item text for copying autofill login details */ +"credential.provider.list.details.copy-prompt" = "%@ kopieren"; + +/* Title for toast when copying address */ +"credential.provider.list.details.copy-toast.address-copied" = "Adresse kopiert"; + +/* Title for toast when copying notes */ +"credential.provider.list.details.copy-toast.notes-copied" = "Notizen kopiert"; + +/* Title for toast when copying password */ +"credential.provider.list.details.copy-toast.password-copied" = "Passwort kopiert"; + +/* Title for toast when copying username */ +"credential.provider.list.details.copy-toast.username-copied" = "Benutzername kopiert"; + +/* Accessibility title for a Hide Password button replacing displayed password with ***** */ +"credential.provider.list.details.hide-password" = "Passwort ausblenden"; + +/* Message displaying when the login was last updated */ +"credential.provider.list.details.last-updated" = "Zuletzt aktualisiert %@"; + +/* Login name label for login details on autofill */ +"credential.provider.list.details.login-name" = "Titel"; + +/* Notes label for login details on autofill */ +"credential.provider.list.details.notes" = "Notizen"; + +/* Password label for login details on autofill */ +"credential.provider.list.details.password" = "Passwort"; + +/* Accessibility title for a Show Password button displaying actial password instead of ***** */ +"credential.provider.list.details.show-password" = "Passwort anzeigen"; + +/* Username label for login details on autofill */ +"credential.provider.list.details.username" = "Benutzername"; + +/* Footer label displayed below table section with option to enable autofill */ +"credential.provider.list.empty-view.footer" = "Passwörter werden sicher auf deinem Gerät gespeichert."; + +/* Title for view displayed when autofill has no items */ +"credential.provider.list.empty-view.title" = "Noch keine Passwörter gespeichert"; + +/* Prompt above the title for screen listing autofill logins, example: Choose a password to use for "website.com" */ +"credential.provider.list.prompt" = "Wähle ein Passwort, das du für „%@“ verwendest"; + +/* Placeholder for search field on autofill login listing */ +"credential.provider.list.search-placeholder" = "Passwörter suchen"; + +/* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ +"credential.provider.list.search.no-results.subtitle" = "für „%@“"; + +/* Title displayed when there are no results on Autofill search */ +"credential.provider.list.search.no-results.title" = "Keine Ergebnisse"; + +/* Section title for group of suggested saved logins */ +"credential.provider.list.suggested" = "Vorgeschlagen"; + +/* Title for screen listing autofill logins */ +"credential.provider.list.title" = "Passwörter"; + +/* Message for alert when device authentication is not set, where %@ is iPhone|iPad|device */ +"credential.provider.no-device-auth-set.message" = "Lege einen Passcode für dein %@ fest, um deine DuckDuckGo-Passwörter automatisch auszufüllen."; + +/* Title for alert when device authentication is not set */ +"credential.provider.no-device-auth-set.title" = "Geräte-Passcode erforderlich"; + diff --git a/AutofillCredentialProvider/el.lproj/InfoPlist.strings b/AutofillCredentialProvider/el.lproj/InfoPlist.strings new file mode 100644 index 0000000000..9809aa19f5 --- /dev/null +++ b/AutofillCredentialProvider/el.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "AutofillCredentialProvider"; + +/* Bundle name */ +"CFBundleName" = "AutofillCredentialProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 DuckDuckGo. All rights reserved."; + diff --git a/AutofillCredentialProvider/el.lproj/Localizable.strings b/AutofillCredentialProvider/el.lproj/Localizable.strings new file mode 100644 index 0000000000..cc073f02b6 --- /dev/null +++ b/AutofillCredentialProvider/el.lproj/Localizable.strings @@ -0,0 +1,96 @@ +/* Close button title */ +"action.button.close" = "Κλείσιμο"; + +/* Done button title */ +"action.button.done" = "Ολοκληρώθηκε"; + +/* Title of button to launch the DuckDuckGo app */ +"credential.provider.activated.button" = "Άνοιγμα του DuckDuckGo"; + +/* The title of the screen confirming DuckDuckGo can now be used for autofilling passwords */ +"credential.provider.activated.title" = "Η Αυτόματη συμπλήρωση κωδικών πρόσβασης ενεργοποιήθηκε!"; + +/* Default string used if users device is not iPhone or iPad */ +"credential.provider.device.type.default" = "συσκευή"; + +/* Device type is iPhone */ +"credential.provider.device.type.iphone" = "iPhone"; + +/* Device type is iPad */ +"credential.provider.device.type.pad" = "iPad"; + +/* Cancel button for auth when opening login list */ +"credential.provider.list.auth.cancel" = "Ακύρωση"; + +/* Reason for auth when opening screen with list of saved passwords */ +"credential.provider.list.auth.reason" = "Ξεκλειδώστε τη συσκευή για πρόσβαση σε κωδικούς πρόσβασης"; + +/* Address label for login details on autofill */ +"credential.provider.list.details.address" = "Διεύθυνση URL ιστότοπου"; + +/* Menu item text for copying autofill login details */ +"credential.provider.list.details.copy-prompt" = "Αντιγραφή %@"; + +/* Title for toast when copying address */ +"credential.provider.list.details.copy-toast.address-copied" = "Η διεύθυνση αντιγράφηκε"; + +/* Title for toast when copying notes */ +"credential.provider.list.details.copy-toast.notes-copied" = "Οι σημειώσεις αντιγράφηκαν"; + +/* Title for toast when copying password */ +"credential.provider.list.details.copy-toast.password-copied" = "Ο κωδικός πρόσβασης αντιγράφηκε"; + +/* Title for toast when copying username */ +"credential.provider.list.details.copy-toast.username-copied" = "Το όνομα χρήστη αντιγράφηκε"; + +/* Accessibility title for a Hide Password button replacing displayed password with ***** */ +"credential.provider.list.details.hide-password" = "Απόκρυψη κωδικού πρόσβασης"; + +/* Message displaying when the login was last updated */ +"credential.provider.list.details.last-updated" = "Τελευταία ενημέρωση %@"; + +/* Login name label for login details on autofill */ +"credential.provider.list.details.login-name" = "Τίτλος"; + +/* Notes label for login details on autofill */ +"credential.provider.list.details.notes" = "Σημειώσεις"; + +/* Password label for login details on autofill */ +"credential.provider.list.details.password" = "Κωδικός πρόσβασης"; + +/* Accessibility title for a Show Password button displaying actial password instead of ***** */ +"credential.provider.list.details.show-password" = "Εμφάνιση κωδικού πρόσβασης"; + +/* Username label for login details on autofill */ +"credential.provider.list.details.username" = "Όνομα χρήστη"; + +/* Footer label displayed below table section with option to enable autofill */ +"credential.provider.list.empty-view.footer" = "Οι κωδικοί πρόσβασης αποθηκεύονται με ασφάλεια στη συσκευή σας."; + +/* Title for view displayed when autofill has no items */ +"credential.provider.list.empty-view.title" = "Δεν έχουν αποθηκευτεί ακόμα κωδικοί πρόσβασης"; + +/* Prompt above the title for screen listing autofill logins, example: Choose a password to use for "website.com" */ +"credential.provider.list.prompt" = "Επιλέξτε έναν κωδικό πρόσβασης για χρήση στο «%@»"; + +/* Placeholder for search field on autofill login listing */ +"credential.provider.list.search-placeholder" = "Αναζήτηση κωδικών πρόσβασης"; + +/* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ +"credential.provider.list.search.no-results.subtitle" = "για '%@'"; + +/* Title displayed when there are no results on Autofill search */ +"credential.provider.list.search.no-results.title" = "Κανένα αποτέλεσμα"; + +/* Section title for group of suggested saved logins */ +"credential.provider.list.suggested" = "Προτεινόμενο"; + +/* Title for screen listing autofill logins */ +"credential.provider.list.title" = "Κωδικός πρόσβασης"; + +/* Message for alert when device authentication is not set, where %@ is iPhone|iPad|device */ +"credential.provider.no-device-auth-set.message" = "Ορίστε έναν κωδικό πρόσβασης στο %@ για αυτόματη συμπλήρωση των κωδικών πρόσβασης DuckDuckGo."; + +/* Title for alert when device authentication is not set */ +"credential.provider.no-device-auth-set.title" = "Απαιτείται κωδικός πρόσβασης συσκευής"; + diff --git a/AutofillCredentialProvider/es.lproj/InfoPlist.strings b/AutofillCredentialProvider/es.lproj/InfoPlist.strings new file mode 100644 index 0000000000..9809aa19f5 --- /dev/null +++ b/AutofillCredentialProvider/es.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "AutofillCredentialProvider"; + +/* Bundle name */ +"CFBundleName" = "AutofillCredentialProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 DuckDuckGo. All rights reserved."; + diff --git a/AutofillCredentialProvider/es.lproj/Localizable.strings b/AutofillCredentialProvider/es.lproj/Localizable.strings new file mode 100644 index 0000000000..572db888b4 --- /dev/null +++ b/AutofillCredentialProvider/es.lproj/Localizable.strings @@ -0,0 +1,96 @@ +/* Close button title */ +"action.button.close" = "Cerrar"; + +/* Done button title */ +"action.button.done" = "Hecho"; + +/* Title of button to launch the DuckDuckGo app */ +"credential.provider.activated.button" = "Abrir DuckDuckGo"; + +/* The title of the screen confirming DuckDuckGo can now be used for autofilling passwords */ +"credential.provider.activated.title" = "¡La función de autocompletar contraseñas está activada!"; + +/* Default string used if users device is not iPhone or iPad */ +"credential.provider.device.type.default" = "dispositivo"; + +/* Device type is iPhone */ +"credential.provider.device.type.iphone" = "iPhone"; + +/* Device type is iPad */ +"credential.provider.device.type.pad" = "iPad"; + +/* Cancel button for auth when opening login list */ +"credential.provider.list.auth.cancel" = "Cancelar"; + +/* Reason for auth when opening screen with list of saved passwords */ +"credential.provider.list.auth.reason" = "Desbloquear dispositivo para acceder a contraseñas"; + +/* Address label for login details on autofill */ +"credential.provider.list.details.address" = "URL del sitio web"; + +/* Menu item text for copying autofill login details */ +"credential.provider.list.details.copy-prompt" = "Copiar %@"; + +/* Title for toast when copying address */ +"credential.provider.list.details.copy-toast.address-copied" = "Dirección copiada"; + +/* Title for toast when copying notes */ +"credential.provider.list.details.copy-toast.notes-copied" = "Notas copiadas"; + +/* Title for toast when copying password */ +"credential.provider.list.details.copy-toast.password-copied" = "Contraseña copiada"; + +/* Title for toast when copying username */ +"credential.provider.list.details.copy-toast.username-copied" = "Nombre de usuario copiado"; + +/* Accessibility title for a Hide Password button replacing displayed password with ***** */ +"credential.provider.list.details.hide-password" = "Ocultar contraseña"; + +/* Message displaying when the login was last updated */ +"credential.provider.list.details.last-updated" = "Última actualización % @"; + +/* Login name label for login details on autofill */ +"credential.provider.list.details.login-name" = "Título"; + +/* Notes label for login details on autofill */ +"credential.provider.list.details.notes" = "Notas"; + +/* Password label for login details on autofill */ +"credential.provider.list.details.password" = "Contraseña"; + +/* Accessibility title for a Show Password button displaying actial password instead of ***** */ +"credential.provider.list.details.show-password" = "Mostrar contraseña"; + +/* Username label for login details on autofill */ +"credential.provider.list.details.username" = "Nombre de usuario"; + +/* Footer label displayed below table section with option to enable autofill */ +"credential.provider.list.empty-view.footer" = "Las contraseñas se almacenan de forma segura en tu dispositivo."; + +/* Title for view displayed when autofill has no items */ +"credential.provider.list.empty-view.title" = "Aún no hay contraseñas guardadas"; + +/* Prompt above the title for screen listing autofill logins, example: Choose a password to use for "website.com" */ +"credential.provider.list.prompt" = "Elige una contraseña para \"%@\""; + +/* Placeholder for search field on autofill login listing */ +"credential.provider.list.search-placeholder" = "Buscar contraseñas"; + +/* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ +"credential.provider.list.search.no-results.subtitle" = "para '%@'"; + +/* Title displayed when there are no results on Autofill search */ +"credential.provider.list.search.no-results.title" = "Sin resultados"; + +/* Section title for group of suggested saved logins */ +"credential.provider.list.suggested" = "Sugerencias"; + +/* Title for screen listing autofill logins */ +"credential.provider.list.title" = "Contraseñas"; + +/* Message for alert when device authentication is not set, where %@ is iPhone|iPad|device */ +"credential.provider.no-device-auth-set.message" = "Configura un código de acceso en %@ para autocompletar tus contraseñas de DuckDuckGo."; + +/* Title for alert when device authentication is not set */ +"credential.provider.no-device-auth-set.title" = "Se requiere código de acceso del dispositivo"; + diff --git a/AutofillCredentialProvider/et.lproj/InfoPlist.strings b/AutofillCredentialProvider/et.lproj/InfoPlist.strings new file mode 100644 index 0000000000..9809aa19f5 --- /dev/null +++ b/AutofillCredentialProvider/et.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "AutofillCredentialProvider"; + +/* Bundle name */ +"CFBundleName" = "AutofillCredentialProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 DuckDuckGo. All rights reserved."; + diff --git a/AutofillCredentialProvider/et.lproj/Localizable.strings b/AutofillCredentialProvider/et.lproj/Localizable.strings new file mode 100644 index 0000000000..0e60b0e2e6 --- /dev/null +++ b/AutofillCredentialProvider/et.lproj/Localizable.strings @@ -0,0 +1,96 @@ +/* Close button title */ +"action.button.close" = "Sulge"; + +/* Done button title */ +"action.button.done" = "Valmis"; + +/* Title of button to launch the DuckDuckGo app */ +"credential.provider.activated.button" = "Ava DuckDuckGo"; + +/* The title of the screen confirming DuckDuckGo can now be used for autofilling passwords */ +"credential.provider.activated.title" = "Paroolide automaatne täitmine on aktiveeritud!"; + +/* Default string used if users device is not iPhone or iPad */ +"credential.provider.device.type.default" = "seade"; + +/* Device type is iPhone */ +"credential.provider.device.type.iphone" = "iPhone"; + +/* Device type is iPad */ +"credential.provider.device.type.pad" = "iPad"; + +/* Cancel button for auth when opening login list */ +"credential.provider.list.auth.cancel" = "Tühista"; + +/* Reason for auth when opening screen with list of saved passwords */ +"credential.provider.list.auth.reason" = "Paroolidele juurdepääsuks ava seade"; + +/* Address label for login details on autofill */ +"credential.provider.list.details.address" = "Veebisaidi URL"; + +/* Menu item text for copying autofill login details */ +"credential.provider.list.details.copy-prompt" = "Kopeeri %@"; + +/* Title for toast when copying address */ +"credential.provider.list.details.copy-toast.address-copied" = "Aadress on kopeeritud"; + +/* Title for toast when copying notes */ +"credential.provider.list.details.copy-toast.notes-copied" = "Märkmed on kopeeritud"; + +/* Title for toast when copying password */ +"credential.provider.list.details.copy-toast.password-copied" = "Parool on kopeeritud"; + +/* Title for toast when copying username */ +"credential.provider.list.details.copy-toast.username-copied" = "Kasutajanimi on kopeeritud"; + +/* Accessibility title for a Hide Password button replacing displayed password with ***** */ +"credential.provider.list.details.hide-password" = "Peida parool"; + +/* Message displaying when the login was last updated */ +"credential.provider.list.details.last-updated" = "Viimati uuendatud %@"; + +/* Login name label for login details on autofill */ +"credential.provider.list.details.login-name" = "Pealkiri"; + +/* Notes label for login details on autofill */ +"credential.provider.list.details.notes" = "Märkused"; + +/* Password label for login details on autofill */ +"credential.provider.list.details.password" = "Parool"; + +/* Accessibility title for a Show Password button displaying actial password instead of ***** */ +"credential.provider.list.details.show-password" = "Kuva parool"; + +/* Username label for login details on autofill */ +"credential.provider.list.details.username" = "Kasutajanimi"; + +/* Footer label displayed below table section with option to enable autofill */ +"credential.provider.list.empty-view.footer" = "Paroolid salvestatakse turvaliselt sinu seadmesse."; + +/* Title for view displayed when autofill has no items */ +"credential.provider.list.empty-view.title" = "Paroole ei ole veel salvestatud"; + +/* Prompt above the title for screen listing autofill logins, example: Choose a password to use for "website.com" */ +"credential.provider.list.prompt" = "Valige salasõna, mida kasutada \"%@\" jaoks"; + +/* Placeholder for search field on autofill login listing */ +"credential.provider.list.search-placeholder" = "Otsi paroole"; + +/* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ +"credential.provider.list.search.no-results.subtitle" = "otsinguga '%@'"; + +/* Title displayed when there are no results on Autofill search */ +"credential.provider.list.search.no-results.title" = "Tulemusi pole"; + +/* Section title for group of suggested saved logins */ +"credential.provider.list.suggested" = "Soovitatud"; + +/* Title for screen listing autofill logins */ +"credential.provider.list.title" = "Paroolid"; + +/* Message for alert when device authentication is not set, where %@ is iPhone|iPad|device */ +"credential.provider.no-device-auth-set.message" = "DuckDuckGo paroolide automaatseks täitmiseks määrake saidile %@ pääsukood."; + +/* Title for alert when device authentication is not set */ +"credential.provider.no-device-auth-set.title" = "Nõutav seadme pääsukood"; + diff --git a/AutofillCredentialProvider/fi.lproj/InfoPlist.strings b/AutofillCredentialProvider/fi.lproj/InfoPlist.strings new file mode 100644 index 0000000000..9809aa19f5 --- /dev/null +++ b/AutofillCredentialProvider/fi.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "AutofillCredentialProvider"; + +/* Bundle name */ +"CFBundleName" = "AutofillCredentialProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 DuckDuckGo. All rights reserved."; + diff --git a/AutofillCredentialProvider/fi.lproj/Localizable.strings b/AutofillCredentialProvider/fi.lproj/Localizable.strings new file mode 100644 index 0000000000..6bbcdd63c0 --- /dev/null +++ b/AutofillCredentialProvider/fi.lproj/Localizable.strings @@ -0,0 +1,96 @@ +/* Close button title */ +"action.button.close" = "Sulje"; + +/* Done button title */ +"action.button.done" = "Valmis"; + +/* Title of button to launch the DuckDuckGo app */ +"credential.provider.activated.button" = "Avaa DuckDuckGo"; + +/* The title of the screen confirming DuckDuckGo can now be used for autofilling passwords */ +"credential.provider.activated.title" = "Salasanojen automaattinen täyttö on nyt käytössä!"; + +/* Default string used if users device is not iPhone or iPad */ +"credential.provider.device.type.default" = "laitteelle"; + +/* Device type is iPhone */ +"credential.provider.device.type.iphone" = "iPhonelle"; + +/* Device type is iPad */ +"credential.provider.device.type.pad" = "iPadille"; + +/* Cancel button for auth when opening login list */ +"credential.provider.list.auth.cancel" = "Peruuta"; + +/* Reason for auth when opening screen with list of saved passwords */ +"credential.provider.list.auth.reason" = "Avaa laitteen lukitus päästäksesi salasanoihin"; + +/* Address label for login details on autofill */ +"credential.provider.list.details.address" = "Sivuston URL-osoite"; + +/* Menu item text for copying autofill login details */ +"credential.provider.list.details.copy-prompt" = "Kopioi %@"; + +/* Title for toast when copying address */ +"credential.provider.list.details.copy-toast.address-copied" = "Osoite kopioitu"; + +/* Title for toast when copying notes */ +"credential.provider.list.details.copy-toast.notes-copied" = "Muistiinpanot kopioitu"; + +/* Title for toast when copying password */ +"credential.provider.list.details.copy-toast.password-copied" = "Salasana kopioitu"; + +/* Title for toast when copying username */ +"credential.provider.list.details.copy-toast.username-copied" = "Käyttäjätunnus kopioitu"; + +/* Accessibility title for a Hide Password button replacing displayed password with ***** */ +"credential.provider.list.details.hide-password" = "Piilota salasana"; + +/* Message displaying when the login was last updated */ +"credential.provider.list.details.last-updated" = "Viimeksi päivitetty %@"; + +/* Login name label for login details on autofill */ +"credential.provider.list.details.login-name" = "Otsikko"; + +/* Notes label for login details on autofill */ +"credential.provider.list.details.notes" = "Huomautukset"; + +/* Password label for login details on autofill */ +"credential.provider.list.details.password" = "Salasana"; + +/* Accessibility title for a Show Password button displaying actial password instead of ***** */ +"credential.provider.list.details.show-password" = "Näytä salasana"; + +/* Username label for login details on autofill */ +"credential.provider.list.details.username" = "Käyttäjätunnus"; + +/* Footer label displayed below table section with option to enable autofill */ +"credential.provider.list.empty-view.footer" = "Salasanat tallennetaan laitteellesi turvallisesti."; + +/* Title for view displayed when autofill has no items */ +"credential.provider.list.empty-view.title" = "Salasanoja ei ole vielä tallennettu"; + +/* Prompt above the title for screen listing autofill logins, example: Choose a password to use for "website.com" */ +"credential.provider.list.prompt" = "Valitse sivuston salasana: %@"; + +/* Placeholder for search field on autofill login listing */ +"credential.provider.list.search-placeholder" = "Etsi salasanoja"; + +/* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ +"credential.provider.list.search.no-results.subtitle" = "kohteelle '%@'"; + +/* Title displayed when there are no results on Autofill search */ +"credential.provider.list.search.no-results.title" = "Ei tuloksia"; + +/* Section title for group of suggested saved logins */ +"credential.provider.list.suggested" = "Ehdotettu"; + +/* Title for screen listing autofill logins */ +"credential.provider.list.title" = "Salasanat"; + +/* Message for alert when device authentication is not set, where %@ is iPhone|iPad|device */ +"credential.provider.no-device-auth-set.message" = "Jos haluat, että DuckDuckGo täyttää salasanasi automaattisesti, määritä pääsykoodi laitteessa %@."; + +/* Title for alert when device authentication is not set */ +"credential.provider.no-device-auth-set.title" = "Edellyttää laitteen pääsykoodia"; + diff --git a/AutofillCredentialProvider/fr.lproj/InfoPlist.strings b/AutofillCredentialProvider/fr.lproj/InfoPlist.strings new file mode 100644 index 0000000000..9809aa19f5 --- /dev/null +++ b/AutofillCredentialProvider/fr.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "AutofillCredentialProvider"; + +/* Bundle name */ +"CFBundleName" = "AutofillCredentialProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 DuckDuckGo. All rights reserved."; + diff --git a/AutofillCredentialProvider/fr.lproj/Localizable.strings b/AutofillCredentialProvider/fr.lproj/Localizable.strings new file mode 100644 index 0000000000..8f01a4e036 --- /dev/null +++ b/AutofillCredentialProvider/fr.lproj/Localizable.strings @@ -0,0 +1,96 @@ +/* Close button title */ +"action.button.close" = "Fermer"; + +/* Done button title */ +"action.button.done" = "Terminé"; + +/* Title of button to launch the DuckDuckGo app */ +"credential.provider.activated.button" = "Ouvrir DuckDuckGo"; + +/* The title of the screen confirming DuckDuckGo can now be used for autofilling passwords */ +"credential.provider.activated.title" = "Saisie automatique des mots de passe activée !"; + +/* Default string used if users device is not iPhone or iPad */ +"credential.provider.device.type.default" = "appareil"; + +/* Device type is iPhone */ +"credential.provider.device.type.iphone" = "iPhone"; + +/* Device type is iPad */ +"credential.provider.device.type.pad" = "iPad"; + +/* Cancel button for auth when opening login list */ +"credential.provider.list.auth.cancel" = "Annuler"; + +/* Reason for auth when opening screen with list of saved passwords */ +"credential.provider.list.auth.reason" = "Déverrouiller l'appareil pour accéder aux mots de passe"; + +/* Address label for login details on autofill */ +"credential.provider.list.details.address" = "URL du site Web"; + +/* Menu item text for copying autofill login details */ +"credential.provider.list.details.copy-prompt" = "Copier %@"; + +/* Title for toast when copying address */ +"credential.provider.list.details.copy-toast.address-copied" = "Adresse copiée"; + +/* Title for toast when copying notes */ +"credential.provider.list.details.copy-toast.notes-copied" = "Notes copiées"; + +/* Title for toast when copying password */ +"credential.provider.list.details.copy-toast.password-copied" = "Mot de passe copié"; + +/* Title for toast when copying username */ +"credential.provider.list.details.copy-toast.username-copied" = "Nom d'utilisateur copié"; + +/* Accessibility title for a Hide Password button replacing displayed password with ***** */ +"credential.provider.list.details.hide-password" = "Masquer le mot de passe"; + +/* Message displaying when the login was last updated */ +"credential.provider.list.details.last-updated" = "Dernière modification : %@"; + +/* Login name label for login details on autofill */ +"credential.provider.list.details.login-name" = "Titre"; + +/* Notes label for login details on autofill */ +"credential.provider.list.details.notes" = "Notes"; + +/* Password label for login details on autofill */ +"credential.provider.list.details.password" = "Mot de passe"; + +/* Accessibility title for a Show Password button displaying actial password instead of ***** */ +"credential.provider.list.details.show-password" = "Afficher le mot de passe"; + +/* Username label for login details on autofill */ +"credential.provider.list.details.username" = "Nom d'utilisateur"; + +/* Footer label displayed below table section with option to enable autofill */ +"credential.provider.list.empty-view.footer" = "Les mots de passe sont stockés en toute sécurité sur votre appareil."; + +/* Title for view displayed when autofill has no items */ +"credential.provider.list.empty-view.title" = "Aucun mot de passe n'a été enregistré"; + +/* Prompt above the title for screen listing autofill logins, example: Choose a password to use for "website.com" */ +"credential.provider.list.prompt" = "Choisissez un mot de passe à utiliser pour « %@ »"; + +/* Placeholder for search field on autofill login listing */ +"credential.provider.list.search-placeholder" = "Rechercher un mot de passe"; + +/* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ +"credential.provider.list.search.no-results.subtitle" = "pour '%@'"; + +/* Title displayed when there are no results on Autofill search */ +"credential.provider.list.search.no-results.title" = "Aucun résultat"; + +/* Section title for group of suggested saved logins */ +"credential.provider.list.suggested" = "Suggéré"; + +/* Title for screen listing autofill logins */ +"credential.provider.list.title" = "Mots de passe"; + +/* Message for alert when device authentication is not set, where %@ is iPhone|iPad|device */ +"credential.provider.no-device-auth-set.message" = "Définissez un code d'accès sur %@ pour saisir automatiquement vos mots de passe DuckDuckGo."; + +/* Title for alert when device authentication is not set */ +"credential.provider.no-device-auth-set.title" = "Code d'accès de l'appareil requis"; + diff --git a/AutofillCredentialProvider/hr.lproj/InfoPlist.strings b/AutofillCredentialProvider/hr.lproj/InfoPlist.strings new file mode 100644 index 0000000000..9809aa19f5 --- /dev/null +++ b/AutofillCredentialProvider/hr.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "AutofillCredentialProvider"; + +/* Bundle name */ +"CFBundleName" = "AutofillCredentialProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 DuckDuckGo. All rights reserved."; + diff --git a/AutofillCredentialProvider/hr.lproj/Localizable.strings b/AutofillCredentialProvider/hr.lproj/Localizable.strings new file mode 100644 index 0000000000..38cc9387d7 --- /dev/null +++ b/AutofillCredentialProvider/hr.lproj/Localizable.strings @@ -0,0 +1,96 @@ +/* Close button title */ +"action.button.close" = "Zatvori"; + +/* Done button title */ +"action.button.done" = "Gotovo"; + +/* Title of button to launch the DuckDuckGo app */ +"credential.provider.activated.button" = "Otvori DuckDuckGo"; + +/* The title of the screen confirming DuckDuckGo can now be used for autofilling passwords */ +"credential.provider.activated.title" = "Automatsko popunjavanje lozinki je aktivirano!"; + +/* Default string used if users device is not iPhone or iPad */ +"credential.provider.device.type.default" = "uređaj"; + +/* Device type is iPhone */ +"credential.provider.device.type.iphone" = "iPhone"; + +/* Device type is iPad */ +"credential.provider.device.type.pad" = "iPad"; + +/* Cancel button for auth when opening login list */ +"credential.provider.list.auth.cancel" = "Otkaži"; + +/* Reason for auth when opening screen with list of saved passwords */ +"credential.provider.list.auth.reason" = "Otključaj uređaj za pristup lozinkama"; + +/* Address label for login details on autofill */ +"credential.provider.list.details.address" = "URL web lokacije"; + +/* Menu item text for copying autofill login details */ +"credential.provider.list.details.copy-prompt" = "Kopiraj %@"; + +/* Title for toast when copying address */ +"credential.provider.list.details.copy-toast.address-copied" = "Adresa je kopirana"; + +/* Title for toast when copying notes */ +"credential.provider.list.details.copy-toast.notes-copied" = "Bilješke su kopirane"; + +/* Title for toast when copying password */ +"credential.provider.list.details.copy-toast.password-copied" = "Lozinka je kopirana"; + +/* Title for toast when copying username */ +"credential.provider.list.details.copy-toast.username-copied" = "Korisničko ime je kopirano"; + +/* Accessibility title for a Hide Password button replacing displayed password with ***** */ +"credential.provider.list.details.hide-password" = "Sakrij lozinku"; + +/* Message displaying when the login was last updated */ +"credential.provider.list.details.last-updated" = "Posljednje ažuriranje: %@"; + +/* Login name label for login details on autofill */ +"credential.provider.list.details.login-name" = "Naslov"; + +/* Notes label for login details on autofill */ +"credential.provider.list.details.notes" = "Bilješke"; + +/* Password label for login details on autofill */ +"credential.provider.list.details.password" = "Lozinka"; + +/* Accessibility title for a Show Password button displaying actial password instead of ***** */ +"credential.provider.list.details.show-password" = "Pokaži lozinku"; + +/* Username label for login details on autofill */ +"credential.provider.list.details.username" = "Korisničko ime"; + +/* Footer label displayed below table section with option to enable autofill */ +"credential.provider.list.empty-view.footer" = "Lozinke su sigurno pohranjene na tvom uređaju."; + +/* Title for view displayed when autofill has no items */ +"credential.provider.list.empty-view.title" = "Još nema spremljenih lozinki"; + +/* Prompt above the title for screen listing autofill logins, example: Choose a password to use for "website.com" */ +"credential.provider.list.prompt" = "Odaberi lozinku koju ćeš koristiti za \"%@\""; + +/* Placeholder for search field on autofill login listing */ +"credential.provider.list.search-placeholder" = "Pretraživanje lozinki"; + +/* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ +"credential.provider.list.search.no-results.subtitle" = "za '%@'"; + +/* Title displayed when there are no results on Autofill search */ +"credential.provider.list.search.no-results.title" = "Nema rezultata"; + +/* Section title for group of suggested saved logins */ +"credential.provider.list.suggested" = "Predloženo"; + +/* Title for screen listing autofill logins */ +"credential.provider.list.title" = "Lozinke"; + +/* Message for alert when device authentication is not set, where %@ is iPhone|iPad|device */ +"credential.provider.no-device-auth-set.message" = "Postavi šifru na uređaju %@ kako bi se tvoje DuckDuckGo lozinke popunjavale automatski."; + +/* Title for alert when device authentication is not set */ +"credential.provider.no-device-auth-set.title" = "Potrebna je šifra uređaja."; + diff --git a/AutofillCredentialProvider/hu.lproj/InfoPlist.strings b/AutofillCredentialProvider/hu.lproj/InfoPlist.strings new file mode 100644 index 0000000000..9809aa19f5 --- /dev/null +++ b/AutofillCredentialProvider/hu.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "AutofillCredentialProvider"; + +/* Bundle name */ +"CFBundleName" = "AutofillCredentialProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 DuckDuckGo. All rights reserved."; + diff --git a/AutofillCredentialProvider/hu.lproj/Localizable.strings b/AutofillCredentialProvider/hu.lproj/Localizable.strings new file mode 100644 index 0000000000..c21a3bb9fb --- /dev/null +++ b/AutofillCredentialProvider/hu.lproj/Localizable.strings @@ -0,0 +1,96 @@ +/* Close button title */ +"action.button.close" = "Bezárás"; + +/* Done button title */ +"action.button.done" = "Kész"; + +/* Title of button to launch the DuckDuckGo app */ +"credential.provider.activated.button" = "Nyisd meg a DuckDuckGo-alkalmazást"; + +/* The title of the screen confirming DuckDuckGo can now be used for autofilling passwords */ +"credential.provider.activated.title" = "Jelszavak automatikus kitöltése aktiválva!"; + +/* Default string used if users device is not iPhone or iPad */ +"credential.provider.device.type.default" = "eszközön"; + +/* Device type is iPhone */ +"credential.provider.device.type.iphone" = "iPhone készüléken"; + +/* Device type is iPad */ +"credential.provider.device.type.pad" = "iPad készüléken"; + +/* Cancel button for auth when opening login list */ +"credential.provider.list.auth.cancel" = "Mégsem"; + +/* Reason for auth when opening screen with list of saved passwords */ +"credential.provider.list.auth.reason" = "Oldd fel az eszközt a jelszavakhoz való hozzáféréshez"; + +/* Address label for login details on autofill */ +"credential.provider.list.details.address" = "Webhely URL-címe"; + +/* Menu item text for copying autofill login details */ +"credential.provider.list.details.copy-prompt" = "%@ másolása"; + +/* Title for toast when copying address */ +"credential.provider.list.details.copy-toast.address-copied" = "Cím másolva"; + +/* Title for toast when copying notes */ +"credential.provider.list.details.copy-toast.notes-copied" = "Megjegyzések másolva"; + +/* Title for toast when copying password */ +"credential.provider.list.details.copy-toast.password-copied" = "Jelszó másolva"; + +/* Title for toast when copying username */ +"credential.provider.list.details.copy-toast.username-copied" = "Felhasználónév másolva"; + +/* Accessibility title for a Hide Password button replacing displayed password with ***** */ +"credential.provider.list.details.hide-password" = "Jelszó elrejtése"; + +/* Message displaying when the login was last updated */ +"credential.provider.list.details.last-updated" = "Utolsó frissítés: %@"; + +/* Login name label for login details on autofill */ +"credential.provider.list.details.login-name" = "Cím"; + +/* Notes label for login details on autofill */ +"credential.provider.list.details.notes" = "Megjegyzések"; + +/* Password label for login details on autofill */ +"credential.provider.list.details.password" = "Jelszó"; + +/* Accessibility title for a Show Password button displaying actial password instead of ***** */ +"credential.provider.list.details.show-password" = "Jelszó megjelenítése"; + +/* Username label for login details on autofill */ +"credential.provider.list.details.username" = "Felhasználónév"; + +/* Footer label displayed below table section with option to enable autofill */ +"credential.provider.list.empty-view.footer" = "A jelszavakat az eszközöd biztonságosan tárolja."; + +/* Title for view displayed when autofill has no items */ +"credential.provider.list.empty-view.title" = "Még nincsenek mentett jelszavak"; + +/* Prompt above the title for screen listing autofill logins, example: Choose a password to use for "website.com" */ +"credential.provider.list.prompt" = "Válassz egy jelszót ehhez: \"%@\""; + +/* Placeholder for search field on autofill login listing */ +"credential.provider.list.search-placeholder" = "Jelszavak keresése"; + +/* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ +"credential.provider.list.search.no-results.subtitle" = "erre: „%@“"; + +/* Title displayed when there are no results on Autofill search */ +"credential.provider.list.search.no-results.title" = "Nincs találat"; + +/* Section title for group of suggested saved logins */ +"credential.provider.list.suggested" = "Javasolt"; + +/* Title for screen listing autofill logins */ +"credential.provider.list.title" = "Jelszavak"; + +/* Message for alert when device authentication is not set, where %@ is iPhone|iPad|device */ +"credential.provider.no-device-auth-set.message" = "A DuckDuckGo-jelszavak automatikus kitöltéséhez állíts be egy jelkódot itt: %@."; + +/* Title for alert when device authentication is not set */ +"credential.provider.no-device-auth-set.title" = "Meg kell adni az eszköz jelkódját"; + diff --git a/AutofillCredentialProvider/it.lproj/InfoPlist.strings b/AutofillCredentialProvider/it.lproj/InfoPlist.strings new file mode 100644 index 0000000000..9809aa19f5 --- /dev/null +++ b/AutofillCredentialProvider/it.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "AutofillCredentialProvider"; + +/* Bundle name */ +"CFBundleName" = "AutofillCredentialProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 DuckDuckGo. All rights reserved."; + diff --git a/AutofillCredentialProvider/it.lproj/Localizable.strings b/AutofillCredentialProvider/it.lproj/Localizable.strings new file mode 100644 index 0000000000..5b97f6c41c --- /dev/null +++ b/AutofillCredentialProvider/it.lproj/Localizable.strings @@ -0,0 +1,96 @@ +/* Close button title */ +"action.button.close" = "Chiudi"; + +/* Done button title */ +"action.button.done" = "Fatto"; + +/* Title of button to launch the DuckDuckGo app */ +"credential.provider.activated.button" = "Apri DuckDuckGo"; + +/* The title of the screen confirming DuckDuckGo can now be used for autofilling passwords */ +"credential.provider.activated.title" = "Compilazione automatica delle password attivata!"; + +/* Default string used if users device is not iPhone or iPad */ +"credential.provider.device.type.default" = "dispositivo"; + +/* Device type is iPhone */ +"credential.provider.device.type.iphone" = "iPhone"; + +/* Device type is iPad */ +"credential.provider.device.type.pad" = "iPad"; + +/* Cancel button for auth when opening login list */ +"credential.provider.list.auth.cancel" = "Annulla"; + +/* Reason for auth when opening screen with list of saved passwords */ +"credential.provider.list.auth.reason" = "Sblocca il dispositivo per accedere alle password"; + +/* Address label for login details on autofill */ +"credential.provider.list.details.address" = "URL del sito Web"; + +/* Menu item text for copying autofill login details */ +"credential.provider.list.details.copy-prompt" = "Copia %@"; + +/* Title for toast when copying address */ +"credential.provider.list.details.copy-toast.address-copied" = "Indirizzo copiato"; + +/* Title for toast when copying notes */ +"credential.provider.list.details.copy-toast.notes-copied" = "Appunti copiati"; + +/* Title for toast when copying password */ +"credential.provider.list.details.copy-toast.password-copied" = "Password copiata"; + +/* Title for toast when copying username */ +"credential.provider.list.details.copy-toast.username-copied" = "Nome utente copiato"; + +/* Accessibility title for a Hide Password button replacing displayed password with ***** */ +"credential.provider.list.details.hide-password" = "Nascondi password"; + +/* Message displaying when the login was last updated */ +"credential.provider.list.details.last-updated" = "Ultimo aggiornamento %@"; + +/* Login name label for login details on autofill */ +"credential.provider.list.details.login-name" = "Titolo"; + +/* Notes label for login details on autofill */ +"credential.provider.list.details.notes" = "Note"; + +/* Password label for login details on autofill */ +"credential.provider.list.details.password" = "Password"; + +/* Accessibility title for a Show Password button displaying actial password instead of ***** */ +"credential.provider.list.details.show-password" = "Mostra password"; + +/* Username label for login details on autofill */ +"credential.provider.list.details.username" = "Nome utente"; + +/* Footer label displayed below table section with option to enable autofill */ +"credential.provider.list.empty-view.footer" = "Le password sono archiviate in modo sicuro sul tuo dispositivo."; + +/* Title for view displayed when autofill has no items */ +"credential.provider.list.empty-view.title" = "Nessuna password ancora salvata"; + +/* Prompt above the title for screen listing autofill logins, example: Choose a password to use for "website.com" */ +"credential.provider.list.prompt" = "Scegli una password da usare per \"%@\""; + +/* Placeholder for search field on autofill login listing */ +"credential.provider.list.search-placeholder" = "Cerca password"; + +/* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ +"credential.provider.list.search.no-results.subtitle" = "per \"%@\""; + +/* Title displayed when there are no results on Autofill search */ +"credential.provider.list.search.no-results.title" = "Nessun risultato"; + +/* Section title for group of suggested saved logins */ +"credential.provider.list.suggested" = "Suggerimenti"; + +/* Title for screen listing autofill logins */ +"credential.provider.list.title" = "Password"; + +/* Message for alert when device authentication is not set, where %@ is iPhone|iPad|device */ +"credential.provider.no-device-auth-set.message" = "Imposta un codice di accesso su %@ per inserire automaticamente le tue password DuckDuckGo."; + +/* Title for alert when device authentication is not set */ +"credential.provider.no-device-auth-set.title" = "È necessario il codice di accesso del dispositivo"; + diff --git a/AutofillCredentialProvider/lt.lproj/InfoPlist.strings b/AutofillCredentialProvider/lt.lproj/InfoPlist.strings new file mode 100644 index 0000000000..9809aa19f5 --- /dev/null +++ b/AutofillCredentialProvider/lt.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "AutofillCredentialProvider"; + +/* Bundle name */ +"CFBundleName" = "AutofillCredentialProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 DuckDuckGo. All rights reserved."; + diff --git a/AutofillCredentialProvider/lt.lproj/Localizable.strings b/AutofillCredentialProvider/lt.lproj/Localizable.strings new file mode 100644 index 0000000000..9867833bfd --- /dev/null +++ b/AutofillCredentialProvider/lt.lproj/Localizable.strings @@ -0,0 +1,96 @@ +/* Close button title */ +"action.button.close" = "Uždaryti"; + +/* Done button title */ +"action.button.done" = "Atlikta"; + +/* Title of button to launch the DuckDuckGo app */ +"credential.provider.activated.button" = "Atidaryti „DuckDuckGo“"; + +/* The title of the screen confirming DuckDuckGo can now be used for autofilling passwords */ +"credential.provider.activated.title" = "Automatinis slaptažodžių pildymas įjungtas!"; + +/* Default string used if users device is not iPhone or iPad */ +"credential.provider.device.type.default" = "įrenginys"; + +/* Device type is iPhone */ +"credential.provider.device.type.iphone" = "iPhone"; + +/* Device type is iPad */ +"credential.provider.device.type.pad" = "iPad"; + +/* Cancel button for auth when opening login list */ +"credential.provider.list.auth.cancel" = "Atšaukti"; + +/* Reason for auth when opening screen with list of saved passwords */ +"credential.provider.list.auth.reason" = "Atrakinkite įrenginį, kad galėtumėte pasiekti slaptažodžius"; + +/* Address label for login details on autofill */ +"credential.provider.list.details.address" = "Svetainės URL"; + +/* Menu item text for copying autofill login details */ +"credential.provider.list.details.copy-prompt" = "Kopijuoti %@"; + +/* Title for toast when copying address */ +"credential.provider.list.details.copy-toast.address-copied" = "Adresas nukopijuotas"; + +/* Title for toast when copying notes */ +"credential.provider.list.details.copy-toast.notes-copied" = "Pastabos nukopijuotos"; + +/* Title for toast when copying password */ +"credential.provider.list.details.copy-toast.password-copied" = "Slaptažodis nukopijuotas"; + +/* Title for toast when copying username */ +"credential.provider.list.details.copy-toast.username-copied" = "Naudotojo vardas nukopijuotas"; + +/* Accessibility title for a Hide Password button replacing displayed password with ***** */ +"credential.provider.list.details.hide-password" = "Paslėpti slaptažodį"; + +/* Message displaying when the login was last updated */ +"credential.provider.list.details.last-updated" = "Paskutinį kartą atnaujinta %@"; + +/* Login name label for login details on autofill */ +"credential.provider.list.details.login-name" = "Pavadinimas"; + +/* Notes label for login details on autofill */ +"credential.provider.list.details.notes" = "Pastabos"; + +/* Password label for login details on autofill */ +"credential.provider.list.details.password" = "Slaptažodis"; + +/* Accessibility title for a Show Password button displaying actial password instead of ***** */ +"credential.provider.list.details.show-password" = "Rodyti slaptažodį"; + +/* Username label for login details on autofill */ +"credential.provider.list.details.username" = "Naudotojo vardas"; + +/* Footer label displayed below table section with option to enable autofill */ +"credential.provider.list.empty-view.footer" = "Slaptažodžiai saugiai saugomi jūsų įrenginyje."; + +/* Title for view displayed when autofill has no items */ +"credential.provider.list.empty-view.title" = "Dar nėra išsaugotų slaptažodžių"; + +/* Prompt above the title for screen listing autofill logins, example: Choose a password to use for "website.com" */ +"credential.provider.list.prompt" = "Pasirinkite slaptažodį, kurį naudosite „%@“"; + +/* Placeholder for search field on autofill login listing */ +"credential.provider.list.search-placeholder" = "Ieškoti slaptažodžių"; + +/* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ +"credential.provider.list.search.no-results.subtitle" = "„%@“"; + +/* Title displayed when there are no results on Autofill search */ +"credential.provider.list.search.no-results.title" = "Rezultatų nerasta"; + +/* Section title for group of suggested saved logins */ +"credential.provider.list.suggested" = "Siūloma"; + +/* Title for screen listing autofill logins */ +"credential.provider.list.title" = "Slaptažodžiai"; + +/* Message for alert when device authentication is not set, where %@ is iPhone|iPad|device */ +"credential.provider.no-device-auth-set.message" = "Nustatykite %@ prieigos kodą, kad automatiškai užpildytumėte „DuckDuckGo“ slaptažodžius."; + +/* Title for alert when device authentication is not set */ +"credential.provider.no-device-auth-set.title" = "Reikalingas įrenginio kodas"; + diff --git a/AutofillCredentialProvider/lv.lproj/InfoPlist.strings b/AutofillCredentialProvider/lv.lproj/InfoPlist.strings new file mode 100644 index 0000000000..9809aa19f5 --- /dev/null +++ b/AutofillCredentialProvider/lv.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "AutofillCredentialProvider"; + +/* Bundle name */ +"CFBundleName" = "AutofillCredentialProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 DuckDuckGo. All rights reserved."; + diff --git a/AutofillCredentialProvider/lv.lproj/Localizable.strings b/AutofillCredentialProvider/lv.lproj/Localizable.strings new file mode 100644 index 0000000000..5c9150e72f --- /dev/null +++ b/AutofillCredentialProvider/lv.lproj/Localizable.strings @@ -0,0 +1,96 @@ +/* Close button title */ +"action.button.close" = "Aizvērt"; + +/* Done button title */ +"action.button.done" = "Gatavs"; + +/* Title of button to launch the DuckDuckGo app */ +"credential.provider.activated.button" = "Atvērt DuckDuckGo"; + +/* The title of the screen confirming DuckDuckGo can now be used for autofilling passwords */ +"credential.provider.activated.title" = "Paroļu automātiskā aizpildīšana ir aktivizēta!"; + +/* Default string used if users device is not iPhone or iPad */ +"credential.provider.device.type.default" = "ierīcē"; + +/* Device type is iPhone */ +"credential.provider.device.type.iphone" = "iPhone"; + +/* Device type is iPad */ +"credential.provider.device.type.pad" = "iPad"; + +/* Cancel button for auth when opening login list */ +"credential.provider.list.auth.cancel" = "Atcelt"; + +/* Reason for auth when opening screen with list of saved passwords */ +"credential.provider.list.auth.reason" = "Atbloķē ierīci, lai piekļūtu parolēm"; + +/* Address label for login details on autofill */ +"credential.provider.list.details.address" = "Tīmekļa vietnes URL"; + +/* Menu item text for copying autofill login details */ +"credential.provider.list.details.copy-prompt" = "Kopēt %@"; + +/* Title for toast when copying address */ +"credential.provider.list.details.copy-toast.address-copied" = "Adrese nokopēta"; + +/* Title for toast when copying notes */ +"credential.provider.list.details.copy-toast.notes-copied" = "Piezīmes nokopētas"; + +/* Title for toast when copying password */ +"credential.provider.list.details.copy-toast.password-copied" = "Parole nokopēta"; + +/* Title for toast when copying username */ +"credential.provider.list.details.copy-toast.username-copied" = "Lietotājvārds nokopēts"; + +/* Accessibility title for a Hide Password button replacing displayed password with ***** */ +"credential.provider.list.details.hide-password" = "Paslēpt paroli"; + +/* Message displaying when the login was last updated */ +"credential.provider.list.details.last-updated" = "Pēdējoreiz atjaunināts %@"; + +/* Login name label for login details on autofill */ +"credential.provider.list.details.login-name" = "Nosaukums"; + +/* Notes label for login details on autofill */ +"credential.provider.list.details.notes" = "Piezīmes"; + +/* Password label for login details on autofill */ +"credential.provider.list.details.password" = "Parole"; + +/* Accessibility title for a Show Password button displaying actial password instead of ***** */ +"credential.provider.list.details.show-password" = "Rādīt paroli"; + +/* Username label for login details on autofill */ +"credential.provider.list.details.username" = "Lietotājvārds"; + +/* Footer label displayed below table section with option to enable autofill */ +"credential.provider.list.empty-view.footer" = "Paroles tiek droši glabātas tavā ierīcē."; + +/* Title for view displayed when autofill has no items */ +"credential.provider.list.empty-view.title" = "Vēl nav saglabāta neviena parole"; + +/* Prompt above the title for screen listing autofill logins, example: Choose a password to use for "website.com" */ +"credential.provider.list.prompt" = "Izvēlies paroli, ko izmantot \"%@\""; + +/* Placeholder for search field on autofill login listing */ +"credential.provider.list.search-placeholder" = "Meklēt paroles"; + +/* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ +"credential.provider.list.search.no-results.subtitle" = "meklējumam \"%@\""; + +/* Title displayed when there are no results on Autofill search */ +"credential.provider.list.search.no-results.title" = "Nav rezultātu"; + +/* Section title for group of suggested saved logins */ +"credential.provider.list.suggested" = "Ieteikts"; + +/* Title for screen listing autofill logins */ +"credential.provider.list.title" = "Paroles"; + +/* Message for alert when device authentication is not set, where %@ is iPhone|iPad|device */ +"credential.provider.no-device-auth-set.message" = "Iestati piekļuves kodu %@, lai automātiski aizpildītu savas DuckDuckGo paroles."; + +/* Title for alert when device authentication is not set */ +"credential.provider.no-device-auth-set.title" = "Nepieciešams ierīces piekļuves kods"; + diff --git a/AutofillCredentialProvider/nb.lproj/InfoPlist.strings b/AutofillCredentialProvider/nb.lproj/InfoPlist.strings new file mode 100644 index 0000000000..9809aa19f5 --- /dev/null +++ b/AutofillCredentialProvider/nb.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "AutofillCredentialProvider"; + +/* Bundle name */ +"CFBundleName" = "AutofillCredentialProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 DuckDuckGo. All rights reserved."; + diff --git a/AutofillCredentialProvider/nb.lproj/Localizable.strings b/AutofillCredentialProvider/nb.lproj/Localizable.strings new file mode 100644 index 0000000000..858f84a0d9 --- /dev/null +++ b/AutofillCredentialProvider/nb.lproj/Localizable.strings @@ -0,0 +1,96 @@ +/* Close button title */ +"action.button.close" = "Lukk"; + +/* Done button title */ +"action.button.done" = "Ferdig"; + +/* Title of button to launch the DuckDuckGo app */ +"credential.provider.activated.button" = "Åpne DuckDuckGo"; + +/* The title of the screen confirming DuckDuckGo can now be used for autofilling passwords */ +"credential.provider.activated.title" = "Automatisk fylling av passord er aktivert!"; + +/* Default string used if users device is not iPhone or iPad */ +"credential.provider.device.type.default" = "enhet"; + +/* Device type is iPhone */ +"credential.provider.device.type.iphone" = "iPhone"; + +/* Device type is iPad */ +"credential.provider.device.type.pad" = "iPad"; + +/* Cancel button for auth when opening login list */ +"credential.provider.list.auth.cancel" = "Avbryt"; + +/* Reason for auth when opening screen with list of saved passwords */ +"credential.provider.list.auth.reason" = "Lås opp enheten for å få tilgang til passord"; + +/* Address label for login details on autofill */ +"credential.provider.list.details.address" = "URL-adresse til nettsted"; + +/* Menu item text for copying autofill login details */ +"credential.provider.list.details.copy-prompt" = "Kopier %@"; + +/* Title for toast when copying address */ +"credential.provider.list.details.copy-toast.address-copied" = "Adressen er kopiert"; + +/* Title for toast when copying notes */ +"credential.provider.list.details.copy-toast.notes-copied" = "Notatene er kopiert"; + +/* Title for toast when copying password */ +"credential.provider.list.details.copy-toast.password-copied" = "Passordet er kopiert"; + +/* Title for toast when copying username */ +"credential.provider.list.details.copy-toast.username-copied" = "Brukernavnet er kopiert"; + +/* Accessibility title for a Hide Password button replacing displayed password with ***** */ +"credential.provider.list.details.hide-password" = "Skjul passord"; + +/* Message displaying when the login was last updated */ +"credential.provider.list.details.last-updated" = "Sist oppdatert %@"; + +/* Login name label for login details on autofill */ +"credential.provider.list.details.login-name" = "Tittel"; + +/* Notes label for login details on autofill */ +"credential.provider.list.details.notes" = "Notater"; + +/* Password label for login details on autofill */ +"credential.provider.list.details.password" = "Passord"; + +/* Accessibility title for a Show Password button displaying actial password instead of ***** */ +"credential.provider.list.details.show-password" = "Vis passord"; + +/* Username label for login details on autofill */ +"credential.provider.list.details.username" = "Brukernavn"; + +/* Footer label displayed below table section with option to enable autofill */ +"credential.provider.list.empty-view.footer" = "Passord lagres på enheten din på en sikker måte."; + +/* Title for view displayed when autofill has no items */ +"credential.provider.list.empty-view.title" = "Ingen passord er lagret ennå"; + +/* Prompt above the title for screen listing autofill logins, example: Choose a password to use for "website.com" */ +"credential.provider.list.prompt" = "Velg et passord du vil bruke til «%@»"; + +/* Placeholder for search field on autofill login listing */ +"credential.provider.list.search-placeholder" = "Søk i passord"; + +/* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ +"credential.provider.list.search.no-results.subtitle" = "for «%@»"; + +/* Title displayed when there are no results on Autofill search */ +"credential.provider.list.search.no-results.title" = "Ingen resultater"; + +/* Section title for group of suggested saved logins */ +"credential.provider.list.suggested" = "Forslag"; + +/* Title for screen listing autofill logins */ +"credential.provider.list.title" = "Passord"; + +/* Message for alert when device authentication is not set, where %@ is iPhone|iPad|device */ +"credential.provider.no-device-auth-set.message" = "Angi en kode på %@ for å fylle inn DuckDuckGo-passordene dine automatisk."; + +/* Title for alert when device authentication is not set */ +"credential.provider.no-device-auth-set.title" = "Enhetens kode kreves"; + diff --git a/AutofillCredentialProvider/nl.lproj/InfoPlist.strings b/AutofillCredentialProvider/nl.lproj/InfoPlist.strings new file mode 100644 index 0000000000..9809aa19f5 --- /dev/null +++ b/AutofillCredentialProvider/nl.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "AutofillCredentialProvider"; + +/* Bundle name */ +"CFBundleName" = "AutofillCredentialProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 DuckDuckGo. All rights reserved."; + diff --git a/AutofillCredentialProvider/nl.lproj/Localizable.strings b/AutofillCredentialProvider/nl.lproj/Localizable.strings new file mode 100644 index 0000000000..5737a0c75a --- /dev/null +++ b/AutofillCredentialProvider/nl.lproj/Localizable.strings @@ -0,0 +1,96 @@ +/* Close button title */ +"action.button.close" = "Sluiten"; + +/* Done button title */ +"action.button.done" = "Klaar"; + +/* Title of button to launch the DuckDuckGo app */ +"credential.provider.activated.button" = "DuckDuckGo openen"; + +/* The title of the screen confirming DuckDuckGo can now be used for autofilling passwords */ +"credential.provider.activated.title" = "Automatisch invullen van wachtwoorden geactiveerd!"; + +/* Default string used if users device is not iPhone or iPad */ +"credential.provider.device.type.default" = "apparaat"; + +/* Device type is iPhone */ +"credential.provider.device.type.iphone" = "iPhone"; + +/* Device type is iPad */ +"credential.provider.device.type.pad" = "iPad"; + +/* Cancel button for auth when opening login list */ +"credential.provider.list.auth.cancel" = "Annuleren"; + +/* Reason for auth when opening screen with list of saved passwords */ +"credential.provider.list.auth.reason" = "Ontgrendel het apparaat om toegang te krijgen tot wachtwoorden"; + +/* Address label for login details on autofill */ +"credential.provider.list.details.address" = "URL van de website"; + +/* Menu item text for copying autofill login details */ +"credential.provider.list.details.copy-prompt" = "%@ kopiëren"; + +/* Title for toast when copying address */ +"credential.provider.list.details.copy-toast.address-copied" = "Adres gekopieerd"; + +/* Title for toast when copying notes */ +"credential.provider.list.details.copy-toast.notes-copied" = "Opmerkingen gekopieerd"; + +/* Title for toast when copying password */ +"credential.provider.list.details.copy-toast.password-copied" = "Wachtwoord gekopieerd"; + +/* Title for toast when copying username */ +"credential.provider.list.details.copy-toast.username-copied" = "Gebruikersnaam gekopieerd"; + +/* Accessibility title for a Hide Password button replacing displayed password with ***** */ +"credential.provider.list.details.hide-password" = "Wachtwoord verbergen"; + +/* Message displaying when the login was last updated */ +"credential.provider.list.details.last-updated" = "Laatst bijgewerkt op %@"; + +/* Login name label for login details on autofill */ +"credential.provider.list.details.login-name" = "Titel"; + +/* Notes label for login details on autofill */ +"credential.provider.list.details.notes" = "Opmerkingen"; + +/* Password label for login details on autofill */ +"credential.provider.list.details.password" = "Wachtwoord"; + +/* Accessibility title for a Show Password button displaying actial password instead of ***** */ +"credential.provider.list.details.show-password" = "Wachtwoord weergeven"; + +/* Username label for login details on autofill */ +"credential.provider.list.details.username" = "Gebruikersnaam"; + +/* Footer label displayed below table section with option to enable autofill */ +"credential.provider.list.empty-view.footer" = "Wachtwoorden worden veilig opgeslagen op je apparaat."; + +/* Title for view displayed when autofill has no items */ +"credential.provider.list.empty-view.title" = "Nog geen wachtwoorden opgeslagen"; + +/* Prompt above the title for screen listing autofill logins, example: Choose a password to use for "website.com" */ +"credential.provider.list.prompt" = "Kies een wachtwoord voor '%@'"; + +/* Placeholder for search field on autofill login listing */ +"credential.provider.list.search-placeholder" = "Wachtwoorden zoeken"; + +/* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ +"credential.provider.list.search.no-results.subtitle" = "voor '%@'"; + +/* Title displayed when there are no results on Autofill search */ +"credential.provider.list.search.no-results.title" = "Geen resultaten"; + +/* Section title for group of suggested saved logins */ +"credential.provider.list.suggested" = "Aanbevolen"; + +/* Title for screen listing autofill logins */ +"credential.provider.list.title" = "Wachtwoorden"; + +/* Message for alert when device authentication is not set, where %@ is iPhone|iPad|device */ +"credential.provider.no-device-auth-set.message" = "Stel een toegangscode in op %@ om je DuckDuckGo-wachtwoorden automatisch in te vullen."; + +/* Title for alert when device authentication is not set */ +"credential.provider.no-device-auth-set.title" = "Toegangscode voor apparaat vereist"; + diff --git a/AutofillCredentialProvider/pl.lproj/InfoPlist.strings b/AutofillCredentialProvider/pl.lproj/InfoPlist.strings new file mode 100644 index 0000000000..9809aa19f5 --- /dev/null +++ b/AutofillCredentialProvider/pl.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "AutofillCredentialProvider"; + +/* Bundle name */ +"CFBundleName" = "AutofillCredentialProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 DuckDuckGo. All rights reserved."; + diff --git a/AutofillCredentialProvider/pl.lproj/Localizable.strings b/AutofillCredentialProvider/pl.lproj/Localizable.strings new file mode 100644 index 0000000000..b2198855c4 --- /dev/null +++ b/AutofillCredentialProvider/pl.lproj/Localizable.strings @@ -0,0 +1,96 @@ +/* Close button title */ +"action.button.close" = "Zamknij"; + +/* Done button title */ +"action.button.done" = "Gotowe"; + +/* Title of button to launch the DuckDuckGo app */ +"credential.provider.activated.button" = "Otwórz DuckDuckGo"; + +/* The title of the screen confirming DuckDuckGo can now be used for autofilling passwords */ +"credential.provider.activated.title" = "Automatyczne uzupełnianie haseł zostało aktywowane!"; + +/* Default string used if users device is not iPhone or iPad */ +"credential.provider.device.type.default" = "tym urządzeniu"; + +/* Device type is iPhone */ +"credential.provider.device.type.iphone" = "tym telefonie iPhone"; + +/* Device type is iPad */ +"credential.provider.device.type.pad" = "tym iPadzie"; + +/* Cancel button for auth when opening login list */ +"credential.provider.list.auth.cancel" = "Anuluj"; + +/* Reason for auth when opening screen with list of saved passwords */ +"credential.provider.list.auth.reason" = "Odblokuj urządzenie, aby uzyskać dostęp do haseł"; + +/* Address label for login details on autofill */ +"credential.provider.list.details.address" = "Adres URL witryny"; + +/* Menu item text for copying autofill login details */ +"credential.provider.list.details.copy-prompt" = "Kopiuj %@"; + +/* Title for toast when copying address */ +"credential.provider.list.details.copy-toast.address-copied" = "Skopiowano adres"; + +/* Title for toast when copying notes */ +"credential.provider.list.details.copy-toast.notes-copied" = "Skopiowano notatki"; + +/* Title for toast when copying password */ +"credential.provider.list.details.copy-toast.password-copied" = "Skopiowano hasło"; + +/* Title for toast when copying username */ +"credential.provider.list.details.copy-toast.username-copied" = "Skopiowano nazwę użytkownika"; + +/* Accessibility title for a Hide Password button replacing displayed password with ***** */ +"credential.provider.list.details.hide-password" = "Ukryj hasło"; + +/* Message displaying when the login was last updated */ +"credential.provider.list.details.last-updated" = "Ostatnia aktualizacja %@"; + +/* Login name label for login details on autofill */ +"credential.provider.list.details.login-name" = "Tytuł"; + +/* Notes label for login details on autofill */ +"credential.provider.list.details.notes" = "Uwagi"; + +/* Password label for login details on autofill */ +"credential.provider.list.details.password" = "Hasło"; + +/* Accessibility title for a Show Password button displaying actial password instead of ***** */ +"credential.provider.list.details.show-password" = "Pokaż hasło"; + +/* Username label for login details on autofill */ +"credential.provider.list.details.username" = "Nazwa użytkownika"; + +/* Footer label displayed below table section with option to enable autofill */ +"credential.provider.list.empty-view.footer" = "Hasła są bezpiecznie przechowywane na Twoim urządzeniu."; + +/* Title for view displayed when autofill has no items */ +"credential.provider.list.empty-view.title" = "Nie zapisano jeszcze żadnych haseł"; + +/* Prompt above the title for screen listing autofill logins, example: Choose a password to use for "website.com" */ +"credential.provider.list.prompt" = "Wybierz hasło do wykorzystania z „%@”"; + +/* Placeholder for search field on autofill login listing */ +"credential.provider.list.search-placeholder" = "Wyszukaj hasła"; + +/* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ +"credential.provider.list.search.no-results.subtitle" = "dla frazy: %@"; + +/* Title displayed when there are no results on Autofill search */ +"credential.provider.list.search.no-results.title" = "Brak wyników"; + +/* Section title for group of suggested saved logins */ +"credential.provider.list.suggested" = "Sugerowane"; + +/* Title for screen listing autofill logins */ +"credential.provider.list.title" = "Hasła"; + +/* Message for alert when device authentication is not set, where %@ is iPhone|iPad|device */ +"credential.provider.no-device-auth-set.message" = "Ustaw kod dostępu na urządzeniu %@, aby automatycznie uzupełniać hasła DuckDuckGo."; + +/* Title for alert when device authentication is not set */ +"credential.provider.no-device-auth-set.title" = "Wymagany kod dostępu urządzenia"; + diff --git a/AutofillCredentialProvider/pt.lproj/InfoPlist.strings b/AutofillCredentialProvider/pt.lproj/InfoPlist.strings new file mode 100644 index 0000000000..9809aa19f5 --- /dev/null +++ b/AutofillCredentialProvider/pt.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "AutofillCredentialProvider"; + +/* Bundle name */ +"CFBundleName" = "AutofillCredentialProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 DuckDuckGo. All rights reserved."; + diff --git a/AutofillCredentialProvider/pt.lproj/Localizable.strings b/AutofillCredentialProvider/pt.lproj/Localizable.strings new file mode 100644 index 0000000000..6d2929f0ee --- /dev/null +++ b/AutofillCredentialProvider/pt.lproj/Localizable.strings @@ -0,0 +1,96 @@ +/* Close button title */ +"action.button.close" = "Fechar"; + +/* Done button title */ +"action.button.done" = "Feito"; + +/* Title of button to launch the DuckDuckGo app */ +"credential.provider.activated.button" = "Abrir DuckDuckGo"; + +/* The title of the screen confirming DuckDuckGo can now be used for autofilling passwords */ +"credential.provider.activated.title" = "Preenchimento automático de palavras-passe ativado!"; + +/* Default string used if users device is not iPhone or iPad */ +"credential.provider.device.type.default" = "dispositivo"; + +/* Device type is iPhone */ +"credential.provider.device.type.iphone" = "iPhone"; + +/* Device type is iPad */ +"credential.provider.device.type.pad" = "iPad"; + +/* Cancel button for auth when opening login list */ +"credential.provider.list.auth.cancel" = "Cancelar"; + +/* Reason for auth when opening screen with list of saved passwords */ +"credential.provider.list.auth.reason" = "Desbloquear dispositivo para aceder às palavras-passe"; + +/* Address label for login details on autofill */ +"credential.provider.list.details.address" = "URL do site"; + +/* Menu item text for copying autofill login details */ +"credential.provider.list.details.copy-prompt" = "Copiar %@"; + +/* Title for toast when copying address */ +"credential.provider.list.details.copy-toast.address-copied" = "Endereço copiado"; + +/* Title for toast when copying notes */ +"credential.provider.list.details.copy-toast.notes-copied" = "Notas copiadas"; + +/* Title for toast when copying password */ +"credential.provider.list.details.copy-toast.password-copied" = "Palavra-passe copiada"; + +/* Title for toast when copying username */ +"credential.provider.list.details.copy-toast.username-copied" = "Nome de utilizador copiado"; + +/* Accessibility title for a Hide Password button replacing displayed password with ***** */ +"credential.provider.list.details.hide-password" = "Ocultar palavra-passe"; + +/* Message displaying when the login was last updated */ +"credential.provider.list.details.last-updated" = "Última atualização em %@"; + +/* Login name label for login details on autofill */ +"credential.provider.list.details.login-name" = "Título"; + +/* Notes label for login details on autofill */ +"credential.provider.list.details.notes" = "Notas"; + +/* Password label for login details on autofill */ +"credential.provider.list.details.password" = "Palavra-passe"; + +/* Accessibility title for a Show Password button displaying actial password instead of ***** */ +"credential.provider.list.details.show-password" = "Mostrar palavra-passe"; + +/* Username label for login details on autofill */ +"credential.provider.list.details.username" = "Nome de utilizador"; + +/* Footer label displayed below table section with option to enable autofill */ +"credential.provider.list.empty-view.footer" = "As palavras-passe são armazenadas com segurança no teu dispositivo."; + +/* Title for view displayed when autofill has no items */ +"credential.provider.list.empty-view.title" = "Ainda não há palavras-passe guardadas"; + +/* Prompt above the title for screen listing autofill logins, example: Choose a password to use for "website.com" */ +"credential.provider.list.prompt" = "Escolhe uma palavra-passe para usar em \"%@\""; + +/* Placeholder for search field on autofill login listing */ +"credential.provider.list.search-placeholder" = "Pesquisar palavras-passe"; + +/* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ +"credential.provider.list.search.no-results.subtitle" = "para \"%@\""; + +/* Title displayed when there are no results on Autofill search */ +"credential.provider.list.search.no-results.title" = "Sem resultados"; + +/* Section title for group of suggested saved logins */ +"credential.provider.list.suggested" = "Sugerido"; + +/* Title for screen listing autofill logins */ +"credential.provider.list.title" = "Palavras-passe"; + +/* Message for alert when device authentication is not set, where %@ is iPhone|iPad|device */ +"credential.provider.no-device-auth-set.message" = "Define um código de acesso em %@ para preencher automaticamente as tuas palavras-passe DuckDuckGo."; + +/* Title for alert when device authentication is not set */ +"credential.provider.no-device-auth-set.title" = "Código de acesso do dispositivo necessário"; + diff --git a/AutofillCredentialProvider/ro.lproj/InfoPlist.strings b/AutofillCredentialProvider/ro.lproj/InfoPlist.strings new file mode 100644 index 0000000000..9809aa19f5 --- /dev/null +++ b/AutofillCredentialProvider/ro.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "AutofillCredentialProvider"; + +/* Bundle name */ +"CFBundleName" = "AutofillCredentialProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 DuckDuckGo. All rights reserved."; + diff --git a/AutofillCredentialProvider/ro.lproj/Localizable.strings b/AutofillCredentialProvider/ro.lproj/Localizable.strings new file mode 100644 index 0000000000..e90eb1030d --- /dev/null +++ b/AutofillCredentialProvider/ro.lproj/Localizable.strings @@ -0,0 +1,96 @@ +/* Close button title */ +"action.button.close" = "Închidere"; + +/* Done button title */ +"action.button.done" = "Terminat"; + +/* Title of button to launch the DuckDuckGo app */ +"credential.provider.activated.button" = "Deschide DuckDuckGo"; + +/* The title of the screen confirming DuckDuckGo can now be used for autofilling passwords */ +"credential.provider.activated.title" = "Completarea automată a parolelor a fost activată!"; + +/* Default string used if users device is not iPhone or iPad */ +"credential.provider.device.type.default" = "dispozitiv"; + +/* Device type is iPhone */ +"credential.provider.device.type.iphone" = "iPhone"; + +/* Device type is iPad */ +"credential.provider.device.type.pad" = "iPad"; + +/* Cancel button for auth when opening login list */ +"credential.provider.list.auth.cancel" = "Renunță"; + +/* Reason for auth when opening screen with list of saved passwords */ +"credential.provider.list.auth.reason" = "Deblochează dispozitivul pentru a accesa parolele"; + +/* Address label for login details on autofill */ +"credential.provider.list.details.address" = "URL-ul site-ului"; + +/* Menu item text for copying autofill login details */ +"credential.provider.list.details.copy-prompt" = "Copiază %@"; + +/* Title for toast when copying address */ +"credential.provider.list.details.copy-toast.address-copied" = "Adresă copiată"; + +/* Title for toast when copying notes */ +"credential.provider.list.details.copy-toast.notes-copied" = "Note copiate"; + +/* Title for toast when copying password */ +"credential.provider.list.details.copy-toast.password-copied" = "Parolă copiată"; + +/* Title for toast when copying username */ +"credential.provider.list.details.copy-toast.username-copied" = "Numele de utilizator a fost copiat"; + +/* Accessibility title for a Hide Password button replacing displayed password with ***** */ +"credential.provider.list.details.hide-password" = "Ascunde parola"; + +/* Message displaying when the login was last updated */ +"credential.provider.list.details.last-updated" = "Ultima actualizare la %@"; + +/* Login name label for login details on autofill */ +"credential.provider.list.details.login-name" = "Titlu"; + +/* Notes label for login details on autofill */ +"credential.provider.list.details.notes" = "Note"; + +/* Password label for login details on autofill */ +"credential.provider.list.details.password" = "Parolă"; + +/* Accessibility title for a Show Password button displaying actial password instead of ***** */ +"credential.provider.list.details.show-password" = "Arată parola"; + +/* Username label for login details on autofill */ +"credential.provider.list.details.username" = "Nume utilizator"; + +/* Footer label displayed below table section with option to enable autofill */ +"credential.provider.list.empty-view.footer" = "Parolele sunt stocate în siguranță pe dispozitivul tău."; + +/* Title for view displayed when autofill has no items */ +"credential.provider.list.empty-view.title" = "Nici o parolă nu a fost salvată încă"; + +/* Prompt above the title for screen listing autofill logins, example: Choose a password to use for "website.com" */ +"credential.provider.list.prompt" = "Alege o parolă pe care să o utilizezi pentru „%@”"; + +/* Placeholder for search field on autofill login listing */ +"credential.provider.list.search-placeholder" = "Caută parole"; + +/* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ +"credential.provider.list.search.no-results.subtitle" = "pentru „%@”"; + +/* Title displayed when there are no results on Autofill search */ +"credential.provider.list.search.no-results.title" = "Niciun rezultat"; + +/* Section title for group of suggested saved logins */ +"credential.provider.list.suggested" = "Sugerat"; + +/* Title for screen listing autofill logins */ +"credential.provider.list.title" = "Parole"; + +/* Message for alert when device authentication is not set, where %@ is iPhone|iPad|device */ +"credential.provider.no-device-auth-set.message" = "Setează o parolă pe %@ pentru a-ți completa automat parolele DuckDuckGo."; + +/* Title for alert when device authentication is not set */ +"credential.provider.no-device-auth-set.title" = "Codul de acces al dispozitivului este necesar"; + diff --git a/AutofillCredentialProvider/ru.lproj/InfoPlist.strings b/AutofillCredentialProvider/ru.lproj/InfoPlist.strings new file mode 100644 index 0000000000..9809aa19f5 --- /dev/null +++ b/AutofillCredentialProvider/ru.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "AutofillCredentialProvider"; + +/* Bundle name */ +"CFBundleName" = "AutofillCredentialProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 DuckDuckGo. All rights reserved."; + diff --git a/AutofillCredentialProvider/ru.lproj/Localizable.strings b/AutofillCredentialProvider/ru.lproj/Localizable.strings new file mode 100644 index 0000000000..823459918c --- /dev/null +++ b/AutofillCredentialProvider/ru.lproj/Localizable.strings @@ -0,0 +1,96 @@ +/* Close button title */ +"action.button.close" = "Закрыть"; + +/* Done button title */ +"action.button.done" = "Готово"; + +/* Title of button to launch the DuckDuckGo app */ +"credential.provider.activated.button" = "Открыть DuckDuckGo"; + +/* The title of the screen confirming DuckDuckGo can now be used for autofilling passwords */ +"credential.provider.activated.title" = "Автозаполнение паролей включено!"; + +/* Default string used if users device is not iPhone or iPad */ +"credential.provider.device.type.default" = "устройстве"; + +/* Device type is iPhone */ +"credential.provider.device.type.iphone" = "iPhone"; + +/* Device type is iPad */ +"credential.provider.device.type.pad" = "iPad"; + +/* Cancel button for auth when opening login list */ +"credential.provider.list.auth.cancel" = "Отменить"; + +/* Reason for auth when opening screen with list of saved passwords */ +"credential.provider.list.auth.reason" = "Разблокируйте устройство, чтобы получить доступ к паролям"; + +/* Address label for login details on autofill */ +"credential.provider.list.details.address" = "Адрес сайта"; + +/* Menu item text for copying autofill login details */ +"credential.provider.list.details.copy-prompt" = "Копировать %@"; + +/* Title for toast when copying address */ +"credential.provider.list.details.copy-toast.address-copied" = "Адрес скопирован"; + +/* Title for toast when copying notes */ +"credential.provider.list.details.copy-toast.notes-copied" = "Примечания скопированы"; + +/* Title for toast when copying password */ +"credential.provider.list.details.copy-toast.password-copied" = "Пароль скопирован"; + +/* Title for toast when copying username */ +"credential.provider.list.details.copy-toast.username-copied" = "Имя пользователя скопировано!"; + +/* Accessibility title for a Hide Password button replacing displayed password with ***** */ +"credential.provider.list.details.hide-password" = "Скрыть пароль"; + +/* Message displaying when the login was last updated */ +"credential.provider.list.details.last-updated" = "Последнее обновление: %@"; + +/* Login name label for login details on autofill */ +"credential.provider.list.details.login-name" = "Название"; + +/* Notes label for login details on autofill */ +"credential.provider.list.details.notes" = "Примечания"; + +/* Password label for login details on autofill */ +"credential.provider.list.details.password" = "Пароль"; + +/* Accessibility title for a Show Password button displaying actial password instead of ***** */ +"credential.provider.list.details.show-password" = "Показать пароль"; + +/* Username label for login details on autofill */ +"credential.provider.list.details.username" = "Имя пользователя"; + +/* Footer label displayed below table section with option to enable autofill */ +"credential.provider.list.empty-view.footer" = "Пароли надежно защищены и хранятся на вашем устройстве."; + +/* Title for view displayed when autofill has no items */ +"credential.provider.list.empty-view.title" = "Сохраненных паролей пока нет"; + +/* Prompt above the title for screen listing autofill logins, example: Choose a password to use for "website.com" */ +"credential.provider.list.prompt" = "Выберите пароль для сайта %@"; + +/* Placeholder for search field on autofill login listing */ +"credential.provider.list.search-placeholder" = "Найти пароль"; + +/* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ +"credential.provider.list.search.no-results.subtitle" = "по запросу «%@»"; + +/* Title displayed when there are no results on Autofill search */ +"credential.provider.list.search.no-results.title" = "Нет результатов"; + +/* Section title for group of suggested saved logins */ +"credential.provider.list.suggested" = "Рекомендации"; + +/* Title for screen listing autofill logins */ +"credential.provider.list.title" = "Пароли"; + +/* Message for alert when device authentication is not set, where %@ is iPhone|iPad|device */ +"credential.provider.no-device-auth-set.message" = "Задайте код доступа, и %@ будет заполнять пароли из DuckDuckGo автоматически."; + +/* Title for alert when device authentication is not set */ +"credential.provider.no-device-auth-set.title" = "Требуется код доступа к устройству"; + diff --git a/AutofillCredentialProvider/sk.lproj/InfoPlist.strings b/AutofillCredentialProvider/sk.lproj/InfoPlist.strings new file mode 100644 index 0000000000..9809aa19f5 --- /dev/null +++ b/AutofillCredentialProvider/sk.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "AutofillCredentialProvider"; + +/* Bundle name */ +"CFBundleName" = "AutofillCredentialProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 DuckDuckGo. All rights reserved."; + diff --git a/AutofillCredentialProvider/sk.lproj/Localizable.strings b/AutofillCredentialProvider/sk.lproj/Localizable.strings new file mode 100644 index 0000000000..c704caedba --- /dev/null +++ b/AutofillCredentialProvider/sk.lproj/Localizable.strings @@ -0,0 +1,96 @@ +/* Close button title */ +"action.button.close" = "Zatvoriť"; + +/* Done button title */ +"action.button.done" = "Hotovo"; + +/* Title of button to launch the DuckDuckGo app */ +"credential.provider.activated.button" = "Otvoriť DuckDuckGo"; + +/* The title of the screen confirming DuckDuckGo can now be used for autofilling passwords */ +"credential.provider.activated.title" = "Automatické dopĺňanie hesiel je aktivované!"; + +/* Default string used if users device is not iPhone or iPad */ +"credential.provider.device.type.default" = "zariadenie"; + +/* Device type is iPhone */ +"credential.provider.device.type.iphone" = "iPhone"; + +/* Device type is iPad */ +"credential.provider.device.type.pad" = "iPad"; + +/* Cancel button for auth when opening login list */ +"credential.provider.list.auth.cancel" = "Zrušiť"; + +/* Reason for auth when opening screen with list of saved passwords */ +"credential.provider.list.auth.reason" = "Ak chcete získať prístup k heslám, odomknite zariadenie"; + +/* Address label for login details on autofill */ +"credential.provider.list.details.address" = "Adresa URL webových stránok"; + +/* Menu item text for copying autofill login details */ +"credential.provider.list.details.copy-prompt" = "Kopírovať %@"; + +/* Title for toast when copying address */ +"credential.provider.list.details.copy-toast.address-copied" = "Adresa bola skopírovaná"; + +/* Title for toast when copying notes */ +"credential.provider.list.details.copy-toast.notes-copied" = "Poznámky boli skopírované"; + +/* Title for toast when copying password */ +"credential.provider.list.details.copy-toast.password-copied" = "Heslo bolo skopírované"; + +/* Title for toast when copying username */ +"credential.provider.list.details.copy-toast.username-copied" = "Meno používateľa bolo skopírované"; + +/* Accessibility title for a Hide Password button replacing displayed password with ***** */ +"credential.provider.list.details.hide-password" = "Skryť heslo"; + +/* Message displaying when the login was last updated */ +"credential.provider.list.details.last-updated" = "Naposledy aktualizované %@"; + +/* Login name label for login details on autofill */ +"credential.provider.list.details.login-name" = "Názov"; + +/* Notes label for login details on autofill */ +"credential.provider.list.details.notes" = "Poznámky"; + +/* Password label for login details on autofill */ +"credential.provider.list.details.password" = "Heslo"; + +/* Accessibility title for a Show Password button displaying actial password instead of ***** */ +"credential.provider.list.details.show-password" = "Zobraziť heslo"; + +/* Username label for login details on autofill */ +"credential.provider.list.details.username" = "Používateľské meno"; + +/* Footer label displayed below table section with option to enable autofill */ +"credential.provider.list.empty-view.footer" = "Heslá sú bezpečne uložené vo vašom zariadení."; + +/* Title for view displayed when autofill has no items */ +"credential.provider.list.empty-view.title" = "Zatiaľ nie sú uložené žiadne heslá"; + +/* Prompt above the title for screen listing autofill logins, example: Choose a password to use for "website.com" */ +"credential.provider.list.prompt" = "Vyber si heslo, ktoré chceš použiť pre „%@“"; + +/* Placeholder for search field on autofill login listing */ +"credential.provider.list.search-placeholder" = "Vyhľadávanie hesiel"; + +/* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ +"credential.provider.list.search.no-results.subtitle" = "pre „%@”"; + +/* Title displayed when there are no results on Autofill search */ +"credential.provider.list.search.no-results.title" = "Žiadne výsledky"; + +/* Section title for group of suggested saved logins */ +"credential.provider.list.suggested" = "Navrhované"; + +/* Title for screen listing autofill logins */ +"credential.provider.list.title" = "Heslo"; + +/* Message for alert when device authentication is not set, where %@ is iPhone|iPad|device */ +"credential.provider.no-device-auth-set.message" = "Nastav prístupový kód na %@ na automatické vypĺňanie tvojich hesiel DuckDuckGo."; + +/* Title for alert when device authentication is not set */ +"credential.provider.no-device-auth-set.title" = "Vyžaduje sa prístupový kód zariadenia."; + diff --git a/AutofillCredentialProvider/sl.lproj/InfoPlist.strings b/AutofillCredentialProvider/sl.lproj/InfoPlist.strings new file mode 100644 index 0000000000..9809aa19f5 --- /dev/null +++ b/AutofillCredentialProvider/sl.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "AutofillCredentialProvider"; + +/* Bundle name */ +"CFBundleName" = "AutofillCredentialProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 DuckDuckGo. All rights reserved."; + diff --git a/AutofillCredentialProvider/sl.lproj/Localizable.strings b/AutofillCredentialProvider/sl.lproj/Localizable.strings new file mode 100644 index 0000000000..2ccec47f15 --- /dev/null +++ b/AutofillCredentialProvider/sl.lproj/Localizable.strings @@ -0,0 +1,96 @@ +/* Close button title */ +"action.button.close" = "Zapri"; + +/* Done button title */ +"action.button.done" = "Končano"; + +/* Title of button to launch the DuckDuckGo app */ +"credential.provider.activated.button" = "Odpri DuckDuckGo"; + +/* The title of the screen confirming DuckDuckGo can now be used for autofilling passwords */ +"credential.provider.activated.title" = "Samodejno izpolnjevanje gesel je aktivirano."; + +/* Default string used if users device is not iPhone or iPad */ +"credential.provider.device.type.default" = "naprava"; + +/* Device type is iPhone */ +"credential.provider.device.type.iphone" = "iPhone"; + +/* Device type is iPad */ +"credential.provider.device.type.pad" = "iPad"; + +/* Cancel button for auth when opening login list */ +"credential.provider.list.auth.cancel" = "Prekliči"; + +/* Reason for auth when opening screen with list of saved passwords */ +"credential.provider.list.auth.reason" = "Če želite dostopati do gesel, odklenite napravo"; + +/* Address label for login details on autofill */ +"credential.provider.list.details.address" = "URL spletnega mesta"; + +/* Menu item text for copying autofill login details */ +"credential.provider.list.details.copy-prompt" = "Kopiraj %@"; + +/* Title for toast when copying address */ +"credential.provider.list.details.copy-toast.address-copied" = "Naslov je bil kopiran"; + +/* Title for toast when copying notes */ +"credential.provider.list.details.copy-toast.notes-copied" = "Opombe so kopirane"; + +/* Title for toast when copying password */ +"credential.provider.list.details.copy-toast.password-copied" = "Geslo je bilo kopirano"; + +/* Title for toast when copying username */ +"credential.provider.list.details.copy-toast.username-copied" = "Uporabniško ime je bilo kopirano"; + +/* Accessibility title for a Hide Password button replacing displayed password with ***** */ +"credential.provider.list.details.hide-password" = "Skrij geslo"; + +/* Message displaying when the login was last updated */ +"credential.provider.list.details.last-updated" = "Zadnja posodobitev %@"; + +/* Login name label for login details on autofill */ +"credential.provider.list.details.login-name" = "Naslov"; + +/* Notes label for login details on autofill */ +"credential.provider.list.details.notes" = "Opombe"; + +/* Password label for login details on autofill */ +"credential.provider.list.details.password" = "Geslo"; + +/* Accessibility title for a Show Password button displaying actial password instead of ***** */ +"credential.provider.list.details.show-password" = "Prikaži geslo"; + +/* Username label for login details on autofill */ +"credential.provider.list.details.username" = "Uporabniško ime"; + +/* Footer label displayed below table section with option to enable autofill */ +"credential.provider.list.empty-view.footer" = "Gesla so varno shranjena v vaši napravi."; + +/* Title for view displayed when autofill has no items */ +"credential.provider.list.empty-view.title" = "Nobeno geslo še ni shranjeno"; + +/* Prompt above the title for screen listing autofill logins, example: Choose a password to use for "website.com" */ +"credential.provider.list.prompt" = "Izberite geslo za »%@«"; + +/* Placeholder for search field on autofill login listing */ +"credential.provider.list.search-placeholder" = "Iskanje gesel"; + +/* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ +"credential.provider.list.search.no-results.subtitle" = "za »%@«"; + +/* Title displayed when there are no results on Autofill search */ +"credential.provider.list.search.no-results.title" = "Ni rezultatov"; + +/* Section title for group of suggested saved logins */ +"credential.provider.list.suggested" = "Predlagano"; + +/* Title for screen listing autofill logins */ +"credential.provider.list.title" = "Gesla"; + +/* Message for alert when device authentication is not set, where %@ is iPhone|iPad|device */ +"credential.provider.no-device-auth-set.message" = "Za samodejno izpolnjevanje gesel DuckDuckGo nastavite kodo za dostop v svoji napravi (%@)."; + +/* Title for alert when device authentication is not set */ +"credential.provider.no-device-auth-set.title" = "Zahtevana je koda za dostop do naprave"; + diff --git a/AutofillCredentialProvider/sv.lproj/InfoPlist.strings b/AutofillCredentialProvider/sv.lproj/InfoPlist.strings new file mode 100644 index 0000000000..9809aa19f5 --- /dev/null +++ b/AutofillCredentialProvider/sv.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "AutofillCredentialProvider"; + +/* Bundle name */ +"CFBundleName" = "AutofillCredentialProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 DuckDuckGo. All rights reserved."; + diff --git a/AutofillCredentialProvider/sv.lproj/Localizable.strings b/AutofillCredentialProvider/sv.lproj/Localizable.strings new file mode 100644 index 0000000000..04f1b6425c --- /dev/null +++ b/AutofillCredentialProvider/sv.lproj/Localizable.strings @@ -0,0 +1,96 @@ +/* Close button title */ +"action.button.close" = "Stäng"; + +/* Done button title */ +"action.button.done" = "Klart"; + +/* Title of button to launch the DuckDuckGo app */ +"credential.provider.activated.button" = "Öppna DuckDuckGo"; + +/* The title of the screen confirming DuckDuckGo can now be used for autofilling passwords */ +"credential.provider.activated.title" = "Automatisk ifyllning av lösenord har aktiverats!"; + +/* Default string used if users device is not iPhone or iPad */ +"credential.provider.device.type.default" = "enhet"; + +/* Device type is iPhone */ +"credential.provider.device.type.iphone" = "iPhone"; + +/* Device type is iPad */ +"credential.provider.device.type.pad" = "iPad"; + +/* Cancel button for auth when opening login list */ +"credential.provider.list.auth.cancel" = "Avbryt"; + +/* Reason for auth when opening screen with list of saved passwords */ +"credential.provider.list.auth.reason" = "Lås upp enheten för att komma åt lösenord"; + +/* Address label for login details on autofill */ +"credential.provider.list.details.address" = "Webbplats-URL"; + +/* Menu item text for copying autofill login details */ +"credential.provider.list.details.copy-prompt" = "Kopiera %@"; + +/* Title for toast when copying address */ +"credential.provider.list.details.copy-toast.address-copied" = "Adress kopierad"; + +/* Title for toast when copying notes */ +"credential.provider.list.details.copy-toast.notes-copied" = "Anteckningar kopierade"; + +/* Title for toast when copying password */ +"credential.provider.list.details.copy-toast.password-copied" = "Lösenord kopierat"; + +/* Title for toast when copying username */ +"credential.provider.list.details.copy-toast.username-copied" = "Användarnamn kopierat"; + +/* Accessibility title for a Hide Password button replacing displayed password with ***** */ +"credential.provider.list.details.hide-password" = "Dölj lösenord"; + +/* Message displaying when the login was last updated */ +"credential.provider.list.details.last-updated" = "Uppdaterades senast %@"; + +/* Login name label for login details on autofill */ +"credential.provider.list.details.login-name" = "Rubrik"; + +/* Notes label for login details on autofill */ +"credential.provider.list.details.notes" = "Anteckningar"; + +/* Password label for login details on autofill */ +"credential.provider.list.details.password" = "Lösenord"; + +/* Accessibility title for a Show Password button displaying actial password instead of ***** */ +"credential.provider.list.details.show-password" = "Visa lösenord"; + +/* Username label for login details on autofill */ +"credential.provider.list.details.username" = "Användarnamn"; + +/* Footer label displayed below table section with option to enable autofill */ +"credential.provider.list.empty-view.footer" = "Lösenord lagras säkert på din enhet."; + +/* Title for view displayed when autofill has no items */ +"credential.provider.list.empty-view.title" = "Inga lösenord sparade ännu"; + +/* Prompt above the title for screen listing autofill logins, example: Choose a password to use for "website.com" */ +"credential.provider.list.prompt" = "Välj ett lösenord att använda för %@"; + +/* Placeholder for search field on autofill login listing */ +"credential.provider.list.search-placeholder" = "Sök lösenord"; + +/* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ +"credential.provider.list.search.no-results.subtitle" = "för ”%@”"; + +/* Title displayed when there are no results on Autofill search */ +"credential.provider.list.search.no-results.title" = "Inga resultat"; + +/* Section title for group of suggested saved logins */ +"credential.provider.list.suggested" = "Förslag"; + +/* Title for screen listing autofill logins */ +"credential.provider.list.title" = "Lösenord"; + +/* Message for alert when device authentication is not set, where %@ is iPhone|iPad|device */ +"credential.provider.no-device-auth-set.message" = "Ställ in en lösenkod på %@ för att automatiskt fylla i dina DuckDuckGo-lösenord."; + +/* Title for alert when device authentication is not set */ +"credential.provider.no-device-auth-set.title" = "Enhetens lösenkod krävs"; + diff --git a/AutofillCredentialProvider/tr.lproj/InfoPlist.strings b/AutofillCredentialProvider/tr.lproj/InfoPlist.strings new file mode 100644 index 0000000000..9809aa19f5 --- /dev/null +++ b/AutofillCredentialProvider/tr.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "AutofillCredentialProvider"; + +/* Bundle name */ +"CFBundleName" = "AutofillCredentialProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2024 DuckDuckGo. All rights reserved."; + diff --git a/AutofillCredentialProvider/tr.lproj/Localizable.strings b/AutofillCredentialProvider/tr.lproj/Localizable.strings new file mode 100644 index 0000000000..1bf39ce8dc --- /dev/null +++ b/AutofillCredentialProvider/tr.lproj/Localizable.strings @@ -0,0 +1,96 @@ +/* Close button title */ +"action.button.close" = "Kapat"; + +/* Done button title */ +"action.button.done" = "Bitti"; + +/* Title of button to launch the DuckDuckGo app */ +"credential.provider.activated.button" = "DuckDuckGo'yu aç"; + +/* The title of the screen confirming DuckDuckGo can now be used for autofilling passwords */ +"credential.provider.activated.title" = "Şifreleri otomatik doldurma etkinleştirildi!"; + +/* Default string used if users device is not iPhone or iPad */ +"credential.provider.device.type.default" = "cihaz"; + +/* Device type is iPhone */ +"credential.provider.device.type.iphone" = "iPhone"; + +/* Device type is iPad */ +"credential.provider.device.type.pad" = "iPad"; + +/* Cancel button for auth when opening login list */ +"credential.provider.list.auth.cancel" = "İptal"; + +/* Reason for auth when opening screen with list of saved passwords */ +"credential.provider.list.auth.reason" = "Şifrelere erişmek için cihazın kilidini açın"; + +/* Address label for login details on autofill */ +"credential.provider.list.details.address" = "Web sitesi URL'si"; + +/* Menu item text for copying autofill login details */ +"credential.provider.list.details.copy-prompt" = "%@ Ögesini Kopyala"; + +/* Title for toast when copying address */ +"credential.provider.list.details.copy-toast.address-copied" = "Adres kopyalandı"; + +/* Title for toast when copying notes */ +"credential.provider.list.details.copy-toast.notes-copied" = "Notlar kopyalandı"; + +/* Title for toast when copying password */ +"credential.provider.list.details.copy-toast.password-copied" = "Şifre kopyalandı"; + +/* Title for toast when copying username */ +"credential.provider.list.details.copy-toast.username-copied" = "Kullanıcı adı kopyalandı"; + +/* Accessibility title for a Hide Password button replacing displayed password with ***** */ +"credential.provider.list.details.hide-password" = "Parolayı gizle"; + +/* Message displaying when the login was last updated */ +"credential.provider.list.details.last-updated" = "Son güncelleme %@"; + +/* Login name label for login details on autofill */ +"credential.provider.list.details.login-name" = "Title"; + +/* Notes label for login details on autofill */ +"credential.provider.list.details.notes" = "Notlar"; + +/* Password label for login details on autofill */ +"credential.provider.list.details.password" = "Parola"; + +/* Accessibility title for a Show Password button displaying actial password instead of ***** */ +"credential.provider.list.details.show-password" = "Parolayı göster"; + +/* Username label for login details on autofill */ +"credential.provider.list.details.username" = "Kullanıcı adı"; + +/* Footer label displayed below table section with option to enable autofill */ +"credential.provider.list.empty-view.footer" = "Şifreler cihazınızda güvenli bir şekilde saklanır."; + +/* Title for view displayed when autofill has no items */ +"credential.provider.list.empty-view.title" = "Henüz şifre kaydedilmedi"; + +/* Prompt above the title for screen listing autofill logins, example: Choose a password to use for "website.com" */ +"credential.provider.list.prompt" = "\"%@\" için kullanılacak bir şifre seçin"; + +/* Placeholder for search field on autofill login listing */ +"credential.provider.list.search-placeholder" = "Şifreleri ara"; + +/* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ +"credential.provider.list.search.no-results.subtitle" = "\"%@\" için"; + +/* Title displayed when there are no results on Autofill search */ +"credential.provider.list.search.no-results.title" = "Sonuç yok"; + +/* Section title for group of suggested saved logins */ +"credential.provider.list.suggested" = "Önerilen"; + +/* Title for screen listing autofill logins */ +"credential.provider.list.title" = "Şifreler"; + +/* Message for alert when device authentication is not set, where %@ is iPhone|iPad|device */ +"credential.provider.no-device-auth-set.message" = "DuckDuckGo şifrelerinizi otomatik olarak doldurmak için %@ üzerinde bir parola belirleyin."; + +/* Title for alert when device authentication is not set */ +"credential.provider.no-device-auth-set.title" = "Cihaz Parolası Gerekli"; + diff --git a/Configuration/Configuration-Alpha.xcconfig b/Configuration/Configuration-Alpha.xcconfig index 73ad56cfdc..ba21ec9797 100644 --- a/Configuration/Configuration-Alpha.xcconfig +++ b/Configuration/Configuration-Alpha.xcconfig @@ -30,5 +30,8 @@ GROUP_ID_PREFIX = group.com.duckduckgo.alpha // The keychain access group for subscriptions SUBSCRIPTION_APP_GROUP = com.duckduckgo.subscriptions.alpha +// The keychain access group for secure vault +VAULT_APP_GROUP = com.duckduckgo.vault.alpha + // Prevents asserts from crashing alpha TF builds OTHER_SWIFT_FLAGS[config=Alpha][arch=*][sdk=*] = $(inherited) -assert-config Release diff --git a/Configuration/Version.xcconfig b/Configuration/Version.xcconfig index 2f435d72f7..fde561fa40 100644 --- a/Configuration/Version.xcconfig +++ b/Configuration/Version.xcconfig @@ -1 +1 @@ -MARKETING_VERSION = 7.148.0 +MARKETING_VERSION = 7.149.1 diff --git a/Core/AppPrivacyConfigurationDataProvider.swift b/Core/AppPrivacyConfigurationDataProvider.swift index 3d1a36860f..ce23ec7e8a 100644 --- a/Core/AppPrivacyConfigurationDataProvider.swift +++ b/Core/AppPrivacyConfigurationDataProvider.swift @@ -23,8 +23,8 @@ import BrowserServicesKit final public class AppPrivacyConfigurationDataProvider: EmbeddedDataProvider { public struct Constants { - public static let embeddedDataETag = "\"a913df909743eadb5bd381c7fddf4902\"" - public static let embeddedDataSHA = "d1fb789ab4def06b6cf359d5e9971c840668ab5307f5690bc1483bb2266a2558" + public static let embeddedDataETag = "\"03c27339b8dd9a474cc2bafb84ba8012\"" + public static let embeddedDataSHA = "783c4cccf30cc0b4fe3caea212c9e1dca3b276b859d07360fa08b586f006438f" } public var embeddedDataEtag: String { diff --git a/Core/AppTrackerDataSetProvider.swift b/Core/AppTrackerDataSetProvider.swift index 1ad2d9fb89..4e73164f0a 100644 --- a/Core/AppTrackerDataSetProvider.swift +++ b/Core/AppTrackerDataSetProvider.swift @@ -23,8 +23,8 @@ import BrowserServicesKit final public class AppTrackerDataSetProvider: EmbeddedDataProvider { public struct Constants { - public static let embeddedDataETag = "\"7af63a1c5812c9200d3120f5dd598993\"" - public static let embeddedDataSHA = "ef3eb8b998faa201192a190cff3fe0fc228cf2212be44271036dbd63ade5ddcf" + public static let embeddedDataETag = "\"382541021cbc55b218714db779561a51\"" + public static let embeddedDataSHA = "5e8293a4c3e3ea5217e8e39db0c8525de4647ec78b102cff292ab5fa213578aa" } public var embeddedDataEtag: String { diff --git a/DuckDuckGo/AutofillInterfaceEmailTruncator.swift b/Core/AutofillInterfaceEmailTruncator.swift similarity index 91% rename from DuckDuckGo/AutofillInterfaceEmailTruncator.swift rename to Core/AutofillInterfaceEmailTruncator.swift index 2eb28faa1b..41fb28342c 100644 --- a/DuckDuckGo/AutofillInterfaceEmailTruncator.swift +++ b/Core/AutofillInterfaceEmailTruncator.swift @@ -19,8 +19,8 @@ import Foundation -struct AutofillInterfaceEmailTruncator { - static func truncateEmail(_ email: String, maxLength: Int) -> String { +public struct AutofillInterfaceEmailTruncator { + public static func truncateEmail(_ email: String, maxLength: Int) -> String { let emailComponents = email.components(separatedBy: "@") if emailComponents.count > 1 && email.count > maxLength { let ellipsis = "..." diff --git a/DuckDuckGo/AutofillLoginListItemViewModel.swift b/Core/AutofillLoginItem.swift similarity index 57% rename from DuckDuckGo/AutofillLoginListItemViewModel.swift rename to Core/AutofillLoginItem.swift index 8bbc5c3f6f..2eb5e3b71a 100644 --- a/DuckDuckGo/AutofillLoginListItemViewModel.swift +++ b/Core/AutofillLoginItem.swift @@ -1,8 +1,8 @@ // -// AutofillLoginListItemViewModel.swift +// AutofillLoginItem.swift // DuckDuckGo // -// Copyright © 2022 DuckDuckGo. All rights reserved. +// Copyright © 2024 DuckDuckGo. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -19,40 +19,44 @@ import Foundation import BrowserServicesKit -import UIKit import Common -final class AutofillLoginListItemViewModel: Identifiable, Hashable { - - var preferredFaviconLetters: String { +public struct AutofillLoginItem: Identifiable, Hashable { + + public let id = UUID() + public let account: SecureVaultModels.WebsiteAccount + public let title: String + public let subtitle: String + + public var preferredFaviconLetters: String { let accountName = self.account.name(tld: tld, autofillDomainNameUrlMatcher: urlMatcher) let accountTitle = (account.title?.isEmpty == false) ? account.title! : "#" return tld.eTLDplus1(accountName) ?? accountTitle } - - let account: SecureVaultModels.WebsiteAccount - let title: String - let subtitle: String - let id = UUID() - let tld: TLD - let urlMatcher: AutofillDomainNameUrlMatcher - - internal init(account: SecureVaultModels.WebsiteAccount, - tld: TLD, - autofillDomainNameUrlMatcher: AutofillDomainNameUrlMatcher, - autofillDomainNameUrlSort: AutofillDomainNameUrlSort) { + + private let tld: TLD + private let urlMatcher: AutofillDomainNameUrlMatcher + + public init(account: SecureVaultModels.WebsiteAccount, + tld: TLD, + autofillDomainNameUrlMatcher: AutofillDomainNameUrlMatcher, + autofillDomainNameUrlSort: AutofillDomainNameUrlSort) { self.account = account self.tld = tld + self.urlMatcher = autofillDomainNameUrlMatcher self.title = account.name(tld: tld, autofillDomainNameUrlMatcher: autofillDomainNameUrlMatcher) self.subtitle = account.username ?? "" - self.urlMatcher = autofillDomainNameUrlMatcher } - - static func == (lhs: AutofillLoginListItemViewModel, rhs: AutofillLoginListItemViewModel) -> Bool { + + public static func == (lhs: AutofillLoginItem, rhs: AutofillLoginItem) -> Bool { lhs.account.id == rhs.account.id } - - func hash(into hasher: inout Hasher) { + + static func < (lhs: AutofillLoginItem, rhs: AutofillLoginItem) -> Bool { + lhs.title < rhs.title + } + + public func hash(into hasher: inout Hasher) { hasher.combine(account.id) } } diff --git a/Core/AutofillLoginListSectionType.swift b/Core/AutofillLoginListSectionType.swift new file mode 100644 index 0000000000..5c05d2f0e3 --- /dev/null +++ b/Core/AutofillLoginListSectionType.swift @@ -0,0 +1,44 @@ +// +// AutofillLoginListSectionType.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +public enum AutofillLoginListSectionType: Comparable { + + case enableAutofill + case suggestions(title: String, items: [AutofillLoginItem]) + case credentials(title: String, items: [AutofillLoginItem]) + + public static func < (lhs: AutofillLoginListSectionType, rhs: AutofillLoginListSectionType) -> Bool { + if case .credentials(let leftTitle, _) = lhs, + case .credentials(let rightTitle, _) = rhs { + if leftTitle == miscSectionHeading { + return false + } else if rightTitle == miscSectionHeading { + return true + } + + return leftTitle.localizedCaseInsensitiveCompare(rightTitle) == .orderedAscending + } + return true + } + + public static let miscSectionHeading = "#" + +} diff --git a/Core/AutofillLoginListSorting.swift b/Core/AutofillLoginListSorting.swift new file mode 100644 index 0000000000..a58407efbe --- /dev/null +++ b/Core/AutofillLoginListSorting.swift @@ -0,0 +1,65 @@ +// +// AutofillLoginListSorting.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import BrowserServicesKit +import Common + +public extension Array where Element == SecureVaultModels.WebsiteAccount { + + func groupedByFirstLetter(tld: TLD, + autofillDomainNameUrlMatcher: AutofillDomainNameUrlMatcher, + autofillDomainNameUrlSort: AutofillDomainNameUrlSort) + -> [String: [AutofillLoginItem]] { + reduce(into: [String: [AutofillLoginItem]]()) { result, account in + + // Unfortunetly, folding doesn't produce perfect results despite respecting the system locale + // E.g. Romainian should treat letters with diacritics as seperate letters, but folding doesn't + // Apple's own apps (e.g. contacts) seem to suffer from the same problem + let key: String + if let firstChar = autofillDomainNameUrlSort.firstCharacterForGrouping(account, tld: tld), + let deDistinctionedChar = String(firstChar).folding(options: [.diacriticInsensitive, .caseInsensitive], locale: nil).first, + deDistinctionedChar.isLetter { + + key = String(deDistinctionedChar) + } else { + key = AutofillLoginListSectionType.miscSectionHeading + } + + return result[key, default: []].append(AutofillLoginItem(account: account, + tld: tld, + autofillDomainNameUrlMatcher: autofillDomainNameUrlMatcher, + autofillDomainNameUrlSort: autofillDomainNameUrlSort)) + } + } +} + +public extension Dictionary where Key == String, Value == [AutofillLoginItem] { + + func sortedIntoSections(_ autofillDomainNameUrlSort: AutofillDomainNameUrlSort, + tld: TLD) -> [AutofillLoginListSectionType] { + map { dictionaryItem -> AutofillLoginListSectionType in + let sortedGroup = dictionaryItem.value.sorted(by: { + autofillDomainNameUrlSort.compareAccountsForSortingAutofill(lhs: $0.account, rhs: $1.account, tld: tld) == .orderedAscending + }) + return AutofillLoginListSectionType.credentials(title: dictionaryItem.key, + items: sortedGroup) + }.sorted() + } +} diff --git a/Core/ContentBlocking.swift b/Core/ContentBlocking.swift index ed3f02d1aa..1a9c2cd7ba 100644 --- a/Core/ContentBlocking.swift +++ b/Core/ContentBlocking.swift @@ -116,9 +116,6 @@ public final class ContentBlocking { domainEvent = .contentBlockingCompilationFailed(listType: listType, component: component) - case .contentBlockingCompilationTime: - domainEvent = .contentBlockingCompilationTime - case .contentBlockingLookupRulesSucceeded: domainEvent = .contentBlockingLookupRulesSucceeded @@ -130,6 +127,10 @@ public final class ContentBlocking { case .contentBlockingLRCMissing: domainEvent = .contentBlockingLRCMissing + + case .contentBlockingCompilationTaskPerformance(let retryCount, let timeBucketAggregation): + domainEvent = .contentBlockingCompilationTaskPerformance(iterationCount: retryCount, + timeBucketAggregation: Pixel.Event.CompileTimeBucketAggregation(number: timeBucketAggregation)) } if let error = error { diff --git a/Core/DefaultVariantManager.swift b/Core/DefaultVariantManager.swift index 76f0f8475a..8e6748dd41 100644 --- a/Core/DefaultVariantManager.swift +++ b/Core/DefaultVariantManager.swift @@ -27,9 +27,8 @@ extension FeatureName { // Define your feature e.g.: // public static let experimentalFeature = FeatureName(rawValue: "experimentalFeature") - public static let newOnboardingIntro = FeatureName(rawValue: "newOnboardingIntro") - public static let newOnboardingIntroHighlights = FeatureName(rawValue: "newOnboardingIntroHighlights") - public static let contextualDaxDialogs = FeatureName(rawValue: "contextualDaxDialogs") + public static let addToDockIntro = FeatureName(rawValue: "addToDockIntro") + public static let addToDockContextual = FeatureName(rawValue: "addToDockContextual") } public struct VariantIOS: Variant { @@ -58,9 +57,9 @@ public struct VariantIOS: Variant { VariantIOS(name: "sd", weight: doNotAllocate, isIncluded: When.always, features: []), VariantIOS(name: "se", weight: doNotAllocate, isIncluded: When.always, features: []), - VariantIOS(name: "ms", weight: 1, isIncluded: When.always, features: [.newOnboardingIntro]), - VariantIOS(name: "mu", weight: 1, isIncluded: When.always, features: [.newOnboardingIntro, .contextualDaxDialogs]), - VariantIOS(name: "mx", weight: 1, isIncluded: When.always, features: [.newOnboardingIntroHighlights, .contextualDaxDialogs]), + VariantIOS(name: "mh", weight: 1, isIncluded: When.notPadDevice, features: []), + VariantIOS(name: "mk", weight: 1, isIncluded: When.notPadDevice, features: [.addToDockIntro]), + VariantIOS(name: "mo", weight: 1, isIncluded: When.notPadDevice, features: [.addToDockContextual]), returningUser ] diff --git a/Core/FeatureFlag.swift b/Core/FeatureFlag.swift index 15263d9e7b..7ec10ba680 100644 --- a/Core/FeatureFlag.swift +++ b/Core/FeatureFlag.swift @@ -32,6 +32,7 @@ public enum FeatureFlag: String { case autofillFailureReporting case autofillOnForExistingUsers case autofillUnknownUsernameCategorization + case autofillPartialFormSaves case incontextSignup case autoconsentOnByDefault case history @@ -56,6 +57,7 @@ public enum FeatureFlag: String { /// https://app.asana.com/0/1208592102886666/1208613627589762/f case crashReportOptInStatusResetting + case isPrivacyProLaunchedROW case isPrivacyProLaunchedROWOverride @@ -100,6 +102,8 @@ extension FeatureFlag: FeatureFlagDescribing { return .remoteReleasable(.subfeature(AutofillSubfeature.onForExistingUsers)) case .autofillUnknownUsernameCategorization: return .remoteReleasable(.subfeature(AutofillSubfeature.unknownUsernameCategorization)) + case .autofillPartialFormSaves: + return .remoteReleasable(.subfeature(AutofillSubfeature.partialFormSaves)) case .incontextSignup: return .remoteReleasable(.feature(.incontextSignup)) case .autoconsentOnByDefault: diff --git a/DuckDuckGo/PasswordHider.swift b/Core/PasswordHider.swift similarity index 83% rename from DuckDuckGo/PasswordHider.swift rename to Core/PasswordHider.swift index 1849248f24..ffe3a70929 100644 --- a/DuckDuckGo/PasswordHider.swift +++ b/Core/PasswordHider.swift @@ -19,11 +19,15 @@ import Foundation -struct PasswordHider { - let password: String - var hiddenPassword: String { +public struct PasswordHider { + public let password: String + public var hiddenPassword: String { let maximumPasswordDisplayCount = 22 let passwordCount = password.count > maximumPasswordDisplayCount ? maximumPasswordDisplayCount : password.count return String(repeating: "•", count: passwordCount) } + + public init(password: String) { + self.password = password + } } diff --git a/Core/Pixel.swift b/Core/Pixel.swift index 04f48ee03f..ded3c22a2c 100644 --- a/Core/Pixel.swift +++ b/Core/Pixel.swift @@ -147,6 +147,7 @@ public struct PixelParameters { // Autofill public static let countBucket = "count_bucket" + public static let backfilled = "backfilled" // Privacy Dashboard public static let daysSinceInstall = "daysSinceInstall" diff --git a/Core/PixelEvent.swift b/Core/PixelEvent.swift index ee2628e3d7..eb924e0142 100644 --- a/Core/PixelEvent.swift +++ b/Core/PixelEvent.swift @@ -27,7 +27,8 @@ import NetworkProtection extension Pixel { public enum Event { - + + case appInstall case appLaunch case refreshPressed case pullToRefresh @@ -59,7 +60,11 @@ extension Pixel { case tabSwitcherClickCloseTab case tabSwitcherSwipeCloseTab case tabSwitchLongPressNewTab - case tabSwitcherOpenDaily + case tabSwitcherOpenedDaily + + case tabSwitcherOpenedFromSerp + case tabSwitcherOpenedFromWebsite + case tabSwitcherOpenedFromNewTabPage case settingsDoNotSellShown case settingsDoNotSellOn @@ -80,6 +85,7 @@ extension Pixel { case browsingMenuShare case browsingMenuCopy case browsingMenuPrint + case browsingMenuListPrint case browsingMenuFindInPage case browsingMenuZoom case browsingMenuDisableProtection @@ -87,7 +93,8 @@ extension Pixel { case browsingMenuReportBrokenSite case browsingMenuFireproof case browsingMenuAutofill - + case browsingMenuAIChat + case addressBarShare case addressBarSettings case addressBarCancelPressedOnNTP @@ -115,7 +122,7 @@ extension Pixel { case tabBarForwardPressed case bookmarksButtonPressed case tabBarBookmarksLongPressed - case tabBarTabSwitcherPressed + case tabBarTabSwitcherOpened case homeScreenShown case homeScreenEditFavorite @@ -309,6 +316,8 @@ extension Pixel { case autofillOnboardedUser case autofillToggledOn case autofillToggledOff + case autofillExtensionToggledOn + case autofillExtensionToggledOff case autofillLoginsStacked case autofillManagementOpened @@ -329,7 +338,18 @@ extension Pixel { case getDesktopCopy case getDesktopShare - + + case autofillExtensionEnabled + case autofillExtensionDisabled + case autofillExtensionWelcomeDismiss + case autofillExtensionWelcomeLaunchApp + case autofillExtensionQuickTypeConfirmed + case autofillExtensionQuickTypeCancelled + case autofillExtensionPasswordsOpened + case autofillExtensionPasswordsDismissed + case autofillExtensionPasswordSelected + case autofillExtensionPasswordsSearch + case autofillJSPixelFired(_ pixel: AutofillUserScript.JSPixel) case secureVaultError @@ -503,6 +523,7 @@ extension Pixel { // MARK: debug pixels case dbCrashDetected + case dbCrashDetectedDaily case crashOnCrashHandlersSetUp case crashReportCRCIDMissing @@ -535,12 +556,12 @@ extension Pixel { case contentBlockingCompilationFailed(listType: CompileRulesListType, component: ContentBlockerDebugEvents.Component) - case contentBlockingCompilationTime case contentBlockingLookupRulesSucceeded case contentBlockingFetchLRCSucceeded case contentBlockingNoMatchInLRC case contentBlockingLRCMissing + case contentBlockingCompilationTaskPerformance(iterationCount: Int, timeBucketAggregation: CompileTimeBucketAggregation) case ampBlockingRulesCompilationFailed case webKitDidTerminate @@ -759,6 +780,8 @@ extension Pixel { // MARK: Pixel Experiment case pixelExperimentEnrollment + + // MARK: Settings case settingsPresented case settingsSetAsDefault case settingsVoiceSearchOn @@ -776,6 +799,26 @@ extension Pixel { case settingsAccessibilityOpen case settingsAccessiblityTextZoom + case settingsPrivateSearchOpen + case settingsEmailProtectionOpen + case settingsEmailProtectionEnable + case settingsGeneralOpen + case settingsSyncOpen + case settingsAppearanceOpen + case settingsThemeSelectorPressed + case settingsAddressBarTopSelected + case settingsAddressBarBottomSelected + case settingsShowFullURLOn + case settingsShowFullURLOff + case settingsDataClearingOpen + case settingsFireButtonSelectorPressed + case settingsDataClearingClearDataOpen + case settingsAutomaticallyClearDataOn + case settingsAutomaticallyClearDataOff + case settingsNextStepsAddAppToDock + case settingsNextStepsAddWidget + case settingsMoreSearchSettings + // Web pixels case privacyProOfferMonthlyPriceClick case privacyProOfferYearlyPriceClick @@ -898,9 +941,12 @@ extension Pixel { case appDidShowUITime(time: BucketAggregation) case appDidBecomeActiveTime(time: BucketAggregation) + // MARK: AI Chat + case aiChatNoRemoteSettingsFound(settings: String) + case openAIChatFromAddressBar + // MARK: Lifecycle case appDidTransitionToUnexpectedState - } } @@ -911,6 +957,7 @@ extension Pixel.Event { public var name: String { switch self { + case .appInstall: return "m_install" case .appLaunch: return "ml" case .refreshPressed: return "m_r" case .pullToRefresh: return "m_pull-to-reload" @@ -942,7 +989,11 @@ extension Pixel.Event { case .tabSwitcherClickCloseTab: return "m_tab_manager_close_tab_click" case .tabSwitcherSwipeCloseTab: return "m_tab_manager_close_tab_swipe" case .tabSwitchLongPressNewTab: return "m_tab_manager_long_press_new_tab" - case .tabSwitcherOpenDaily: return "m_tab_manager_clicked_daily" + case .tabSwitcherOpenedDaily: return "m_tab_manager_opened_daily" + + case .tabSwitcherOpenedFromSerp: return "m_tab_manager_open_from_serp" + case .tabSwitcherOpenedFromWebsite: return "m_tab_manager_open_from_website" + case .tabSwitcherOpenedFromNewTabPage: return "m_tab_manager_open_from_newtabpage" case .settingsDoNotSellShown: return "ms_dns" case .settingsDoNotSellOn: return "ms_dns_on" @@ -951,7 +1002,27 @@ extension Pixel.Event { case .settingsAutoconsentShown: return "m_settings_autoconsent_shown" case .settingsAutoconsentOn: return "m_settings_autoconsent_on" case .settingsAutoconsentOff: return "m_settings_autoconsent_off" - + + case .settingsPrivateSearchOpen: return "m_settings_private_search_open" + case .settingsEmailProtectionOpen: return "m_settings_email_protection_open" + case .settingsEmailProtectionEnable: return "m_settings_email_protection_enable" + case .settingsGeneralOpen: return "m_settings_general_open" + case .settingsSyncOpen: return "m_settings_sync_open" + case .settingsAppearanceOpen: return "m_settings_appearance_open" + case .settingsThemeSelectorPressed: return "m_settings_theme_selector_pressed" + case .settingsAddressBarTopSelected: return "m_settings_address_bar_top_selected" + case .settingsAddressBarBottomSelected: return "m_settings_address_bar_bottom_selected" + case .settingsShowFullURLOn: return "m_settings_show_full_url_on" + case .settingsShowFullURLOff: return "m_settings_show_full_url_off" + case .settingsDataClearingOpen: return "m_settings_data_clearing_open" + case .settingsFireButtonSelectorPressed: return "m_settings_fire_button_selector_pressed" + case .settingsDataClearingClearDataOpen: return "m_settings_data_clearing_clear_data_open" + case .settingsAutomaticallyClearDataOn: return "m_settings_automatically_clear_data_on" + case .settingsAutomaticallyClearDataOff: return "m_settings_automatically_clear_data_off" + case .settingsNextStepsAddAppToDock: return "m_settings_next_steps_add_app_to_dock" + case .settingsNextStepsAddWidget: return "m_settings_next_steps_add_widget" + case .settingsMoreSearchSettings: return "m_settings_more_search_settings" + case .browsingMenuOpened: return "mb" case .browsingMenuNewTab: return "mb_tb" case .browsingMenuAddToBookmarks: return "mb_abk" @@ -962,6 +1033,7 @@ extension Pixel.Event { case .browsingMenuToggleBrowsingMode: return "mb_dm" case .browsingMenuCopy: return "mb_cp" case .browsingMenuPrint: return "mb_pr" + case .browsingMenuFindInPage: return "mb_fp" case .browsingMenuZoom: return "m_menu_page_zoom_taps" case .browsingMenuDisableProtection: return "mb_wla" @@ -971,6 +1043,8 @@ extension Pixel.Event { case .browsingMenuAutofill: return "m_nav_autofill_menu_item_pressed" case .browsingMenuShare: return "m_browsingmenu_share" + case .browsingMenuAIChat: return "m_aichat_menu_tab_icon" + case .browsingMenuListPrint: return "m_browsing_menu_list_print" case .addressBarShare: return "m_addressbar_share" case .addressBarSettings: return "m_addressbar_settings" @@ -999,7 +1073,7 @@ extension Pixel.Event { case .tabBarForwardPressed: return "mt_fw" case .bookmarksButtonPressed: return "mt_bm" case .tabBarBookmarksLongPressed: return "mt_bl" - case .tabBarTabSwitcherPressed: return "mt_tb" + case .tabBarTabSwitcherOpened: return "m_tab_manager_opened" case .bookmarkLaunchList: return "m_bookmark_launch_list" case .bookmarkLaunchScored: return "m_bookmark_launch_scored" @@ -1194,6 +1268,8 @@ extension Pixel.Event { case .autofillOnboardedUser: return "m_autofill_onboardeduser" case .autofillToggledOn: return "m_autofill_toggled_on" case .autofillToggledOff: return "m_autofill_toggled_off" + case .autofillExtensionToggledOn: return "m_autofill_extension_toggled_on" + case .autofillExtensionToggledOff: return "m_autofill_extension_toggled_off" case .autofillLoginsStacked: return "m_autofill_logins_stacked" @@ -1223,6 +1299,18 @@ extension Pixel.Event { case .getDesktopCopy: return "m_get_desktop_copy" case .getDesktopShare: return "m_get_desktop_share" + // Autofill Credential Provider Extension + case .autofillExtensionEnabled: return "autofill_extension_enabled" + case .autofillExtensionDisabled: return "autofill_extension_disabled" + case .autofillExtensionWelcomeDismiss: return "autofill_extension_welcome_dismiss" + case .autofillExtensionWelcomeLaunchApp: return "autofill_extension_welcome_launch_app" + case .autofillExtensionQuickTypeConfirmed: return "autofill_extension_quicktype_confirmed" + case .autofillExtensionQuickTypeCancelled: return "autofill_extension_quicktype_cancelled" + case .autofillExtensionPasswordsOpened: return "autofill_extension_passwords_opened" + case .autofillExtensionPasswordsDismissed: return "autofill_extension_passwords_dismissed" + case .autofillExtensionPasswordSelected: return "autofill_extension_password_selected" + case .autofillExtensionPasswordsSearch: return "autofill_extension_passwords_search" + case .autofillJSPixelFired(let pixel): return "m_ios_\(pixel.pixelName)" @@ -1370,6 +1458,7 @@ extension Pixel.Event { // MARK: debug pixels case .dbCrashDetected: return "m_d_crash" + case .dbCrashDetectedDaily: return "m_d_crash_daily" case .crashReportCRCIDMissing: return "crashreporting_crcid-missing" case .crashReportingSubmissionFailed: return "crashreporting_submission-failed" case .crashOnCrashHandlersSetUp: return "m_d_crash_on_handlers_setup" @@ -1402,13 +1491,14 @@ extension Pixel.Event { case .contentBlockingCompilationFailed(let listType, let component): return "m_d_content_blocking_\(listType)_\(component)_compilation_failed" - case .contentBlockingCompilationTime: return "m_content_blocking_compilation_time" case .contentBlockingLookupRulesSucceeded: return "m_content_blocking_lookup_rules_succeeded" case .contentBlockingFetchLRCSucceeded: return "m_content_blocking_fetch_lrc_succeeded" case .contentBlockingNoMatchInLRC: return "m_content_blocking_no_match_in_lrc" case .contentBlockingLRCMissing: return "m_content_blocking_lrc_missing" + case .contentBlockingCompilationTaskPerformance(let iterationCount, let timeBucketAggregation): + return "m_content_blocking_compilation_loops_\(iterationCount)_time_\(timeBucketAggregation)" case .ampBlockingRulesCompilationFailed: return "m_debug_amp_rules_compilation_failed" case .webKitDidTerminate: return "m_d_wkt" @@ -1637,6 +1727,8 @@ extension Pixel.Event { // MARK: Pixel Experiment case .pixelExperimentEnrollment: return "pixel_experiment_enrollment" + + // MARK: Settings case .settingsPresented: return "m_settings_presented" case .settingsSetAsDefault: return "m_settings_set_as_default" case .settingsVoiceSearchOn: return "m_settings_voice_search_on" @@ -1792,6 +1884,11 @@ extension Pixel.Event { case .appDidShowUITime(let time): return "m_debug_app-did-show-ui-time-\(time)" case .appDidBecomeActiveTime(let time): return "m_debug_app-did-become-active-time-\(time)" + // MARK: AI Chat + case .aiChatNoRemoteSettingsFound(let settings): + return "m_aichat_no_remote_settings_found-\(settings.lowercased())" + case .openAIChatFromAddressBar: return "m_aichat_addressbar_icon" + // MARK: Lifecycle case .appDidTransitionToUnexpectedState: return "m_debug_app-did-transition-to-unexpected-state" @@ -1868,6 +1965,50 @@ extension Pixel.Event { case blockingAttribution case attributed case unknown - + + } + + public enum CompileTimeBucketAggregation: String, CustomStringConvertible { + + public var description: String { rawValue } + + case lessThan1 = "1" + case lessThan2 = "2" + case lessThan3 = "3" + case lessThan4 = "4" + case lessThan5 = "5" + case lessThan6 = "6" + case lessThan7 = "7" + case lessThan8 = "8" + case lessThan9 = "9" + case lessThan10 = "10" + case more + + public init(number: Double) { + switch number { + case ...1: + self = .lessThan1 + case ...2: + self = .lessThan2 + case ...3: + self = .lessThan3 + case ...4: + self = .lessThan4 + case ...5: + self = .lessThan5 + case ...6: + self = .lessThan6 + case ...7: + self = .lessThan7 + case ...8: + self = .lessThan8 + case ...9: + self = .lessThan9 + case ...10: + self = .lessThan10 + default: + self = .more + } + } } } diff --git a/Core/StatisticsLoader.swift b/Core/StatisticsLoader.swift index a8001e6077..166e8e5014 100644 --- a/Core/StatisticsLoader.swift +++ b/Core/StatisticsLoader.swift @@ -21,6 +21,8 @@ import Common import Foundation import BrowserServicesKit import Networking +import PixelKit +import PixelExperimentKit import os.log public class StatisticsLoader { @@ -35,15 +37,24 @@ public class StatisticsLoader { private let parser = AtbParser() private let atbPresenceFileMarker = BoolFileMarker(name: .isATBPresent) private let inconsistencyMonitoring: StatisticsStoreInconsistencyMonitoring + private let fireSearchExperimentPixels: () -> Void + private let fireAppRetentionExperimentPixels: () -> Void + private let pixelFiring: PixelFiring.Type init(statisticsStore: StatisticsStore = StatisticsUserDefaults(), returnUserMeasurement: ReturnUserMeasurement = KeychainReturnUserMeasurement(), usageSegmentation: UsageSegmenting = UsageSegmentation(), - inconsistencyMonitoring: StatisticsStoreInconsistencyMonitoring = StorageInconsistencyMonitor()) { + inconsistencyMonitoring: StatisticsStoreInconsistencyMonitoring = StorageInconsistencyMonitor(), + fireAppRetentionExperimentPixels: @escaping () -> Void = PixelKit.fireAppRetentionExperimentPixels, + fireSearchExperimentPixels: @escaping () -> Void = PixelKit.fireSearchExperimentPixels, + pixelFiring: PixelFiring.Type = Pixel.self) { self.statisticsStore = statisticsStore self.returnUserMeasurement = returnUserMeasurement self.usageSegmentation = usageSegmentation self.inconsistencyMonitoring = inconsistencyMonitoring + self.fireSearchExperimentPixels = fireSearchExperimentPixels + self.fireAppRetentionExperimentPixels = fireAppRetentionExperimentPixels + self.pixelFiring = pixelFiring } public func load(completion: @escaping Completion = {}) { @@ -94,6 +105,7 @@ public class StatisticsLoader { completion() return } + self.fireInstallPixel() self.statisticsStore.installDate = Date() self.statisticsStore.atb = atb.version self.returnUserMeasurement.installCompletedWithATB(atb) @@ -102,11 +114,26 @@ public class StatisticsLoader { } } + private func fireInstallPixel() { + let formattedLocale = Locale.current.localeIdentifierAsJsonFormat + let isReinstall = String(statisticsStore.variant == VariantIOS.returningUser.name) + let parameters = [ + "locale": formattedLocale, + "reinstall": isReinstall + ] + pixelFiring.fire(.appInstall, withAdditionalParameters: parameters, includedParameters: [.appVersion], onComplete: { error in + if let error { + Logger.general.error("Install pixel failed with error: \(error.localizedDescription, privacy: .public)") + } + }) + } + private func createATBFileMarker() { atbPresenceFileMarker?.mark() } public func refreshSearchRetentionAtb(completion: @escaping Completion = {}) { + fireSearchExperimentPixels() guard let url = StatisticsDependentURLFactory(statisticsStore: statisticsStore).makeSearchAtbURL() else { requestInstallStatistics { self.updateUsageSegmentationAfterInstall(activityType: .search) @@ -136,6 +163,7 @@ public class StatisticsLoader { } public func refreshAppRetentionAtb(completion: @escaping Completion = {}) { + fireAppRetentionExperimentPixels() guard let url = StatisticsDependentURLFactory(statisticsStore: statisticsStore).makeAppAtbURL() else { requestInstallStatistics { self.updateUsageSegmentationAfterInstall(activityType: .appUse) diff --git a/Core/SyncCredentialsAdapter.swift b/Core/SyncCredentialsAdapter.swift index 7fa4b8c509..888341cc87 100644 --- a/Core/SyncCredentialsAdapter.swift +++ b/Core/SyncCredentialsAdapter.swift @@ -33,10 +33,12 @@ public final class SyncCredentialsAdapter { public static let syncCredentialsPausedStateChanged = SyncBookmarksAdapter.syncBookmarksPausedStateChanged public static let credentialsSyncLimitReached = Notification.Name("com.duckduckgo.app.SyncCredentialsLimitReached") let syncErrorHandler: SyncErrorHandling + let credentialIdentityStoreManager: AutofillCredentialIdentityStoreManaging public init(secureVaultFactory: AutofillVaultFactory = AutofillSecureVaultFactory, secureVaultErrorReporter: SecureVaultReporting, - syncErrorHandler: SyncErrorHandling) { + syncErrorHandler: SyncErrorHandling, + tld: TLD) { syncDidCompletePublisher = syncDidCompleteSubject.eraseToAnyPublisher() self.secureVaultErrorReporter = secureVaultErrorReporter self.syncErrorHandler = syncErrorHandler @@ -45,6 +47,8 @@ public final class SyncCredentialsAdapter { secureVaultErrorReporter: secureVaultErrorReporter, errorEvents: CredentialsCleanupErrorHandling() ) + credentialIdentityStoreManager = AutofillCredentialIdentityStoreManager( + vault: try? secureVaultFactory.makeVault(reporter: secureVaultErrorReporter), tld: tld) } public func cleanUpDatabaseAndUpdateSchedule(shouldEnable: Bool) { @@ -74,9 +78,15 @@ public final class SyncCredentialsAdapter { syncDidUpdateData: { [weak self] in self?.syncDidCompleteSubject.send() self?.syncErrorHandler.syncCredentialsSucceded() + }, + syncDidFinish: { [weak self] credentialsInput in + if let credentialsInput, !credentialsInput.modifiedAccounts.isEmpty || !credentialsInput.deletedAccounts.isEmpty { + Task { + await self?.credentialIdentityStoreManager.updateCredentialStoreWith(updatedAccounts: credentialsInput.modifiedAccounts, deletedAccounts: credentialsInput.deletedAccounts) + } + } } ) - syncErrorCancellable = provider.syncErrorPublisher .sink { [weak self] error in self?.syncErrorHandler.handleCredentialError(error) diff --git a/Core/SyncDataProviders.swift b/Core/SyncDataProviders.swift index a32f3b8508..2220b05bd6 100644 --- a/Core/SyncDataProviders.swift +++ b/Core/SyncDataProviders.swift @@ -101,7 +101,8 @@ public class SyncDataProviders: DataProvidersSource { settingHandlers: [SettingSyncHandler], favoritesDisplayModeStorage: FavoritesDisplayModeStoring, syncErrorHandler: SyncErrorHandling, - faviconStoring: FaviconStoring + faviconStoring: FaviconStoring, + tld: TLD ) { self.bookmarksDatabase = bookmarksDatabase self.secureVaultFactory = secureVaultFactory @@ -112,7 +113,8 @@ public class SyncDataProviders: DataProvidersSource { faviconStoring: faviconStoring) credentialsAdapter = SyncCredentialsAdapter(secureVaultFactory: secureVaultFactory, secureVaultErrorReporter: secureVaultErrorReporter, - syncErrorHandler: syncErrorHandler) + syncErrorHandler: syncErrorHandler, + tld: tld) settingsAdapter = SyncSettingsAdapter(settingHandlers: settingHandlers, syncErrorHandler: syncErrorHandler) } diff --git a/Core/UserAuthenticator.swift b/Core/UserAuthenticator.swift new file mode 100644 index 0000000000..551a598b4f --- /dev/null +++ b/Core/UserAuthenticator.swift @@ -0,0 +1,101 @@ +// +// UserAuthenticator.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import LocalAuthentication +import os.log + +open class UserAuthenticator { + + public enum AuthError: Error, Equatable { + case noAuthAvailable + case failedToAuthenticate + } + + public enum AuthenticationState { + case loggedIn, loggedOut, notAvailable + } + + public struct Notifications { + public static let invalidateContext = Notification.Name("com.duckduckgo.app.UserAuthenticator.invalidateContext") + } + + private var context = LAContext() + private var reason: String + private var cancelTitle: String + @Published public private(set) var state = AuthenticationState.loggedOut + + public init(reason: String, cancelTitle: String) { + self.reason = reason + self.cancelTitle = cancelTitle + } + + public func logOut() { + state = .loggedOut + } + + public func canAuthenticate() -> Bool { + var error: NSError? + let canAuthenticate = LAContext().canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) + return canAuthenticate + } + + public func canAuthenticateViaBiometrics() -> Bool { + var error: NSError? + let canAuthenticate = LAContext().canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) + return canAuthenticate + } + + open func authenticate(completion: ((AuthError?) -> Void)? = nil) { + + if state == .loggedIn { + completion?(nil) + return + } + + context = LAContext() + context.localizedCancelTitle = cancelTitle + context.interactionNotAllowed = false + context.localizedReason = reason + + if canAuthenticate() { + let reason = reason + context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: reason ) { [weak self] success, error in + + DispatchQueue.main.async { + if success { + self?.state = .loggedIn + completion?(nil) + } else { + Logger.general.error("Failed to authenticate: \(error?.localizedDescription ?? "nil", privacy: .public)") + completion?(.failedToAuthenticate) + } + } + } + } else { + state = .notAvailable + completion?(.noAuthAvailable) + } + } + + public func invalidateContext() { + context.invalidate() + } + +} diff --git a/Core/UserDefaultsPropertyWrapper.swift b/Core/UserDefaultsPropertyWrapper.swift index 4858ad964d..4b0b9682ab 100644 --- a/Core/UserDefaultsPropertyWrapper.swift +++ b/Core/UserDefaultsPropertyWrapper.swift @@ -95,6 +95,7 @@ public struct UserDefaultsWrapper { case autofillFillDate = "com.duckduckgo.app.autofill.FillDate" case autofillOnboardedUser = "com.duckduckgo.app.autofill.OnboardedUser" case autofillSurveysCompleted = "com.duckduckgo.app.autofill.SurveysCompleted" + case autofillExtensionEnabled = "com.duckduckgo.app.autofill.ExtensionEnabled" case syncPromoBookmarksDismissed = "com.duckduckgo.app.sync.PromoBookmarksDismissed" case syncPromoPasswordsDismissed = "com.duckduckgo.app.sync.PromoPasswordsDismissed" diff --git a/Core/ios-config.json b/Core/ios-config.json index 188f9299e7..4e06aee6ff 100644 --- a/Core/ios-config.json +++ b/Core/ios-config.json @@ -1,15 +1,15 @@ { "readme": "https://github.com/duckduckgo/privacy-configuration", - "version": 1733157661102, + "version": 1734119755860, "features": { "adAttributionReporting": { - "state": "disabled", + "state": "enabled", "exceptions": [], - "minSupportedVersion": "7.145.0", + "minSupportedVersion": "7.146.0", "settings": { "includeToken": false }, - "hash": "1da4782852f0317459b4465d801eda1f" + "hash": "4c739e2de28deaaf9408f254041bf113" }, "adClickAttribution": { "readme": "https://help.duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/#3rd-party-tracker-loading-protection", @@ -70,25 +70,31 @@ "hash": "b813ade8472a097ffbd43a3331116fe1" }, "additionalCampaignPixelParams": { - "state": "disabled", + "state": "enabled", "exceptions": [], - "hash": "c292bb627849854515cebbded288ef5a" + "settings": { + "origins": [ + "funnel_pro_iosrmf_announcement" + ] + }, + "minSupportedVersion": "7.131.0", + "hash": "7616abe480cf4aecb67ffbaa71d17217" }, "aiChat": { - "state": "enabled", + "state": "disabled", "exceptions": [], "features": { "browsingToolbarShortcut": { - "state": "enabled" + "state": "disabled" }, "addressBarShortcut": { - "state": "enabled" + "state": "disabled" } }, "settings": { "aiChatURL": "https://duckduckgo.com/?q=DuckDuckGo+AI+Chat&ia=chat&duckai=4" }, - "hash": "14908e76cd3a8b4919e03003fd201300" + "hash": "4b5209f8cbe489299641ea30ed9a2542" }, "ampLinks": { "exceptions": [ @@ -156,6 +162,19 @@ "state": "disabled", "hash": "728493ef7a1488e4781656d3f9db84aa" }, + "apiManipulation": { + "state": "disabled", + "exceptions": [], + "hash": "c292bb627849854515cebbded288ef5a" + }, + "auraExperiment": { + "exceptions": [], + "settings": { + "packages": [] + }, + "state": "disabled", + "hash": "527fd2e156c7669f001516ee2d3d7c0e" + }, "autocompleteTabs": { "state": "enabled", "exceptions": [], @@ -601,6 +620,207 @@ { "domain": "outlet46.de" }, + { + "domain": "mytolino.de" + }, + { + "domain": "google.ae" + }, + { + "domain": "google.at" + }, + { + "domain": "google.be" + }, + { + "domain": "google.bg" + }, + { + "domain": "google.by" + }, + { + "domain": "google.ca" + }, + { + "domain": "google.ch" + }, + { + "domain": "google.cl" + }, + { + "domain": "google.co.id" + }, + { + "domain": "google.co.il" + }, + { + "domain": "google.co.in" + }, + { + "domain": "google.co.jp" + }, + { + "domain": "google.co.ke" + }, + { + "domain": "google.co.kr" + }, + { + "domain": "google.co.nz" + }, + { + "domain": "google.co.th" + }, + { + "domain": "google.co.uk" + }, + { + "domain": "google.co.ve" + }, + { + "domain": "google.co.za" + }, + { + "domain": "google.com" + }, + { + "domain": "google.com.ar" + }, + { + "domain": "google.com.au" + }, + { + "domain": "google.com.br" + }, + { + "domain": "google.com.co" + }, + { + "domain": "google.com.ec" + }, + { + "domain": "google.com.eg" + }, + { + "domain": "google.com.hk" + }, + { + "domain": "google.com.mx" + }, + { + "domain": "google.com.my" + }, + { + "domain": "google.com.pe" + }, + { + "domain": "google.com.ph" + }, + { + "domain": "google.com.pk" + }, + { + "domain": "google.com.py" + }, + { + "domain": "google.com.sa" + }, + { + "domain": "google.com.sg" + }, + { + "domain": "google.com.tr" + }, + { + "domain": "google.com.tw" + }, + { + "domain": "google.com.ua" + }, + { + "domain": "google.com.uy" + }, + { + "domain": "google.com.vn" + }, + { + "domain": "google.cz" + }, + { + "domain": "google.de" + }, + { + "domain": "google.dk" + }, + { + "domain": "google.dz" + }, + { + "domain": "google.ee" + }, + { + "domain": "google.es" + }, + { + "domain": "google.fi" + }, + { + "domain": "google.fr" + }, + { + "domain": "google.gr" + }, + { + "domain": "google.hr" + }, + { + "domain": "google.hu" + }, + { + "domain": "google.ie" + }, + { + "domain": "google.it" + }, + { + "domain": "google.lt" + }, + { + "domain": "google.lv" + }, + { + "domain": "google.nl" + }, + { + "domain": "google.no" + }, + { + "domain": "google.pl" + }, + { + "domain": "google.pt" + }, + { + "domain": "google.ro" + }, + { + "domain": "google.rs" + }, + { + "domain": "google.ru" + }, + { + "domain": "google.se" + }, + { + "domain": "google.sk" + }, + { + "domain": "serif.com" + }, + { + "domain": "wetransfer.com" + }, { "domain": "marvel.com" }, @@ -643,7 +863,7 @@ } } }, - "hash": "2c73b04e22812bf92bc2a859e7540c3a" + "hash": "7a26ec675103d4e0dd106a46950156d0" }, "autofillBreakageReporter": { "state": "enabled", @@ -724,9 +944,12 @@ } ] } + }, + "partialFormSaves": { + "state": "enabled" } }, - "hash": "28d4af98382248e184c4315bd49f4222" + "hash": "07b6b3bb0e6ddc4bf3dadbbbd0419478" }, "backgroundAgentPixelTest": { "state": "enabled", @@ -1638,6 +1861,9 @@ { "domain": "instructure.com" }, + { + "domain": "duckduckgo.com" + }, { "domain": "marvel.com" }, @@ -1655,7 +1881,7 @@ } ], "state": "disabled", - "hash": "fce0a9ccd7ae060d25e7debe4d8905fb" + "hash": "d04093e35c2ea53300a62df3a8a2abea" }, "customUserAgent": { "settings": { @@ -2773,6 +2999,15 @@ } ] }, + { + "domain": "chase.com", + "rules": [ + { + "selector": ".browserupdate", + "type": "hide" + } + ] + }, { "domain": "cleartax.in", "rules": [ @@ -2791,7 +3026,7 @@ }, { "selector": "[data-identity='billboard-ad']", - "type": "hide-empty" + "type": "hide" }, { "selector": "[data-identity='leaderboard-ad']", @@ -2994,6 +3229,23 @@ } ] }, + { + "domain": "e-chords.com", + "rules": [ + { + "selector": ".adscifra", + "type": "hide" + }, + { + "selector": ".banner", + "type": "hide-empty" + }, + { + "selector": "#banner", + "type": "hide-empty" + } + ] + }, { "domain": "ebay.com", "rules": [ @@ -3138,6 +3390,15 @@ } ] }, + { + "domain": "facebook.com", + "rules": [ + { + "selector": ".xnw9j1v:has(div > a[href='https://www.facebook.com/help/597429858389632/'])", + "type": "hide" + } + ] + }, { "domain": "fandom.com", "rules": [ @@ -3461,6 +3722,26 @@ "selector": "div:has(> iframe[src*='prid=19031780'])", "type": "hide" }, + { + "selector": "div:has(> iframe[src*='prid=19044580'])", + "type": "hide" + }, + { + "selector": "div:has(> iframe[src*='prid=19044578'])", + "type": "hide" + }, + { + "selector": "div:has(> iframe[src*='prid=19044538'])", + "type": "hide" + }, + { + "selector": "div:has(> iframe[src*='prid=19044687'])", + "type": "hide" + }, + { + "selector": "div:has(> iframe[src*='prid=19044636'])", + "type": "hide" + }, { "selector": "[aria-labelledby='promo-header']", "type": "hide" @@ -5210,7 +5491,7 @@ ] }, "state": "enabled", - "hash": "16e64ea4c926bdf7c865a8b7cbde1b2b" + "hash": "e0632ffb8c02bf14874c33ccd66ee99a" }, "exceptionHandler": { "exceptions": [ @@ -5613,6 +5894,9 @@ { "domain": "norton.com" }, + { + "domain": "madewell.com" + }, { "domain": "marvel.com" }, @@ -5638,7 +5922,7 @@ "privacy-test-pages.site" ] }, - "hash": "501bbc6471eb079cb27fa8a2a47467a5" + "hash": "cc0c0adfc06919bb973a7636b59b2430" }, "harmfulApis": { "settings": { @@ -5838,11 +6122,6 @@ "exceptions": [], "hash": "429cea8d27316dc62af04159ec7c42b5" }, - "loadingBarExp": { - "exceptions": [], - "state": "disabled", - "hash": "728493ef7a1488e4781656d3f9db84aa" - }, "maliciousSiteProtection": { "state": "internal", "exceptions": [], @@ -6052,7 +6331,7 @@ "minSupportedVersion": "7.136.0" }, "setAccessTokenCookieForSubscriptionDomains": { - "state": "disabled", + "state": "enabled", "rollout": { "steps": [ { @@ -6065,13 +6344,29 @@ "percent": 100 } ] - } + }, + "minSupportedVersion": "7.148.0" }, - "freeTrials": { - "state": "internal" + "privacyProFreeTrialJan25": { + "state": "internal", + "targets": [ + { + "localeCountry": "US" + } + ], + "cohorts": [ + { + "name": "control", + "weight": 1 + }, + { + "name": "treatment", + "weight": 1 + } + ] } }, - "hash": "c002a2f7b1299197657574c7328e8f88" + "hash": "c30e064d2aa8e0c9dd00c9ed051b98d3" }, "privacyProtectionsPopup": { "state": "disabled", @@ -7150,10 +7445,12 @@ "domains": [ "ah.nl", "applesfera.com", + "asuracomic.net", "goplay.be", "itsthevibe.com", "nytimes.com", "realmadrid.com", + "repretel.com", "rocketnews24.com", "solitaired.com", "stuff.co.nz", @@ -7171,6 +7468,7 @@ { "rule": "securepubads.g.doubleclick.net/pagead/ppub_config", "domains": [ + "repretel.com", "rocketnews24.com", "weather.com", "wunderground.com" @@ -8836,6 +9134,7 @@ { "rule": "a.pub.network/core/prebid-universal-creative.js", "domains": [ + "boingboing.net", "signupgenius.com", "smithsonianmag.com", "stockcharts.com", @@ -9719,7 +10018,7 @@ "domain": "instructure.com" } ], - "hash": "f5b235ed3c5c9afdd44edfa3464235cf" + "hash": "fe0934622a089920211c50aaf246954c" }, "trackingCookies1p": { "settings": { @@ -10021,6 +10320,26 @@ "exceptions": [], "state": "disabled", "hash": "728493ef7a1488e4781656d3f9db84aa" + }, + "experimentTest": { + "state": "enabled", + "exceptions": [], + "features": { + "experimentTestAA": { + "state": "enabled", + "cohorts": [ + { + "name": "control", + "weight": 1 + }, + { + "name": "treatment", + "weight": 1 + } + ] + } + }, + "hash": "0fe5af5a94e036bf5d78076df6dd771b" } }, "unprotectedTemporary": [] diff --git a/Core/trackerData.json b/Core/trackerData.json index b2123dd778..f09798f0e6 100644 --- a/Core/trackerData.json +++ b/Core/trackerData.json @@ -1,6 +1,6 @@ { "_builtWith": { - "tracker-radar": "e270978af9ffd0c909a62969df5f894128d41682920fb7a4c913e25e9ab1ecef-4013b4e91930c643394cb31c6c745356f133b04f", + "tracker-radar": "1108f03ca047465b2b8d8ab3385ba2cd4f21e1d0111571f8342b3eb0735b1c4a-4013b4e91930c643394cb31c6c745356f133b04f", "tracker-surrogates": "fe15c53e755af1257774459ecf066f673f485cb0" }, "readme": "https://github.com/duckduckgo/tracker-blocklists", @@ -6604,44 +6604,7 @@ "fingerprinting": 2, "cookies": 0.00112, "categories": [], - "default": "ignore", - "rules": [ - { - "rule": "coveo\\.com\\/coveo\\.analytics\\.js\\/2\\/coveoua\\.js", - "fingerprinting": 2, - "cookies": 0.000163 - }, - { - "rule": "coveo\\.com\\/coveo\\.analytics\\.js\\/1\\.0\\/coveoua\\.js", - "fingerprinting": 2, - "cookies": 0.0000613 - }, - { - "rule": "coveo\\.com\\/coveo\\.analytics\\.js\\/coveoua\\.js", - "fingerprinting": 1, - "cookies": 0.0000681 - }, - { - "rule": "coveo\\.com\\/coveo\\.analytics\\.js\\/latest\\/coveoua\\.js", - "fingerprinting": 1, - "cookies": 0.0000204 - }, - { - "rule": "coveo\\.com\\/atomic\\/v1\\.110\\/p-f4cb35d7\\.js", - "fingerprinting": 2, - "cookies": 0.0000136 - }, - { - "rule": "coveo\\.com\\/atomic\\/v1\\/p-217d4a01\\.js", - "fingerprinting": 2, - "cookies": 0 - }, - { - "rule": "coveo\\.com\\/coveo\\.analytics\\.js\\/2\\/coveoua\\.browser\\.js", - "fingerprinting": 2, - "cookies": 0.0000136 - } - ] + "default": "ignore" }, "cpmstar.com": { "domain": "cpmstar.com", @@ -8652,14 +8615,7 @@ "fingerprinting": 2, "cookies": 0.00114, "categories": [], - "default": "ignore", - "rules": [ - { - "rule": "elfsight\\.com\\/apps\\/social-share-buttons\\/release\\/bcbd1ca01fc572c7bc650c98dbe032152aa706f5\\/app\\/socialShareButtons\\.js", - "fingerprinting": 3, - "cookies": 0 - } - ] + "default": "ignore" }, "elitrack.com": { "domain": "elitrack.com", @@ -13283,7 +13239,27 @@ "default": "ignore", "rules": [ { - "rule": "impervadns\\.net//SP2013v1/Enterprise/JS/", + "rule": "impervadns\\.net\\/\\/SP2013v1\\/Enterprise\\/JS\\/foundation\\.min\\.js", + "fingerprinting": 1, + "cookies": 0.000136 + }, + { + "rule": "impervadns\\.net\\/\\/SP2013v1\\/Enterprise\\/JS\\/enterprise\\.min\\.js", + "fingerprinting": 1, + "cookies": 0.000136 + }, + { + "rule": "impervadns\\.net\\/\\/SP2013v1\\/Enterprise\\/JS\\/jquery\\.cookie\\.js", + "fingerprinting": 1, + "cookies": 0.000136 + }, + { + "rule": "impervadns\\.net\\/\\/SP2013v1\\/Enterprise\\/JS\\/knockout\\.min\\.js", + "fingerprinting": 1, + "cookies": 0.000136 + }, + { + "rule": "impervadns\\.net\\/\\/SP2013v1\\/Enterprise\\/JS\\/jquery-ui\\.js", "fingerprinting": 1, "cookies": 0.000136 }, @@ -15753,30 +15729,9 @@ { "rule": "listrakbi\\.com/v1/Product" }, - { - "rule": "listrakbi\\.com/json/07c34100-1080-4c55-9097-05c7f4a2b19b" - }, - { - "rule": "listrakbi\\.com/json/f5f04466-f923-491d-9354-679d54e32def" - }, - { - "rule": "listrakbi\\.com/json/eb9d26f3-d7e9-4df8-9800-a79cfaf345be" - }, { "rule": "listrakbi\\.com/SadvKLqKgI5T/cart/update" }, - { - "rule": "listrakbi\\.com/json/a6c8a6f0-379f-4e0e-8f1a-db4e3efa6123" - }, - { - "rule": "listrakbi\\.com/json/86d9476c-c670-4b5d-a33c-bca65c91576f" - }, - { - "rule": "listrakbi\\.com/json/043b15b6-cb62-4e61-9d8a-89856bdbf496" - }, - { - "rule": "listrakbi\\.com/json/9902c090-0d30-4673-b94b-3851cb8d6035" - }, { "rule": "listrakbi\\.com/RmR1Kqh2AYPr/cart/update" }, @@ -15797,6 +15752,9 @@ }, { "rule": "listrakbi\\.com/[a-zA-Z0-9]+\\.js" + }, + { + "rule": "listrakbi\\.com/json/.*" } ] }, @@ -21827,18 +21785,6 @@ { "rule": "qualtrics\\.com/WRSiteInterceptEngine/" }, - { - "rule": "qualtrics\\.com/WRQualtricsShared/Graphics/siteintercept/wr-dialog-close-btn-black\\.png" - }, - { - "rule": "qualtrics\\.com/WRQualtricsShared/Graphics/siteintercept/wr-dialog-close-btn-white\\.png" - }, - { - "rule": "qualtrics\\.com/WRQualtricsShared/Graphics/siteintercept/building_preview\\.gif" - }, - { - "rule": "qualtrics\\.com/WRQualtricsShared/Graphics/siteintercept/remove_screen_capture\\.png" - }, { "rule": "qualtrics\\.com/static/prototype-ui-modules/SharedGraphics/siteintercept/svg-close-btn-black-7\\.svg" }, @@ -21854,12 +21800,6 @@ { "rule": "qualtrics\\.com/static/prototype-ui-modules/SharedGraphics/siteintercept/svg-close-btn-black-4\\.svg" }, - { - "rule": "qualtrics\\.com/WRQualtricsShared/Graphics/siteintercept/popup_shadow_transparent\\.png" - }, - { - "rule": "qualtrics\\.com/WRQualtricsShared/Graphics/siteintercept/feedback-blue-right\\.png" - }, { "rule": "qualtrics\\.com/ControlPanel/Graphic\\.php" }, @@ -21874,6 +21814,9 @@ }, { "rule": "qualtrics\\.com/static/q-siteintercept/" + }, + { + "rule": "qualtrics\\.com/WRQualtricsShared/" } ] }, @@ -22243,6 +22186,18 @@ "cookies": 0.01, "default": "block" }, + "rebuyengine.com": { + "domain": "rebuyengine.com", + "owner": { + "name": "rebuyengine.com", + "displayName": "rebuyengine.com" + }, + "prevalence": 0.00142, + "fingerprinting": 2, + "cookies": 0.00125, + "categories": [], + "default": "ignore" + }, "reconditerake.com": { "domain": "reconditerake.com", "owner": { @@ -26891,30 +26846,12 @@ "categories": [], "default": "ignore", "rules": [ - { - "rule": "tiktok\\.com/i18n/pixel/events\\.js" - }, - { - "rule": "tiktok\\.com/i18n/pixel/static/identify_d1af3\\.js" - }, { "rule": "tiktok\\.com/api/v2/pixel" }, - { - "rule": "tiktok\\.com/i18n/pixel/sdk\\.js" - }, { "rule": "tiktok\\.com/api/v2/performance_interaction" }, - { - "rule": "tiktok\\.com/i18n/pixel/config\\.js" - }, - { - "rule": "tiktok\\.com/i18n/pixel/disable_cookie" - }, - { - "rule": "tiktok\\.com/i18n/pixel/static/identify_821f6\\.js" - }, { "rule": "tiktok\\.com/v1/user/webid" }, @@ -26934,13 +26871,7 @@ "rule": "tiktok\\.com/oembed" }, { - "rule": "tiktok\\.com/i18n/pixel/enable_cookie" - }, - { - "rule": "tiktok\\.com/i18n/pixel/static/identify_a7248\\.js" - }, - { - "rule": "tiktok\\.com/i18n/pixel/static/main\\..*\\.js" + "rule": "tiktok\\.com/i18n/pixel/" } ] }, @@ -32665,17 +32596,6 @@ "cookies": 0.01, "default": "block" }, - "acceptableauthority.com": { - "domain": "acceptableauthority.com", - "owner": { - "name": "Leven Labs, Inc. DBA Admiral (acceptableauthority.com)", - "displayName": "Admiral" - }, - "prevalence": 0.0107, - "fingerprinting": 1, - "cookies": 0.01, - "default": "block" - }, "accountsdoor.com": { "domain": "accountsdoor.com", "owner": { @@ -32951,17 +32871,6 @@ "cookies": 0.01, "default": "block" }, - "alertarithmetic.com": { - "domain": "alertarithmetic.com", - "owner": { - "name": "Leven Labs, Inc. DBA Admiral (alertarithmetic.com)", - "displayName": "Admiral" - }, - "prevalence": 0.0107, - "fingerprinting": 1, - "cookies": 0.01, - "default": "block" - }, "aliasanvil.com": { "domain": "aliasanvil.com", "owner": { @@ -35107,17 +35016,6 @@ "cookies": 0.01, "default": "block" }, - "concernedchange.com": { - "domain": "concernedchange.com", - "owner": { - "name": "Leven Labs, Inc. DBA Admiral (concernedchange.com)", - "displayName": "Admiral" - }, - "prevalence": 0.0107, - "fingerprinting": 1, - "cookies": 0.01, - "default": "block" - }, "concernedchickens.com": { "domain": "concernedchickens.com", "owner": { @@ -37516,17 +37414,6 @@ "cookies": 0.01, "default": "block" }, - "fertilefeeling.com": { - "domain": "fertilefeeling.com", - "owner": { - "name": "Leven Labs, Inc. DBA Admiral (fertilefeeling.com)", - "displayName": "Admiral" - }, - "prevalence": 0.0107, - "fingerprinting": 1, - "cookies": 0.01, - "default": "block" - }, "fewjuice.com": { "domain": "fewjuice.com", "owner": { @@ -38913,17 +38800,6 @@ "cookies": 0.01, "default": "block" }, - "hatefulrequest.com": { - "domain": "hatefulrequest.com", - "owner": { - "name": "Leven Labs, Inc. DBA Admiral (hatefulrequest.com)", - "displayName": "Admiral" - }, - "prevalence": 0.0107, - "fingerprinting": 1, - "cookies": 0.01, - "default": "block" - }, "headydegree.com": { "domain": "headydegree.com", "owner": { @@ -41454,17 +41330,6 @@ "cookies": 0.01, "default": "block" }, - "numberlessring.com": { - "domain": "numberlessring.com", - "owner": { - "name": "Leven Labs, Inc. DBA Admiral (numberlessring.com)", - "displayName": "Admiral" - }, - "prevalence": 0.0107, - "fingerprinting": 1, - "cookies": 0.01, - "default": "block" - }, "numerousnest.com": { "domain": "numerousnest.com", "owner": { @@ -42928,6 +42793,17 @@ "cookies": 0.01, "default": "block" }, + "razzweb.com": { + "domain": "razzweb.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral (razzweb.com)", + "displayName": "Admiral" + }, + "prevalence": 0.0107, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, "reactjspdf.com": { "domain": "reactjspdf.com", "owner": { @@ -43082,17 +42958,6 @@ "cookies": 0.01, "default": "block" }, - "reflectivestatement.com": { - "domain": "reflectivestatement.com", - "owner": { - "name": "Leven Labs, Inc. DBA Admiral (reflectivestatement.com)", - "displayName": "Admiral" - }, - "prevalence": 0.0107, - "fingerprinting": 1, - "cookies": 0.01, - "default": "block" - }, "refundradar.com": { "domain": "refundradar.com", "owner": { @@ -43500,6 +43365,17 @@ "cookies": 0.01, "default": "block" }, + "sableshelf.com": { + "domain": "sableshelf.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral (sableshelf.com)", + "displayName": "Admiral" + }, + "prevalence": 0.0107, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, "sablesmile.com": { "domain": "sablesmile.com", "owner": { @@ -43588,6 +43464,17 @@ "cookies": 0.01, "default": "block" }, + "samuraibots.com": { + "domain": "samuraibots.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral (samuraibots.com)", + "displayName": "Admiral" + }, + "prevalence": 0.0107, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, "sandstrophies.com": { "domain": "sandstrophies.com", "owner": { @@ -43841,17 +43728,6 @@ "cookies": 0.01, "default": "block" }, - "scribbleson.com": { - "domain": "scribbleson.com", - "owner": { - "name": "Leven Labs, Inc. DBA Admiral (scribbleson.com)", - "displayName": "Admiral" - }, - "prevalence": 0.0107, - "fingerprinting": 1, - "cookies": 0.01, - "default": "block" - }, "scrollservice.com": { "domain": "scrollservice.com", "owner": { @@ -43940,17 +43816,6 @@ "cookies": 0.01, "default": "block" }, - "seemlysuggestion.com": { - "domain": "seemlysuggestion.com", - "owner": { - "name": "Leven Labs, Inc. DBA Admiral (seemlysuggestion.com)", - "displayName": "Admiral" - }, - "prevalence": 0.0107, - "fingerprinting": 1, - "cookies": 0.01, - "default": "block" - }, "selfishsea.com": { "domain": "selfishsea.com", "owner": { @@ -44171,6 +44036,17 @@ "cookies": 0.01, "default": "block" }, + "shallowart.com": { + "domain": "shallowart.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral (shallowart.com)", + "displayName": "Admiral" + }, + "prevalence": 0.0107, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, "shallowblade.com": { "domain": "shallowblade.com", "owner": { @@ -44402,17 +44278,6 @@ "cookies": 0.01, "default": "block" }, - "sinkbooks.com": { - "domain": "sinkbooks.com", - "owner": { - "name": "Leven Labs, Inc. DBA Admiral (sinkbooks.com)", - "displayName": "Admiral" - }, - "prevalence": 0.0107, - "fingerprinting": 1, - "cookies": 0.01, - "default": "block" - }, "sixscissors.com": { "domain": "sixscissors.com", "owner": { @@ -45293,17 +45158,6 @@ "cookies": 0.01, "default": "block" }, - "steepscale.com": { - "domain": "steepscale.com", - "owner": { - "name": "Leven Labs, Inc. DBA Admiral (steepscale.com)", - "displayName": "Admiral" - }, - "prevalence": 0.0107, - "fingerprinting": 1, - "cookies": 0.01, - "default": "block" - }, "steepsister.com": { "domain": "steepsister.com", "owner": { @@ -45491,17 +45345,6 @@ "cookies": 0.01, "default": "block" }, - "strangersponge.com": { - "domain": "strangersponge.com", - "owner": { - "name": "Leven Labs, Inc. DBA Admiral (strangersponge.com)", - "displayName": "Admiral" - }, - "prevalence": 0.0107, - "fingerprinting": 1, - "cookies": 0.01, - "default": "block" - }, "strangesink.com": { "domain": "strangesink.com", "owner": { @@ -45678,17 +45521,6 @@ "cookies": 0.01, "default": "block" }, - "sugarfriction.com": { - "domain": "sugarfriction.com", - "owner": { - "name": "Leven Labs, Inc. DBA Admiral (sugarfriction.com)", - "displayName": "Admiral" - }, - "prevalence": 0.0107, - "fingerprinting": 1, - "cookies": 0.01, - "default": "block" - }, "suggestionbridge.com": { "domain": "suggestionbridge.com", "owner": { @@ -45777,17 +45609,6 @@ "cookies": 0.01, "default": "block" }, - "swelteringsleep.com": { - "domain": "swelteringsleep.com", - "owner": { - "name": "Leven Labs, Inc. DBA Admiral (swelteringsleep.com)", - "displayName": "Admiral" - }, - "prevalence": 0.0107, - "fingerprinting": 1, - "cookies": 0.01, - "default": "block" - }, "swimfreely.com": { "domain": "swimfreely.com", "owner": { @@ -45942,17 +45763,6 @@ "cookies": 0.01, "default": "block" }, - "tastelesstrucks.com": { - "domain": "tastelesstrucks.com", - "owner": { - "name": "Leven Labs, Inc. DBA Admiral (tastelesstrucks.com)", - "displayName": "Admiral" - }, - "prevalence": 0.0107, - "fingerprinting": 1, - "cookies": 0.01, - "default": "block" - }, "tastesnake.com": { "domain": "tastesnake.com", "owner": { @@ -47251,17 +47061,6 @@ "cookies": 0.01, "default": "block" }, - "voicelessvein.com": { - "domain": "voicelessvein.com", - "owner": { - "name": "Leven Labs, Inc. DBA Admiral (voicelessvein.com)", - "displayName": "Admiral" - }, - "prevalence": 0.0107, - "fingerprinting": 1, - "cookies": 0.01, - "default": "block" - }, "voidgoo.com": { "domain": "voidgoo.com", "owner": { @@ -47548,6 +47347,17 @@ "cookies": 0.01, "default": "block" }, + "wildwoodavenue.com": { + "domain": "wildwoodavenue.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral (wildwoodavenue.com)", + "displayName": "Admiral" + }, + "prevalence": 0.0107, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, "wirecomic.com": { "domain": "wirecomic.com", "owner": { @@ -55488,6 +55298,13 @@ "prevalence": 0.0136, "displayName": "Rating-Widget" }, + "rebuyengine.com": { + "domains": [ + "rebuyengine.com" + ], + "prevalence": 0, + "displayName": "rebuyengine.com" + }, "Recruitics LLC": { "domains": [ "recruitics.com" @@ -57949,13 +57766,6 @@ "prevalence": 0.0107, "displayName": "Admiral" }, - "Leven Labs, Inc. DBA Admiral (acceptableauthority.com)": { - "domains": [ - "acceptableauthority.com" - ], - "prevalence": 0.0107, - "displayName": "Admiral" - }, "Leven Labs, Inc. DBA Admiral (accountsdoor.com)": { "domains": [ "accountsdoor.com" @@ -58138,13 +57948,6 @@ "prevalence": 0.0107, "displayName": "Admiral" }, - "Leven Labs, Inc. DBA Admiral (alertarithmetic.com)": { - "domains": [ - "alertarithmetic.com" - ], - "prevalence": 0.0107, - "displayName": "Admiral" - }, "Leven Labs, Inc. DBA Admiral (aliasanvil.com)": { "domains": [ "aliasanvil.com" @@ -59755,13 +59558,6 @@ "prevalence": 0.0107, "displayName": "Admiral" }, - "Leven Labs, Inc. DBA Admiral (concernedchange.com)": { - "domains": [ - "concernedchange.com" - ], - "prevalence": 0.0107, - "displayName": "Admiral" - }, "Leven Labs, Inc. DBA Admiral (concernedchickens.com)": { "domains": [ "concernedchickens.com" @@ -61477,13 +61273,6 @@ "prevalence": 0.0107, "displayName": "Admiral" }, - "Leven Labs, Inc. DBA Admiral (fertilefeeling.com)": { - "domains": [ - "fertilefeeling.com" - ], - "prevalence": 0.0107, - "displayName": "Admiral" - }, "Leven Labs, Inc. DBA Admiral (fewjuice.com)": { "domains": [ "fewjuice.com" @@ -62520,13 +62309,6 @@ "prevalence": 0.0107, "displayName": "Admiral" }, - "Leven Labs, Inc. DBA Admiral (hatefulrequest.com)": { - "domains": [ - "hatefulrequest.com" - ], - "prevalence": 0.0107, - "displayName": "Admiral" - }, "Leven Labs, Inc. DBA Admiral (headydegree.com)": { "domains": [ "headydegree.com" @@ -64284,13 +64066,6 @@ "prevalence": 0.0107, "displayName": "Admiral" }, - "Leven Labs, Inc. DBA Admiral (numberlessring.com)": { - "domains": [ - "numberlessring.com" - ], - "prevalence": 0.0107, - "displayName": "Admiral" - }, "Leven Labs, Inc. DBA Admiral (numerousnest.com)": { "domains": [ "numerousnest.com" @@ -65418,6 +65193,13 @@ "prevalence": 0.0107, "displayName": "Admiral" }, + "Leven Labs, Inc. DBA Admiral (razzweb.com)": { + "domains": [ + "razzweb.com" + ], + "prevalence": 0.0107, + "displayName": "Admiral" + }, "Leven Labs, Inc. DBA Admiral (reactjspdf.com)": { "domains": [ "reactjspdf.com" @@ -65537,13 +65319,6 @@ "prevalence": 0.0107, "displayName": "Admiral" }, - "Leven Labs, Inc. DBA Admiral (reflectivestatement.com)": { - "domains": [ - "reflectivestatement.com" - ], - "prevalence": 0.0107, - "displayName": "Admiral" - }, "Leven Labs, Inc. DBA Admiral (refundradar.com)": { "domains": [ "refundradar.com" @@ -65866,6 +65641,13 @@ "prevalence": 0.0107, "displayName": "Admiral" }, + "Leven Labs, Inc. DBA Admiral (sableshelf.com)": { + "domains": [ + "sableshelf.com" + ], + "prevalence": 0.0107, + "displayName": "Admiral" + }, "Leven Labs, Inc. DBA Admiral (sablesmile.com)": { "domains": [ "sablesmile.com" @@ -65936,6 +65718,13 @@ "prevalence": 0.0107, "displayName": "Admiral" }, + "Leven Labs, Inc. DBA Admiral (samuraibots.com)": { + "domains": [ + "samuraibots.com" + ], + "prevalence": 0.0107, + "displayName": "Admiral" + }, "Leven Labs, Inc. DBA Admiral (sandstrophies.com)": { "domains": [ "sandstrophies.com" @@ -66153,13 +65942,6 @@ "prevalence": 0.0107, "displayName": "Admiral" }, - "Leven Labs, Inc. DBA Admiral (scribbleson.com)": { - "domains": [ - "scribbleson.com" - ], - "prevalence": 0.0107, - "displayName": "Admiral" - }, "Leven Labs, Inc. DBA Admiral (scribblestring.com)": { "domains": [ "scribblestring.com" @@ -66230,13 +66012,6 @@ "prevalence": 0.0107, "displayName": "Admiral" }, - "Leven Labs, Inc. DBA Admiral (seemlysuggestion.com)": { - "domains": [ - "seemlysuggestion.com" - ], - "prevalence": 0.0107, - "displayName": "Admiral" - }, "Leven Labs, Inc. DBA Admiral (selectivesummer.com)": { "domains": [ "selectivesummer.com" @@ -66398,6 +66173,13 @@ "prevalence": 0.0107, "displayName": "Admiral" }, + "Leven Labs, Inc. DBA Admiral (shallowart.com)": { + "domains": [ + "shallowart.com" + ], + "prevalence": 0.0107, + "displayName": "Admiral" + }, "Leven Labs, Inc. DBA Admiral (shallowblade.com)": { "domains": [ "shallowblade.com" @@ -66580,13 +66362,6 @@ "prevalence": 0.0107, "displayName": "Admiral" }, - "Leven Labs, Inc. DBA Admiral (sinkbooks.com)": { - "domains": [ - "sinkbooks.com" - ], - "prevalence": 0.0107, - "displayName": "Admiral" - }, "Leven Labs, Inc. DBA Admiral (sixauthority.com)": { "domains": [ "sixauthority.com" @@ -67266,13 +67041,6 @@ "prevalence": 0.0107, "displayName": "Admiral" }, - "Leven Labs, Inc. DBA Admiral (steepscale.com)": { - "domains": [ - "steepscale.com" - ], - "prevalence": 0.0107, - "displayName": "Admiral" - }, "Leven Labs, Inc. DBA Admiral (steepsister.com)": { "domains": [ "steepsister.com" @@ -67427,13 +67195,6 @@ "prevalence": 0.0107, "displayName": "Admiral" }, - "Leven Labs, Inc. DBA Admiral (strangersponge.com)": { - "domains": [ - "strangersponge.com" - ], - "prevalence": 0.0107, - "displayName": "Admiral" - }, "Leven Labs, Inc. DBA Admiral (strangesink.com)": { "domains": [ "strangesink.com" @@ -67581,13 +67342,6 @@ "prevalence": 0.0107, "displayName": "Admiral" }, - "Leven Labs, Inc. DBA Admiral (sugarfriction.com)": { - "domains": [ - "sugarfriction.com" - ], - "prevalence": 0.0107, - "displayName": "Admiral" - }, "Leven Labs, Inc. DBA Admiral (suggestionbridge.com)": { "domains": [ "suggestionbridge.com" @@ -67672,13 +67426,6 @@ "prevalence": 0.0107, "displayName": "Admiral" }, - "Leven Labs, Inc. DBA Admiral (swelteringsleep.com)": { - "domains": [ - "swelteringsleep.com" - ], - "prevalence": 0.0107, - "displayName": "Admiral" - }, "Leven Labs, Inc. DBA Admiral (swimfreely.com)": { "domains": [ "swimfreely.com" @@ -67784,13 +67531,6 @@ "prevalence": 0.0107, "displayName": "Admiral" }, - "Leven Labs, Inc. DBA Admiral (tastelesstrucks.com)": { - "domains": [ - "tastelesstrucks.com" - ], - "prevalence": 0.0107, - "displayName": "Admiral" - }, "Leven Labs, Inc. DBA Admiral (tastesnake.com)": { "domains": [ "tastesnake.com" @@ -68708,13 +68448,6 @@ "prevalence": 0.0107, "displayName": "Admiral" }, - "Leven Labs, Inc. DBA Admiral (voicelessvein.com)": { - "domains": [ - "voicelessvein.com" - ], - "prevalence": 0.0107, - "displayName": "Admiral" - }, "Leven Labs, Inc. DBA Admiral (voidgoo.com)": { "domains": [ "voidgoo.com" @@ -68925,6 +68658,13 @@ "prevalence": 0.0107, "displayName": "Admiral" }, + "Leven Labs, Inc. DBA Admiral (wildwoodavenue.com)": { + "domains": [ + "wildwoodavenue.com" + ], + "prevalence": 0.0107, + "displayName": "Admiral" + }, "Leven Labs, Inc. DBA Admiral (wirecomic.com)": { "domains": [ "wirecomic.com" @@ -69842,7 +69582,6 @@ "abstractedauthority.com": "Leven Labs, Inc. DBA Admiral (abstractedauthority.com)", "absurdapple.com": "Leven Labs, Inc. DBA Admiral (absurdapple.com)", "abundantcoin.com": "Leven Labs, Inc. DBA Admiral (abundantcoin.com)", - "acceptableauthority.com": "Leven Labs, Inc. DBA Admiral (acceptableauthority.com)", "accountsdoor.com": "Leven Labs, Inc. DBA Admiral (accountsdoor.com)", "accurateanimal.com": "Leven Labs, Inc. DBA Admiral (accurateanimal.com)", "accuratecoal.com": "Leven Labs, Inc. DBA Admiral (accuratecoal.com)", @@ -69869,7 +69608,6 @@ "aheadgrow.com": "Leven Labs, Inc. DBA Admiral (aheadgrow.com)", "aheadmachine.com": "Leven Labs, Inc. DBA Admiral (aheadmachine.com)", "ak0gsh40.com": "Leven Labs, Inc. DBA Admiral (ak0gsh40.com)", - "alertarithmetic.com": "Leven Labs, Inc. DBA Admiral (alertarithmetic.com)", "aliasanvil.com": "Leven Labs, Inc. DBA Admiral (aliasanvil.com)", "alikeaddition.com": "Leven Labs, Inc. DBA Admiral (alikeaddition.com)", "aliveachiever.com": "Leven Labs, Inc. DBA Admiral (aliveachiever.com)", @@ -70100,7 +69838,6 @@ "comfygoodness.com": "Leven Labs, Inc. DBA Admiral (comfygoodness.com)", "comparereaction.com": "Leven Labs, Inc. DBA Admiral (comparereaction.com)", "compiledoctor.com": "Leven Labs, Inc. DBA Admiral (compiledoctor.com)", - "concernedchange.com": "Leven Labs, Inc. DBA Admiral (concernedchange.com)", "concernedchickens.com": "Leven Labs, Inc. DBA Admiral (concernedchickens.com)", "condemnedcomb.com": "Leven Labs, Inc. DBA Admiral (condemnedcomb.com)", "conditionchange.com": "Leven Labs, Inc. DBA Admiral (conditionchange.com)", @@ -70346,7 +70083,6 @@ "feeblestamp.com": "Leven Labs, Inc. DBA Admiral (feeblestamp.com)", "feignedfaucet.com": "Leven Labs, Inc. DBA Admiral (feignedfaucet.com)", "fernwaycloud.com": "Leven Labs, Inc. DBA Admiral (fernwaycloud.com)", - "fertilefeeling.com": "Leven Labs, Inc. DBA Admiral (fertilefeeling.com)", "fewjuice.com": "Leven Labs, Inc. DBA Admiral (fewjuice.com)", "fewkittens.com": "Leven Labs, Inc. DBA Admiral (fewkittens.com)", "finalizeforce.com": "Leven Labs, Inc. DBA Admiral (finalizeforce.com)", @@ -70495,7 +70231,6 @@ "harborcub.com": "Leven Labs, Inc. DBA Admiral (harborcub.com)", "harmonicbamboo.com": "Leven Labs, Inc. DBA Admiral (harmonicbamboo.com)", "harmonywing.com": "Leven Labs, Inc. DBA Admiral (harmonywing.com)", - "hatefulrequest.com": "Leven Labs, Inc. DBA Admiral (hatefulrequest.com)", "headydegree.com": "Leven Labs, Inc. DBA Admiral (headydegree.com)", "headyhook.com": "Leven Labs, Inc. DBA Admiral (headyhook.com)", "healflowers.com": "Leven Labs, Inc. DBA Admiral (healflowers.com)", @@ -70747,7 +70482,6 @@ "notifyglass.com": "Leven Labs, Inc. DBA Admiral (notifyglass.com)", "nudgeduck.com": "Leven Labs, Inc. DBA Admiral (nudgeduck.com)", "nullnorth.com": "Leven Labs, Inc. DBA Admiral (nullnorth.com)", - "numberlessring.com": "Leven Labs, Inc. DBA Admiral (numberlessring.com)", "numerousnest.com": "Leven Labs, Inc. DBA Admiral (numerousnest.com)", "nutritiousbean.com": "Leven Labs, Inc. DBA Admiral (nutritiousbean.com)", "nuttyorganization.com": "Leven Labs, Inc. DBA Admiral (nuttyorganization.com)", @@ -70909,6 +70643,7 @@ "rangergustav.com": "Leven Labs, Inc. DBA Admiral (rangergustav.com)", "rarestcandy.com": "Leven Labs, Inc. DBA Admiral (rarestcandy.com)", "raresummer.com": "Leven Labs, Inc. DBA Admiral (raresummer.com)", + "razzweb.com": "Leven Labs, Inc. DBA Admiral (razzweb.com)", "reactjspdf.com": "Leven Labs, Inc. DBA Admiral (reactjspdf.com)", "readingguilt.com": "Leven Labs, Inc. DBA Admiral (readingguilt.com)", "readymoon.com": "Leven Labs, Inc. DBA Admiral (readymoon.com)", @@ -70926,7 +70661,6 @@ "reconditeprison.com": "Leven Labs, Inc. DBA Admiral (reconditeprison.com)", "reconditerake.com": "Leven Labs, Inc. DBA Admiral (reconditerake.com)", "reconditerespect.com": "Leven Labs, Inc. DBA Admiral (reconditerespect.com)", - "reflectivestatement.com": "Leven Labs, Inc. DBA Admiral (reflectivestatement.com)", "refundradar.com": "Leven Labs, Inc. DBA Admiral (refundradar.com)", "regexmail.com": "Leven Labs, Inc. DBA Admiral (regexmail.com)", "regularplants.com": "Leven Labs, Inc. DBA Admiral (regularplants.com)", @@ -70973,6 +70707,7 @@ "ruthlessdegree.com": "Leven Labs, Inc. DBA Admiral (ruthlessdegree.com)", "ruthlessmilk.com": "Leven Labs, Inc. DBA Admiral (ruthlessmilk.com)", "sableloss.com": "Leven Labs, Inc. DBA Admiral (sableloss.com)", + "sableshelf.com": "Leven Labs, Inc. DBA Admiral (sableshelf.com)", "sablesmile.com": "Leven Labs, Inc. DBA Admiral (sablesmile.com)", "sablesong.com": "Leven Labs, Inc. DBA Admiral (sablesong.com)", "sadloaf.com": "Leven Labs, Inc. DBA Admiral (sadloaf.com)", @@ -70983,6 +70718,7 @@ "samesticks.com": "Leven Labs, Inc. DBA Admiral (samesticks.com)", "samestretch.com": "Leven Labs, Inc. DBA Admiral (samestretch.com)", "samplesamba.com": "Leven Labs, Inc. DBA Admiral (samplesamba.com)", + "samuraibots.com": "Leven Labs, Inc. DBA Admiral (samuraibots.com)", "sandstrophies.com": "Leven Labs, Inc. DBA Admiral (sandstrophies.com)", "satisfycork.com": "Leven Labs, Inc. DBA Admiral (satisfycork.com)", "savoryorange.com": "Leven Labs, Inc. DBA Admiral (savoryorange.com)", @@ -71014,7 +70750,6 @@ "screechingfurniture.com": "Leven Labs, Inc. DBA Admiral (screechingfurniture.com)", "screechingstocking.com": "Leven Labs, Inc. DBA Admiral (screechingstocking.com)", "screechingstove.com": "Leven Labs, Inc. DBA Admiral (screechingstove.com)", - "scribbleson.com": "Leven Labs, Inc. DBA Admiral (scribbleson.com)", "scribblestring.com": "Leven Labs, Inc. DBA Admiral (scribblestring.com)", "scrollservice.com": "Leven Labs, Inc. DBA Admiral (scrollservice.com)", "scrubswim.com": "Leven Labs, Inc. DBA Admiral (scrubswim.com)", @@ -71025,7 +70760,6 @@ "secretspiders.com": "Leven Labs, Inc. DBA Admiral (secretspiders.com)", "secretturtle.com": "Leven Labs, Inc. DBA Admiral (secretturtle.com)", "seedscissors.com": "Leven Labs, Inc. DBA Admiral (seedscissors.com)", - "seemlysuggestion.com": "Leven Labs, Inc. DBA Admiral (seemlysuggestion.com)", "selectivesummer.com": "Leven Labs, Inc. DBA Admiral (selectivesummer.com)", "selfishsea.com": "Leven Labs, Inc. DBA Admiral (selfishsea.com)", "selfishsnake.com": "Leven Labs, Inc. DBA Admiral (selfishsnake.com)", @@ -71049,6 +70783,7 @@ "shakyseat.com": "Leven Labs, Inc. DBA Admiral (shakyseat.com)", "shakysurprise.com": "Leven Labs, Inc. DBA Admiral (shakysurprise.com)", "shakytaste.com": "Leven Labs, Inc. DBA Admiral (shakytaste.com)", + "shallowart.com": "Leven Labs, Inc. DBA Admiral (shallowart.com)", "shallowblade.com": "Leven Labs, Inc. DBA Admiral (shallowblade.com)", "shamerain.com": "Leven Labs, Inc. DBA Admiral (shamerain.com)", "shapecomb.com": "Leven Labs, Inc. DBA Admiral (shapecomb.com)", @@ -71075,7 +70810,6 @@ "sincerepelican.com": "Leven Labs, Inc. DBA Admiral (sincerepelican.com)", "sinceresubstance.com": "Leven Labs, Inc. DBA Admiral (sinceresubstance.com)", "singroot.com": "Leven Labs, Inc. DBA Admiral (singroot.com)", - "sinkbooks.com": "Leven Labs, Inc. DBA Admiral (sinkbooks.com)", "sixauthority.com": "Leven Labs, Inc. DBA Admiral (sixauthority.com)", "sixscissors.com": "Leven Labs, Inc. DBA Admiral (sixscissors.com)", "sizzlingsmoke.com": "Leven Labs, Inc. DBA Admiral (sizzlingsmoke.com)", @@ -71173,7 +70907,6 @@ "steadfastsystem.com": "Leven Labs, Inc. DBA Admiral (steadfastsystem.com)", "steadycopper.com": "Leven Labs, Inc. DBA Admiral (steadycopper.com)", "stealsteel.com": "Leven Labs, Inc. DBA Admiral (stealsteel.com)", - "steepscale.com": "Leven Labs, Inc. DBA Admiral (steepscale.com)", "steepsister.com": "Leven Labs, Inc. DBA Admiral (steepsister.com)", "steepsquirrel.com": "Leven Labs, Inc. DBA Admiral (steepsquirrel.com)", "stepcattle.com": "Leven Labs, Inc. DBA Admiral (stepcattle.com)", @@ -71196,7 +70929,6 @@ "stormyachiever.com": "Leven Labs, Inc. DBA Admiral (stormyachiever.com)", "straightnest.com": "Leven Labs, Inc. DBA Admiral (straightnest.com)", "strangeclocks.com": "Leven Labs, Inc. DBA Admiral (strangeclocks.com)", - "strangersponge.com": "Leven Labs, Inc. DBA Admiral (strangersponge.com)", "strangesink.com": "Leven Labs, Inc. DBA Admiral (strangesink.com)", "streetsort.com": "Leven Labs, Inc. DBA Admiral (streetsort.com)", "stretchsister.com": "Leven Labs, Inc. DBA Admiral (stretchsister.com)", @@ -71218,7 +70950,6 @@ "succeedscene.com": "Leven Labs, Inc. DBA Admiral (succeedscene.com)", "successfulscent.com": "Leven Labs, Inc. DBA Admiral (successfulscent.com)", "suddensoda.com": "Leven Labs, Inc. DBA Admiral (suddensoda.com)", - "sugarfriction.com": "Leven Labs, Inc. DBA Admiral (sugarfriction.com)", "suggestionbridge.com": "Leven Labs, Inc. DBA Admiral (suggestionbridge.com)", "sulkycook.com": "Leven Labs, Inc. DBA Admiral (sulkycook.com)", "summerobject.com": "Leven Labs, Inc. DBA Admiral (summerobject.com)", @@ -71231,7 +70962,6 @@ "suspectmark.com": "Leven Labs, Inc. DBA Admiral (suspectmark.com)", "swankysquare.com": "Leven Labs, Inc. DBA Admiral (swankysquare.com)", "swellstocking.com": "Leven Labs, Inc. DBA Admiral (swellstocking.com)", - "swelteringsleep.com": "Leven Labs, Inc. DBA Admiral (swelteringsleep.com)", "swimfreely.com": "Leven Labs, Inc. DBA Admiral (swimfreely.com)", "swingslip.com": "Leven Labs, Inc. DBA Admiral (swingslip.com)", "swordgoose.com": "Leven Labs, Inc. DBA Admiral (swordgoose.com)", @@ -71247,7 +70977,6 @@ "tangyamount.com": "Leven Labs, Inc. DBA Admiral (tangyamount.com)", "tangycover.com": "Leven Labs, Inc. DBA Admiral (tangycover.com)", "tastelesstrees.com": "Leven Labs, Inc. DBA Admiral (tastelesstrees.com)", - "tastelesstrucks.com": "Leven Labs, Inc. DBA Admiral (tastelesstrucks.com)", "tastesnake.com": "Leven Labs, Inc. DBA Admiral (tastesnake.com)", "tawdryson.com": "Leven Labs, Inc. DBA Admiral (tawdryson.com)", "tdzvm.pw": "Leven Labs, Inc. DBA Admiral (tdzvm.pw)", @@ -71379,7 +71108,6 @@ "vividfrost.com": "Leven Labs, Inc. DBA Admiral (vividfrost.com)", "vividmeadow.com": "Leven Labs, Inc. DBA Admiral (vividmeadow.com)", "vividplume.com": "Leven Labs, Inc. DBA Admiral (vividplume.com)", - "voicelessvein.com": "Leven Labs, Inc. DBA Admiral (voicelessvein.com)", "voidgoo.com": "Leven Labs, Inc. DBA Admiral (voidgoo.com)", "volatileprofit.com": "Leven Labs, Inc. DBA Admiral (volatileprofit.com)", "volatilevessel.com": "Leven Labs, Inc. DBA Admiral (volatilevessel.com)", @@ -71410,6 +71138,7 @@ "whisperingsummit.com": "Leven Labs, Inc. DBA Admiral (whisperingsummit.com)", "whispermeeting.com": "Leven Labs, Inc. DBA Admiral (whispermeeting.com)", "wildcommittee.com": "Leven Labs, Inc. DBA Admiral (wildcommittee.com)", + "wildwoodavenue.com": "Leven Labs, Inc. DBA Admiral (wildwoodavenue.com)", "wirecomic.com": "Leven Labs, Inc. DBA Admiral (wirecomic.com)", "wiredforcoffee.com": "Leven Labs, Inc. DBA Admiral (wiredforcoffee.com)", "wirypaste.com": "Leven Labs, Inc. DBA Admiral (wirypaste.com)", @@ -74914,6 +74643,7 @@ "secretmag.ru": "Rambler Internet Holding, LLC", "top100.ru": "Rambler Internet Holding, LLC", "rating-widget.com": "Rating-Widget, Inc.", + "rebuyengine.com": "rebuyengine.com", "recruitics.com": "Recruitics LLC", "dubsmash.com": "Reddit Inc.", "redd.it": "Reddit Inc.", diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index a3974aa725..991ba96441 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -137,6 +137,8 @@ 310D09212799FD1A00DC0060 /* MIMEType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 310D09202799FD1A00DC0060 /* MIMEType.swift */; }; 310E79BD2949CAA5007C49E8 /* FireButtonReferenceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 310E79BC2949CAA5007C49E8 /* FireButtonReferenceTests.swift */; }; 310ECFDD282A8BB0005029B3 /* EnableAutofillSettingsTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 310ECFDC282A8BB0005029B3 /* EnableAutofillSettingsTableViewCell.swift */; }; + 310EEA2F2CFFCDC60043CA1A /* AIChatSettingsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 310EEA2E2CFFCDBF0043CA1A /* AIChatSettingsTests.swift */; }; + 311711912D00E5500063AC3D /* OmnibarAccessoryHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 311711902D00E53A0063AC3D /* OmnibarAccessoryHandling.swift */; }; 311BD1AD2836BB3900AEF6C1 /* AutofillItemsEmptyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 311BD1AC2836BB3900AEF6C1 /* AutofillItemsEmptyView.swift */; }; 311BD1AF2836BB4200AEF6C1 /* AutofillItemsLockedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 311BD1AE2836BB4200AEF6C1 /* AutofillItemsLockedView.swift */; }; 311BD1B12836C0CA00AEF6C1 /* AutofillLoginListAuthenticator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 311BD1B02836C0CA00AEF6C1 /* AutofillLoginListAuthenticator.swift */; }; @@ -157,17 +159,21 @@ 3157B43827F4C8490042D3D7 /* FaviconsHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3157B43727F4C8490042D3D7 /* FaviconsHelper.swift */; }; 31584616281AFB46004ADB8B /* AutofillLoginDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31584615281AFB46004ADB8B /* AutofillLoginDetailsViewController.swift */; }; 3158461A281B08F5004ADB8B /* AutofillLoginListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31584619281B08F5004ADB8B /* AutofillLoginListViewModel.swift */; }; + 315C77822CFA41A400699683 /* AIChat in Frameworks */ = {isa = PBXBuildFile; productRef = 315C77812CFA41A400699683 /* AIChat */; }; 3161D13227AC161B00285CF6 /* DownloadMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3161D13127AC161B00285CF6 /* DownloadMetadata.swift */; }; 31669B9A28020A460071CC18 /* SaveLoginViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31669B9928020A460071CC18 /* SaveLoginViewModel.swift */; }; 316790E52C9352190090B0A2 /* MarketplaceAdPostbackManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 316790E42C9352190090B0A2 /* MarketplaceAdPostbackManagerTests.swift */; }; 316931D727BD10BB0095F5ED /* SaveToDownloadsAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 316931D627BD10BB0095F5ED /* SaveToDownloadsAlert.swift */; }; 316931D927BD22A80095F5ED /* DownloadActionMessageViewHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 316931D827BD22A80095F5ED /* DownloadActionMessageViewHelper.swift */; }; + 316AA45A2CF8E31F00A2ED28 /* AIChatSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 316AA4592CF8E31F00A2ED28 /* AIChatSettings.swift */; }; 3170048227A9504F00C03F35 /* DownloadMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3170048127A9504F00C03F35 /* DownloadMocks.swift */; }; 317045C02858C6B90016ED1F /* AutofillInterfaceEmailTruncatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 317045BF2858C6B90016ED1F /* AutofillInterfaceEmailTruncatorTests.swift */; }; + 317CA3432CFF82E100F88848 /* SettingsAIChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 317CA3422CFF82DB00F88848 /* SettingsAIChatView.swift */; }; + 317DF6082D01E7B900DE0145 /* RoundedPageSheetContainerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 317DF6072D01E7B900DE0145 /* RoundedPageSheetContainerViewController.swift */; }; + 317DF60B2D01E7D600DE0145 /* RoundedPageSheetPresentationAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 317DF60A2D01E7D600DE0145 /* RoundedPageSheetPresentationAnimator.swift */; }; 317F5F982C94A9EB0081666F /* MarketplaceAdPostbackStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 317F5F972C94A9EB0081666F /* MarketplaceAdPostbackStorage.swift */; }; 31860A5B2C57ED2D005561F5 /* DuckPlayerStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31860A5A2C57ED2D005561F5 /* DuckPlayerStorage.swift */; }; 31951E8E2823003200CAF535 /* AutofillLoginDetailsHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31951E8D2823003200CAF535 /* AutofillLoginDetailsHeaderView.swift */; }; - 319A371028299A850079FBCE /* PasswordHider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 319A370F28299A850079FBCE /* PasswordHider.swift */; }; 319A37152829A55F0079FBCE /* AutofillListItemTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 319A37142829A55F0079FBCE /* AutofillListItemTableViewCell.swift */; }; 319A37172829C8AD0079FBCE /* UITableViewExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 319A37162829C8AD0079FBCE /* UITableViewExtension.swift */; }; 31A42564285A09E800049386 /* FaviconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31A42563285A09E800049386 /* FaviconView.swift */; }; @@ -193,7 +199,7 @@ 31DE43C42C2C60E800F8C51F /* DuckPlayerModalPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31DE43C32C2C60E800F8C51F /* DuckPlayerModalPresenter.swift */; }; 31DE43C62C2DA70A00F8C51F /* DuckPlayer-ModalAnimation.json in Resources */ = {isa = PBXBuildFile; fileRef = 31DE43C52C2DA70A00F8C51F /* DuckPlayer-ModalAnimation.json */; }; 31E69A63280F4CB600478327 /* DuckUI in Frameworks */ = {isa = PBXBuildFile; productRef = 31E69A62280F4CB600478327 /* DuckUI */; }; - 31EF52E1281B3BDC0034796E /* AutofillLoginListItemViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31EF52E0281B3BDC0034796E /* AutofillLoginListItemViewModel.swift */; }; + 31E77B272D038BBC006F1C9F /* OmnibarAccessoryHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31E77B262D038BB9006F1C9F /* OmnibarAccessoryHandlerTests.swift */; }; 3712091E2C21E390003ADF3D /* RemoteMessagingStoreErrorHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3712091D2C21E390003ADF3D /* RemoteMessagingStoreErrorHandling.swift */; }; 372A0FF02B2389590033BF7F /* SyncMetricsEventsHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372A0FEF2B2389590033BF7F /* SyncMetricsEventsHandler.swift */; }; 373608902ABB1E6C00629E7F /* FavoritesDisplayModeStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3736088F2ABB1E6C00629E7F /* FavoritesDisplayModeStorage.swift */; }; @@ -284,6 +290,9 @@ 56D060262C359D2E003BAEB5 /* ContextualOnboardingDialogs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56D060252C359D2E003BAEB5 /* ContextualOnboardingDialogs.swift */; }; 56D060282C380D83003BAEB5 /* OnboardingSuggestedSearchesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56D060272C380D83003BAEB5 /* OnboardingSuggestedSearchesProvider.swift */; }; 56D0602D2C383FD2003BAEB5 /* OnboardingSuggestedSearchesProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56D0602C2C383FD2003BAEB5 /* OnboardingSuggestedSearchesProviderTests.swift */; }; + 56D7792C2CFF476800B619EF /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D664C7DC2B28A02800CBFA76 /* StoreKit.framework */; }; + 56D7793A2CFFC7E800B619EF /* PixelExperimentKit in Frameworks */ = {isa = PBXBuildFile; productRef = 56D779392CFFC7E800B619EF /* PixelExperimentKit */; }; + 56D7793C2CFFC7E800B619EF /* PixelKit in Frameworks */ = {isa = PBXBuildFile; productRef = 56D7793B2CFFC7E800B619EF /* PixelKit */; }; 56D8556A2BEA9169009F9698 /* CurrentDateProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56D855692BEA9169009F9698 /* CurrentDateProviding.swift */; }; 56D8556C2BEA91C4009F9698 /* SyncAlertsPresenting.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56D8556B2BEA91C4009F9698 /* SyncAlertsPresenting.swift */; }; 6AC6DAB328804F97002723C0 /* BarsAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AC6DAB228804F97002723C0 /* BarsAnimator.swift */; }; @@ -321,6 +330,7 @@ 6F7FB8E32C660BF300867DA7 /* DailyPixelFiring.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F7FB8E22C660BF300867DA7 /* DailyPixelFiring.swift */; }; 6F7FB8E52C66158D00867DA7 /* NewTabPageShortcutsSettingsModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F7FB8E42C66158D00867DA7 /* NewTabPageShortcutsSettingsModelTests.swift */; }; 6F7FB8E72C66197E00867DA7 /* NewTabPageSectionsSettingsModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F7FB8E62C66197E00867DA7 /* NewTabPageSectionsSettingsModelTests.swift */; }; + 6F8348E32D01E401005872E3 /* AlternateAppIcons.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6F8348E22D01E401005872E3 /* AlternateAppIcons.xcassets */; }; 6F8496412BC3D8EE00ADA54E /* OnboardingButtonsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F8496402BC3D8EE00ADA54E /* OnboardingButtonsView.swift */; }; 6F934F862C58DB00008364E4 /* NewTabPageSettingsPersistentStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F934F852C58DB00008364E4 /* NewTabPageSettingsPersistentStorageTests.swift */; }; 6F96FF102C2B128500162692 /* NewTabPageCustomizeButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F96FF0F2C2B128500162692 /* NewTabPageCustomizeButtonView.swift */; }; @@ -623,7 +633,6 @@ 981FED76220464EF008488D7 /* AutoClearSettingsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 981FED75220464EF008488D7 /* AutoClearSettingsModel.swift */; }; 9820EAF522613CD30089094D /* WebProgressWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9820EAF422613CD30089094D /* WebProgressWorker.swift */; }; 9820FF502244FECC008D4782 /* UIScrollViewExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9820FF4F2244FECC008D4782 /* UIScrollViewExtension.swift */; }; - 9821234E2B6D0A6300F08C57 /* UserAuthenticator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9821234D2B6D0A6300F08C57 /* UserAuthenticator.swift */; }; 982123502B6D233E00F08C57 /* UserSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9821234F2B6D233E00F08C57 /* UserSession.swift */; }; 9825F9DB293F2E8700F220F2 /* BookmarksTestData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9825F9DA293F2E8700F220F2 /* BookmarksTestData.swift */; }; 982686AD2600C0850011A8D6 /* ActionMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 982686AC2600C0850011A8D6 /* ActionMessageView.swift */; }; @@ -739,8 +748,6 @@ 98F3A1D8217B37010011A0D4 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98F3A1D7217B37010011A0D4 /* Theme.swift */; }; 98F6EA472863124100720957 /* ContentBlockerRulesLists.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98F6EA462863124100720957 /* ContentBlockerRulesLists.swift */; }; 98F78B8E22419093007CACF4 /* ThemableNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98F78B8D22419093007CACF4 /* ThemableNavigationController.swift */; }; - 9F1061652C9C013F008DD5A0 /* DefaultVariantManager+Onboarding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F1061642C9C013F008DD5A0 /* DefaultVariantManager+Onboarding.swift */; }; - 9F1623092C9D14F10093C4FC /* DefaultVariantManagerOnboardingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F1623082C9D14F10093C4FC /* DefaultVariantManagerOnboardingTests.swift */; }; 9F16230B2CA0F0190093C4FC /* DebouncerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F16230A2CA0F0190093C4FC /* DebouncerTests.swift */; }; 9F1798572CD2443F0073018B /* AddToDockPromoViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F1798562CD2443F0073018B /* AddToDockPromoViewModelTests.swift */; }; 9F23B8012C2BC94400950875 /* OnboardingBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F23B8002C2BC94400950875 /* OnboardingBackground.swift */; }; @@ -817,50 +824,6 @@ AA3D854723D9E88E00788410 /* AppIconSettingsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3D854623D9E88E00788410 /* AppIconSettingsCell.swift */; }; AA3D854923DA1DFB00788410 /* AppIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA3D854823DA1DFB00788410 /* AppIcon.swift */; }; AA4D6A6A23DB87B1007E8790 /* AppIconManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA4D6A6923DB87B1007E8790 /* AppIconManager.swift */; }; - AA4D6A8C23DE49A5007E8790 /* AppIconBlack40x40@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6A8223DE49A4007E8790 /* AppIconBlack40x40@2x.png */; }; - AA4D6A8D23DE49A5007E8790 /* AppIconBlack40x40@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6A8323DE49A4007E8790 /* AppIconBlack40x40@3x.png */; }; - AA4D6A8E23DE49A5007E8790 /* AppIconBlack60x60@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6A8423DE49A4007E8790 /* AppIconBlack60x60@2x.png */; }; - AA4D6A8F23DE49A5007E8790 /* AppIconBlack29x29@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6A8523DE49A4007E8790 /* AppIconBlack29x29@3x.png */; }; - AA4D6A9123DE49A5007E8790 /* AppIconBlack60x60@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6A8723DE49A5007E8790 /* AppIconBlack60x60@3x.png */; }; - AA4D6A9323DE49A5007E8790 /* AppIconBlack76x76@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6A8923DE49A5007E8790 /* AppIconBlack76x76@2x.png */; }; - AA4D6A9423DE49A5007E8790 /* AppIconBlack29x29@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6A8A23DE49A5007E8790 /* AppIconBlack29x29@2x.png */; }; - AA4D6AA123DE4CC4007E8790 /* AppIconBlue60x60@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6A9723DE4CC3007E8790 /* AppIconBlue60x60@3x.png */; }; - AA4D6AA223DE4CC4007E8790 /* AppIconBlue76x76@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6A9823DE4CC3007E8790 /* AppIconBlue76x76@2x.png */; }; - AA4D6AA323DE4CC4007E8790 /* AppIconBlue40x40@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6A9923DE4CC3007E8790 /* AppIconBlue40x40@3x.png */; }; - AA4D6AA423DE4CC4007E8790 /* AppIconBlue29x29@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6A9A23DE4CC3007E8790 /* AppIconBlue29x29@2x.png */; }; - AA4D6AA523DE4CC4007E8790 /* AppIconBlue29x29@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6A9B23DE4CC3007E8790 /* AppIconBlue29x29@3x.png */; }; - AA4D6AA723DE4CC4007E8790 /* AppIconBlue60x60@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6A9D23DE4CC4007E8790 /* AppIconBlue60x60@2x.png */; }; - AA4D6AA823DE4CC4007E8790 /* AppIconBlue40x40@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6A9E23DE4CC4007E8790 /* AppIconBlue40x40@2x.png */; }; - AA4D6AB823DE4D15007E8790 /* AppIconYellow29x29@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6AAE23DE4D14007E8790 /* AppIconYellow29x29@2x.png */; }; - AA4D6AB923DE4D15007E8790 /* AppIconYellow29x29@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6AAF23DE4D14007E8790 /* AppIconYellow29x29@3x.png */; }; - AA4D6ABB23DE4D15007E8790 /* AppIconYellow40x40@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6AB123DE4D14007E8790 /* AppIconYellow40x40@2x.png */; }; - AA4D6ABC23DE4D15007E8790 /* AppIconYellow60x60@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6AB223DE4D14007E8790 /* AppIconYellow60x60@3x.png */; }; - AA4D6ABD23DE4D15007E8790 /* AppIconYellow60x60@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6AB323DE4D15007E8790 /* AppIconYellow60x60@2x.png */; }; - AA4D6ABF23DE4D15007E8790 /* AppIconYellow40x40@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6AB523DE4D15007E8790 /* AppIconYellow40x40@3x.png */; }; - AA4D6AC023DE4D15007E8790 /* AppIconYellow76x76@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6AB623DE4D15007E8790 /* AppIconYellow76x76@2x.png */; }; - AA4D6ACC23DE4D27007E8790 /* AppIconPurple60x60@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6AC223DE4D26007E8790 /* AppIconPurple60x60@2x.png */; }; - AA4D6ACD23DE4D27007E8790 /* AppIconPurple29x29@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6AC323DE4D26007E8790 /* AppIconPurple29x29@3x.png */; }; - AA4D6ACE23DE4D27007E8790 /* AppIconPurple60x60@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6AC423DE4D26007E8790 /* AppIconPurple60x60@3x.png */; }; - AA4D6ACF23DE4D27007E8790 /* AppIconPurple76x76@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6AC523DE4D26007E8790 /* AppIconPurple76x76@2x.png */; }; - AA4D6AD123DE4D27007E8790 /* AppIconPurple40x40@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6AC723DE4D26007E8790 /* AppIconPurple40x40@2x.png */; }; - AA4D6AD323DE4D27007E8790 /* AppIconPurple29x29@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6AC923DE4D26007E8790 /* AppIconPurple29x29@2x.png */; }; - AA4D6AD423DE4D27007E8790 /* AppIconPurple40x40@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6ACA23DE4D26007E8790 /* AppIconPurple40x40@3x.png */; }; - AA4D6AE123DE4D33007E8790 /* AppIconGreen76x76@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6AD723DE4D32007E8790 /* AppIconGreen76x76@2x.png */; }; - AA4D6AE223DE4D33007E8790 /* AppIconGreen40x40@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6AD823DE4D32007E8790 /* AppIconGreen40x40@2x.png */; }; - AA4D6AE323DE4D33007E8790 /* AppIconGreen60x60@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6AD923DE4D32007E8790 /* AppIconGreen60x60@2x.png */; }; - AA4D6AE423DE4D33007E8790 /* AppIconGreen40x40@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6ADA23DE4D32007E8790 /* AppIconGreen40x40@3x.png */; }; - AA4D6AE623DE4D33007E8790 /* AppIconGreen60x60@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6ADC23DE4D33007E8790 /* AppIconGreen60x60@3x.png */; }; - AA4D6AE723DE4D33007E8790 /* AppIconGreen29x29@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6ADD23DE4D33007E8790 /* AppIconGreen29x29@2x.png */; }; - AA4D6AE923DE4D33007E8790 /* AppIconGreen29x29@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6ADF23DE4D33007E8790 /* AppIconGreen29x29@3x.png */; }; - AA4D6AF623DF0312007E8790 /* AppIconRed60x60@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6AF423DF0312007E8790 /* AppIconRed60x60@3x.png */; }; - AA4D6AF723DF0312007E8790 /* AppIconRed60x60@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6AF523DF0312007E8790 /* AppIconRed60x60@2x.png */; }; - AA4D6AFA23DF0CF6007E8790 /* AppIconRed29x29@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6AF823DF0CF5007E8790 /* AppIconRed29x29@3x.png */; }; - AA4D6AFB23DF0CF6007E8790 /* AppIconRed29x29@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AA4D6AF923DF0CF6007E8790 /* AppIconRed29x29@2x.png */; }; - AAF2E28123E0495400962AF8 /* AppIconBlack83.5x83.5@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AAF2E28023E0495400962AF8 /* AppIconBlack83.5x83.5@2x.png */; }; - AAF2E28323E0495E00962AF8 /* AppIconBlue83.5x83.5@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AAF2E28223E0495E00962AF8 /* AppIconBlue83.5x83.5@2x.png */; }; - AAF2E28523E0496F00962AF8 /* AppIconGreen83.5x83.5@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AAF2E28423E0496F00962AF8 /* AppIconGreen83.5x83.5@2x.png */; }; - AAF2E28723E0498200962AF8 /* AppIconPurple83.5x83.5@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AAF2E28623E0498100962AF8 /* AppIconPurple83.5x83.5@2x.png */; }; - AAF2E28B23E049DF00962AF8 /* AppIconYellow83.5x83.5@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = AAF2E28A23E049DF00962AF8 /* AppIconYellow83.5x83.5@2x.png */; }; B603974929C19F6F00902A34 /* Assertions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B603974829C19F6F00902A34 /* Assertions.swift */; }; B609D5522862EAFF0088CAC2 /* InlineWKDownloadDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B609D5512862EAFF0088CAC2 /* InlineWKDownloadDelegate.swift */; }; B60DFF072872B64B0061E7C2 /* JSAlertController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B60DFF062872B64B0061E7C2 /* JSAlertController.swift */; }; @@ -910,6 +873,7 @@ C12726F02A5FF89900215B02 /* EmailSignupPromptViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C12726EF2A5FF89900215B02 /* EmailSignupPromptViewModel.swift */; }; C12726F22A5FF8CB00215B02 /* EmailSignupPromptViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C12726F12A5FF8CB00215B02 /* EmailSignupPromptViewController.swift */; }; C13B32D22A0E750700A59236 /* AutofillSettingStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = C13B32D12A0E750700A59236 /* AutofillSettingStatus.swift */; }; + C13C076C2D00A6B7006386CF /* VaultCredentialManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C13C076B2D00A6B7006386CF /* VaultCredentialManager.swift */; }; C13F3F682B7F88100083BE40 /* AuthConfirmationPromptView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C13F3F672B7F88100083BE40 /* AuthConfirmationPromptView.swift */; }; C13F3F6A2B7F883A0083BE40 /* AuthConfirmationPromptViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C13F3F692B7F883A0083BE40 /* AuthConfirmationPromptViewController.swift */; }; C13F3F6C2B7F88470083BE40 /* AuthConfirmationPromptViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C13F3F6B2B7F88470083BE40 /* AuthConfirmationPromptViewModel.swift */; }; @@ -930,6 +894,7 @@ C1641EB12BC2F52B0012607A /* ImportPasswordsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1641EB02BC2F52B0012607A /* ImportPasswordsView.swift */; }; C1641EB32BC2F53C0012607A /* ImportPasswordsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1641EB22BC2F53C0012607A /* ImportPasswordsViewModel.swift */; }; C174CE602BD6A6CE00AED2EA /* MockDDGSyncing.swift in Sources */ = {isa = PBXBuildFile; fileRef = C185ED652BD43A5500BAE9DC /* MockDDGSyncing.swift */; }; + C177D9F62CFDDFEB0039CBF7 /* UIAlertControllerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C177D9F52CFDDFEB0039CBF7 /* UIAlertControllerExtension.swift */; }; C17B59592A03AAD30055F2D1 /* PasswordGenerationPromptViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C17B59562A03AAD30055F2D1 /* PasswordGenerationPromptViewModel.swift */; }; C17B595A2A03AAD30055F2D1 /* PasswordGenerationPromptViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C17B59572A03AAD30055F2D1 /* PasswordGenerationPromptViewController.swift */; }; C17B595B2A03AAD30055F2D1 /* PasswordGenerationPromptView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C17B59582A03AAD30055F2D1 /* PasswordGenerationPromptView.swift */; }; @@ -944,6 +909,7 @@ C1935A222C89CA9F001AD72D /* AutofillSurveyManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1935A212C89CA9F001AD72D /* AutofillSurveyManagerTests.swift */; }; C1935A242C89CC6D001AD72D /* AutofillHeaderViewFactoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1935A232C89CC6D001AD72D /* AutofillHeaderViewFactoryTests.swift */; }; C1963863283794A000298D4D /* BookmarksCachingSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1963862283794A000298D4D /* BookmarksCachingSearch.swift */; }; + C19D90D12CFE3A7F00D17DF3 /* AutofillLoginListSectionType.swift in Sources */ = {isa = PBXBuildFile; fileRef = C19D90D02CFE3A7F00D17DF3 /* AutofillLoginListSectionType.swift */; }; C1B7B51C28941E980098FD6A /* HomeMessageViewModelBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1B7B51B28941E980098FD6A /* HomeMessageViewModelBuilder.swift */; }; C1B7B52528941F2A0098FD6A /* RemoteMessagingClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1B7B52128941F2A0098FD6A /* RemoteMessagingClient.swift */; }; C1B7B52D2894469D0098FD6A /* DefaultVariantManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1B7B52C2894469D0098FD6A /* DefaultVariantManager.swift */; }; @@ -952,13 +918,48 @@ C1BF0BA529B63D7200482B73 /* AutofillLoginPromptHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1BF0BA429B63D7200482B73 /* AutofillLoginPromptHelper.swift */; }; C1BF0BA929B63E2200482B73 /* AutofillLoginPromptViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1BF0BA729B63E1A00482B73 /* AutofillLoginPromptViewModelTests.swift */; }; C1BF26152C74D10F00F6405E /* SyncPromoManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1BF26142C74D10F00F6405E /* SyncPromoManager.swift */; }; + C1C1FF452D085A280017ACCE /* CredentialProviderListDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1C1FF412D085A280017ACCE /* CredentialProviderListDetailsView.swift */; }; + C1C1FF462D085A280017ACCE /* CredentialProviderListDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1C1FF422D085A280017ACCE /* CredentialProviderListDetailsViewController.swift */; }; + C1C1FF472D085A280017ACCE /* CredentialProviderListDetailsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1C1FF432D085A280017ACCE /* CredentialProviderListDetailsViewModel.swift */; }; + C1C1FF4A2D085A4C0017ACCE /* AutofillInterfaceEmailTruncator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1C1FF482D085A4C0017ACCE /* AutofillInterfaceEmailTruncator.swift */; }; + C1C1FF4B2D085A4C0017ACCE /* PasswordHider.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1C1FF492D085A4C0017ACCE /* PasswordHider.swift */; }; + C1C1FF502D085AD70017ACCE /* ActionMessageView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C1C1FF4D2D085AD70017ACCE /* ActionMessageView.xib */; }; + C1C1FF512D085AD70017ACCE /* FaviconHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1C1FF4E2D085AD70017ACCE /* FaviconHelper.swift */; }; + C1C1FF522D085AD70017ACCE /* ActionMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1C1FF4C2D085AD70017ACCE /* ActionMessageView.swift */; }; + C1C1FF532D085AD70017ACCE /* NibLoading.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1C1FF4F2D085AD70017ACCE /* NibLoading.swift */; }; + C1CAAA6A2CF8BABF00C37EE6 /* CredentialProviderActivatedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1CAAA692CF8BABF00C37EE6 /* CredentialProviderActivatedView.swift */; }; + C1CAAA712CF8BC0B00C37EE6 /* UIViewControllerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1CAAA702CF8BC0B00C37EE6 /* UIViewControllerExtension.swift */; }; + C1CAAA732CF8BD1C00C37EE6 /* CredentialProviderActivatedViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1CAAA722CF8BD1C00C37EE6 /* CredentialProviderActivatedViewModel.swift */; }; + C1CAAA782CF8BDF200C37EE6 /* UserText.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1CAAA772CF8BDF200C37EE6 /* UserText.swift */; }; + C1CAAA7A2CF8BE0200C37EE6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C1CAAA792CF8BE0200C37EE6 /* Assets.xcassets */; }; + C1CAAA7B2CF8C8ED00C37EE6 /* Core.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F143C2E41E4A4CD400CFDE3A /* Core.framework */; }; + C1CAAA812CF8C8F400C37EE6 /* DuckUI in Frameworks */ = {isa = PBXBuildFile; productRef = C1CAAA802CF8C8F400C37EE6 /* DuckUI */; }; + C1CAAA832CF8C8FF00C37EE6 /* DesignResourcesKit in Frameworks */ = {isa = PBXBuildFile; productRef = C1CAAA822CF8C8FF00C37EE6 /* DesignResourcesKit */; }; + C1CAAA852CF8C9EA00C37EE6 /* UIResponderExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1CAAA842CF8C9EA00C37EE6 /* UIResponderExtension.swift */; }; + C1CAAA882CF9FFE100C37EE6 /* CredentialProviderListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1CAAA872CF9FFE100C37EE6 /* CredentialProviderListViewController.swift */; }; + C1CAAA8A2CF9FFF300C37EE6 /* CredentialProviderListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1CAAA892CF9FFF300C37EE6 /* CredentialProviderListViewModel.swift */; }; + C1CAAA922CFCAA0500C37EE6 /* CredentialProviderListItemTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1CAAA912CFCAA0500C37EE6 /* CredentialProviderListItemTableViewCell.swift */; }; + C1CAAA9A2CFCAD3E00C37EE6 /* AutofillLoginItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1CAAA992CFCAD3E00C37EE6 /* AutofillLoginItem.swift */; }; + C1CAAA9C2CFCB39800C37EE6 /* AutofillLoginListSorting.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1CAAA9B2CFCB39800C37EE6 /* AutofillLoginListSorting.swift */; }; + C1CAAA9E2CFCB78700C37EE6 /* UIColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1CAAA9D2CFCB78700C37EE6 /* UIColorExtension.swift */; }; + C1CAAAA02CFCB7C200C37EE6 /* UImageExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1CAAA9F2CFCB7C200C37EE6 /* UImageExtension.swift */; }; + C1CAAAA32CFCBBBD00C37EE6 /* UserAuthenticator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1CAAAA22CFCBBBD00C37EE6 /* UserAuthenticator.swift */; }; + C1CAAAA62CFCBD7900C37EE6 /* LockScreenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1CAAAA52CFCBD7900C37EE6 /* LockScreenView.swift */; }; + C1CAAAA82CFCBE4800C37EE6 /* EmptySearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1CAAAA72CFCBE4800C37EE6 /* EmptySearchView.swift */; }; + C1CAAAAA2CFCC13E00C37EE6 /* SecureVaultReporter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1CAAAA92CFCC13E00C37EE6 /* SecureVaultReporter.swift */; }; + C1CAAAAC2CFCC91D00C37EE6 /* EmptyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1CAAAAB2CFCC91D00C37EE6 /* EmptyView.swift */; }; C1CDA3162AFB9C7F006D1476 /* AutofillNeverPromptWebsitesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1CDA3152AFB9C7F006D1476 /* AutofillNeverPromptWebsitesManager.swift */; }; C1CDA31E2AFBF811006D1476 /* AutofillNeverPromptWebsitesManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1CDA31D2AFBF811006D1476 /* AutofillNeverPromptWebsitesManagerTests.swift */; }; C1D21E2D293A5965006E5A05 /* AutofillLoginSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1D21E2C293A5965006E5A05 /* AutofillLoginSession.swift */; }; C1D21E2F293A599C006E5A05 /* AutofillLoginSessionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1D21E2E293A599C006E5A05 /* AutofillLoginSessionTests.swift */; }; C1E42C7B2C5CD8AE00509204 /* AutofillCredentialsDebugViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1E42C7A2C5CD8AD00509204 /* AutofillCredentialsDebugViewController.swift */; }; + C1E4E9A62D0861AD00AA39AF /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = C1E4E9A42D0861AD00AA39AF /* InfoPlist.strings */; }; + C1E4E9A92D0861AD00AA39AF /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = C1E4E9A72D0861AD00AA39AF /* Localizable.strings */; }; C1EA86602C74CB6C00E8604D /* SyncPromoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1EA865F2C74CB6C00E8604D /* SyncPromoView.swift */; }; C1EA86622C74CB8B00E8604D /* SyncPromoViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1EA86612C74CB8B00E8604D /* SyncPromoViewModel.swift */; }; + C1EF5B232CC0457B002980E6 /* AuthenticationServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C1EF5B222CC0457B002980E6 /* AuthenticationServices.framework */; }; + C1EF5B262CC0457B002980E6 /* CredentialProviderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1EF5B252CC0457B002980E6 /* CredentialProviderViewController.swift */; }; + C1EF5B2E2CC0457B002980E6 /* AutofillCredentialProvider.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = C1EF5B212CC0457B002980E6 /* AutofillCredentialProvider.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; C1F341C52A6924000032057B /* EmailAddressPromptView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1F341C42A6924000032057B /* EmailAddressPromptView.swift */; }; C1F341C72A6924100032057B /* EmailAddressPromptViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1F341C62A6924100032057B /* EmailAddressPromptViewModel.swift */; }; C1F341C92A6926920032057B /* EmailAddressPromptViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1F341C82A6926920032057B /* EmailAddressPromptViewController.swift */; }; @@ -973,6 +974,7 @@ CB2A7EEF283D185100885F67 /* RulesCompilationMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB2A7EEE283D185100885F67 /* RulesCompilationMonitor.swift */; }; CB2A7EF128410DF700885F67 /* PixelEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB2A7EF028410DF700885F67 /* PixelEvent.swift */; }; CB2A7EF4285383B300885F67 /* AppLastCompiledRulesStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB2A7EF3285383B300885F67 /* AppLastCompiledRulesStore.swift */; }; + CB3C78912D08484800A7E4ED /* InactiveBackground.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB3C78902D08483F00A7E4ED /* InactiveBackground.swift */; }; CB48D3332B90CE9F00631D8B /* PageRefreshStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB48D3312B90CE9F00631D8B /* PageRefreshStore.swift */; }; CB4FA44E2C78AACE00A16F5A /* SpecialErrorPageUserScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB4FA44D2C78AACE00A16F5A /* SpecialErrorPageUserScript.swift */; }; CB5516D0286500290079B175 /* TrackerRadarIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85519124247468580010FDD0 /* TrackerRadarIntegrationTests.swift */; }; @@ -1039,7 +1041,6 @@ D664C7C92B289AA200CBFA76 /* AsyncHeadlessWebView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D664C7AF2B289AA000CBFA76 /* AsyncHeadlessWebView.swift */; }; D664C7CC2B289AA200CBFA76 /* SubscriptionPagesUserScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = D664C7B32B289AA000CBFA76 /* SubscriptionPagesUserScript.swift */; }; D664C7CE2B289AA200CBFA76 /* SubscriptionPagesUseSubscriptionFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = D664C7B52B289AA000CBFA76 /* SubscriptionPagesUseSubscriptionFeature.swift */; }; - D664C7DD2B28A02800CBFA76 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D664C7DC2B28A02800CBFA76 /* StoreKit.framework */; }; D668D9252B693778008E2FF2 /* SubscriptionITPView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D668D9242B693778008E2FF2 /* SubscriptionITPView.swift */; }; D668D9272B6937D2008E2FF2 /* SubscriptionITPViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D668D9262B6937D2008E2FF2 /* SubscriptionITPViewModel.swift */; }; D668D9292B69681C008E2FF2 /* IdentityTheftRestorationPagesUserScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = D668D9282B69681C008E2FF2 /* IdentityTheftRestorationPagesUserScript.swift */; }; @@ -1055,7 +1056,6 @@ D6ACEA322BBD55BF008FADDF /* TabURLInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6ACEA312BBD55BF008FADDF /* TabURLInterceptor.swift */; }; D6B67A122C332B6E002122EB /* DuckPlayerMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B67A112C332B6E002122EB /* DuckPlayerMocks.swift */; }; D6B9E8D22CDA4420002B640C /* DuckPlayerOverlayUsagePixels.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B9E8D12CDA4418002B640C /* DuckPlayerOverlayUsagePixels.swift */; }; - D6B9E8D42CDA8375002B640C /* DuckPlayerOverlayUsagePixelsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B9E8D32CDA8369002B640C /* DuckPlayerOverlayUsagePixelsTests.swift */; }; D6BC8ACB2C5AA3860025375B /* DuckPlayer in Frameworks */ = {isa = PBXBuildFile; productRef = D6BC8ACA2C5AA3860025375B /* DuckPlayer */; }; D6BFCB5F2B7524AA0051FF81 /* SubscriptionPIRView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6BFCB5E2B7524AA0051FF81 /* SubscriptionPIRView.swift */; }; D6BFCB612B7525160051FF81 /* SubscriptionPIRViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6BFCB602B7525160051FF81 /* SubscriptionPIRViewModel.swift */; }; @@ -1215,7 +1215,6 @@ F486D3382506A225002D07D7 /* OHHTTPStubsSwift in Frameworks */ = {isa = PBXBuildFile; productRef = F486D3372506A225002D07D7 /* OHHTTPStubsSwift */; }; F4B0B78C252CAFF700830156 /* OnboardingWidgetsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4B0B78B252CAFF700830156 /* OnboardingWidgetsViewController.swift */; }; F4B0B796252CB35700830156 /* OnboardingWidgetsDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4B0B795252CB35700830156 /* OnboardingWidgetsDetailsViewController.swift */; }; - F4C9FBF528340DDA002281CC /* AutofillInterfaceEmailTruncator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4C9FBF428340DDA002281CC /* AutofillInterfaceEmailTruncator.swift */; }; F4CE6D1B257EA33C00D0A6AA /* FireButtonAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4CE6D1A257EA33C00D0A6AA /* FireButtonAnimator.swift */; }; F4D7221026F29A70007D6193 /* BookmarkDetailsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4D7220F26F29A70007D6193 /* BookmarkDetailsCell.swift */; }; F4D7F634298C00C3006C3AE9 /* FindInPageIOSJSSupport in Frameworks */ = {isa = PBXBuildFile; productRef = F4D7F633298C00C3006C3AE9 /* FindInPageIOSJSSupport */; }; @@ -1323,6 +1322,20 @@ remoteGlobalIDString = B6DFE6CE2BC7E47500A9CE59; remoteInfo = SwiftLintToolBundle; }; + C1CAAA7D2CF8C8ED00C37EE6 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 84E3418A1E2F7EFB00BDBA6F /* Project object */; + proxyType = 1; + remoteGlobalIDString = F143C2E31E4A4CD400CFDE3A; + remoteInfo = Core; + }; + C1EF5B2C2CC0457B002980E6 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 84E3418A1E2F7EFB00BDBA6F /* Project object */; + proxyType = 1; + remoteGlobalIDString = C1EF5B202CC0457B002980E6; + remoteInfo = AutofillCredentialProvider; + }; F143C2E91E4A4CD400CFDE3A /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 84E3418A1E2F7EFB00BDBA6F /* Project object */; @@ -1339,6 +1352,7 @@ dstPath = ""; dstSubfolderSpec = 13; files = ( + C1EF5B2E2CC0457B002980E6 /* AutofillCredentialProvider.appex in Embed App Extensions */, 85482D942462DCD100EDEDD1 /* OpenAction.appex in Embed App Extensions */, 8512EA5D24ED30D30073EE19 /* WidgetsExtension.appex in Embed App Extensions */, 8390447620BDCE10006461CD /* ShareExtension.appex in Embed App Extensions */, @@ -1498,6 +1512,8 @@ 310D09202799FD1A00DC0060 /* MIMEType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MIMEType.swift; sourceTree = ""; }; 310E79BC2949CAA5007C49E8 /* FireButtonReferenceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FireButtonReferenceTests.swift; sourceTree = ""; }; 310ECFDC282A8BB0005029B3 /* EnableAutofillSettingsTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnableAutofillSettingsTableViewCell.swift; sourceTree = ""; }; + 310EEA2E2CFFCDBF0043CA1A /* AIChatSettingsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AIChatSettingsTests.swift; sourceTree = ""; }; + 311711902D00E53A0063AC3D /* OmnibarAccessoryHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OmnibarAccessoryHandling.swift; sourceTree = ""; }; 311BD1AC2836BB3900AEF6C1 /* AutofillItemsEmptyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillItemsEmptyView.swift; sourceTree = ""; }; 311BD1AE2836BB4200AEF6C1 /* AutofillItemsLockedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillItemsLockedView.swift; sourceTree = ""; }; 311BD1B02836C0CA00AEF6C1 /* AutofillLoginListAuthenticator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillLoginListAuthenticator.swift; sourceTree = ""; }; @@ -1518,18 +1534,22 @@ 3157B43727F4C8490042D3D7 /* FaviconsHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaviconsHelper.swift; sourceTree = ""; }; 31584615281AFB46004ADB8B /* AutofillLoginDetailsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillLoginDetailsViewController.swift; sourceTree = ""; }; 31584619281B08F5004ADB8B /* AutofillLoginListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillLoginListViewModel.swift; sourceTree = ""; }; + 315C77802CFA414400699683 /* AIChat */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = AIChat; sourceTree = ""; }; 3161D13127AC161B00285CF6 /* DownloadMetadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadMetadata.swift; sourceTree = ""; }; 31669B9928020A460071CC18 /* SaveLoginViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveLoginViewModel.swift; sourceTree = ""; }; 316790E42C9352190090B0A2 /* MarketplaceAdPostbackManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarketplaceAdPostbackManagerTests.swift; sourceTree = ""; }; 316931D627BD10BB0095F5ED /* SaveToDownloadsAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveToDownloadsAlert.swift; sourceTree = ""; }; 316931D827BD22A80095F5ED /* DownloadActionMessageViewHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadActionMessageViewHelper.swift; sourceTree = ""; }; + 316AA4592CF8E31F00A2ED28 /* AIChatSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AIChatSettings.swift; sourceTree = ""; }; 3170048127A9504F00C03F35 /* DownloadMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadMocks.swift; sourceTree = ""; }; 317045BF2858C6B90016ED1F /* AutofillInterfaceEmailTruncatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillInterfaceEmailTruncatorTests.swift; sourceTree = ""; }; 31794BFF2821DFB600F18633 /* DuckUI */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = DuckUI; sourceTree = ""; }; + 317CA3422CFF82DB00F88848 /* SettingsAIChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsAIChatView.swift; sourceTree = ""; }; + 317DF6072D01E7B900DE0145 /* RoundedPageSheetContainerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedPageSheetContainerViewController.swift; sourceTree = ""; }; + 317DF60A2D01E7D600DE0145 /* RoundedPageSheetPresentationAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedPageSheetPresentationAnimator.swift; sourceTree = ""; }; 317F5F972C94A9EB0081666F /* MarketplaceAdPostbackStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarketplaceAdPostbackStorage.swift; sourceTree = ""; }; 31860A5A2C57ED2D005561F5 /* DuckPlayerStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DuckPlayerStorage.swift; sourceTree = ""; }; 31951E8D2823003200CAF535 /* AutofillLoginDetailsHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillLoginDetailsHeaderView.swift; sourceTree = ""; }; - 319A370F28299A850079FBCE /* PasswordHider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordHider.swift; sourceTree = ""; }; 319A37142829A55F0079FBCE /* AutofillListItemTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillListItemTableViewCell.swift; sourceTree = ""; }; 319A37162829C8AD0079FBCE /* UITableViewExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITableViewExtension.swift; sourceTree = ""; }; 31A42563285A09E800049386 /* FaviconView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaviconView.swift; sourceTree = ""; }; @@ -1554,7 +1574,7 @@ 31DE43C12C2C480D00F8C51F /* DuckPlayerFeaturePresentationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DuckPlayerFeaturePresentationView.swift; sourceTree = ""; }; 31DE43C32C2C60E800F8C51F /* DuckPlayerModalPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DuckPlayerModalPresenter.swift; sourceTree = ""; }; 31DE43C52C2DA70A00F8C51F /* DuckPlayer-ModalAnimation.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "DuckPlayer-ModalAnimation.json"; sourceTree = ""; }; - 31EF52E0281B3BDC0034796E /* AutofillLoginListItemViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillLoginListItemViewModel.swift; sourceTree = ""; }; + 31E77B262D038BB9006F1C9F /* OmnibarAccessoryHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OmnibarAccessoryHandlerTests.swift; sourceTree = ""; }; 3712091D2C21E390003ADF3D /* RemoteMessagingStoreErrorHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteMessagingStoreErrorHandling.swift; sourceTree = ""; }; 372A0FEF2B2389590033BF7F /* SyncMetricsEventsHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncMetricsEventsHandler.swift; sourceTree = ""; }; 3736088F2ABB1E6C00629E7F /* FavoritesDisplayModeStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoritesDisplayModeStorage.swift; sourceTree = ""; }; @@ -1666,6 +1686,7 @@ 6F7FB8E22C660BF300867DA7 /* DailyPixelFiring.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DailyPixelFiring.swift; sourceTree = ""; }; 6F7FB8E42C66158D00867DA7 /* NewTabPageShortcutsSettingsModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageShortcutsSettingsModelTests.swift; sourceTree = ""; }; 6F7FB8E62C66197E00867DA7 /* NewTabPageSectionsSettingsModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageSectionsSettingsModelTests.swift; sourceTree = ""; }; + 6F8348E22D01E401005872E3 /* AlternateAppIcons.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = AlternateAppIcons.xcassets; sourceTree = ""; }; 6F8496402BC3D8EE00ADA54E /* OnboardingButtonsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingButtonsView.swift; sourceTree = ""; }; 6F934F852C58DB00008364E4 /* NewTabPageSettingsPersistentStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageSettingsPersistentStorageTests.swift; sourceTree = ""; }; 6F96FF0F2C2B128500162692 /* NewTabPageCustomizeButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageCustomizeButtonView.swift; sourceTree = ""; }; @@ -2017,7 +2038,6 @@ 9820A5D522B1C0B20024E37C /* DDG Trace.tracetemplate */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = "DDG Trace.tracetemplate"; sourceTree = ""; }; 9820EAF422613CD30089094D /* WebProgressWorker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebProgressWorker.swift; sourceTree = ""; }; 9820FF4F2244FECC008D4782 /* UIScrollViewExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIScrollViewExtension.swift; sourceTree = ""; }; - 9821234D2B6D0A6300F08C57 /* UserAuthenticator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAuthenticator.swift; sourceTree = ""; }; 9821234F2B6D233E00F08C57 /* UserSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSession.swift; sourceTree = ""; }; 9825F9D7293F2DE900F220F2 /* PerformanceTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PerformanceTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 9825F9DA293F2E8700F220F2 /* BookmarksTestData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksTestData.swift; sourceTree = ""; }; @@ -2579,8 +2599,6 @@ 98F3A1D7217B37010011A0D4 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = ""; }; 98F6EA462863124100720957 /* ContentBlockerRulesLists.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentBlockerRulesLists.swift; sourceTree = ""; }; 98F78B8D22419093007CACF4 /* ThemableNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemableNavigationController.swift; sourceTree = ""; }; - 9F1061642C9C013F008DD5A0 /* DefaultVariantManager+Onboarding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DefaultVariantManager+Onboarding.swift"; sourceTree = ""; }; - 9F1623082C9D14F10093C4FC /* DefaultVariantManagerOnboardingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultVariantManagerOnboardingTests.swift; sourceTree = ""; }; 9F16230A2CA0F0190093C4FC /* DebouncerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebouncerTests.swift; sourceTree = ""; }; 9F1798562CD2443F0073018B /* AddToDockPromoViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddToDockPromoViewModelTests.swift; sourceTree = ""; }; 9F23B8002C2BC94400950875 /* OnboardingBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingBackground.swift; sourceTree = ""; }; @@ -2655,50 +2673,6 @@ AA3D854623D9E88E00788410 /* AppIconSettingsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIconSettingsCell.swift; sourceTree = ""; }; AA3D854823DA1DFB00788410 /* AppIcon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIcon.swift; sourceTree = ""; }; AA4D6A6923DB87B1007E8790 /* AppIconManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIconManager.swift; sourceTree = ""; }; - AA4D6A8223DE49A4007E8790 /* AppIconBlack40x40@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconBlack40x40@2x.png"; sourceTree = ""; }; - AA4D6A8323DE49A4007E8790 /* AppIconBlack40x40@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconBlack40x40@3x.png"; sourceTree = ""; }; - AA4D6A8423DE49A4007E8790 /* AppIconBlack60x60@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconBlack60x60@2x.png"; sourceTree = ""; }; - AA4D6A8523DE49A4007E8790 /* AppIconBlack29x29@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconBlack29x29@3x.png"; sourceTree = ""; }; - AA4D6A8723DE49A5007E8790 /* AppIconBlack60x60@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconBlack60x60@3x.png"; sourceTree = ""; }; - AA4D6A8923DE49A5007E8790 /* AppIconBlack76x76@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconBlack76x76@2x.png"; sourceTree = ""; }; - AA4D6A8A23DE49A5007E8790 /* AppIconBlack29x29@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconBlack29x29@2x.png"; sourceTree = ""; }; - AA4D6A9723DE4CC3007E8790 /* AppIconBlue60x60@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconBlue60x60@3x.png"; sourceTree = ""; }; - AA4D6A9823DE4CC3007E8790 /* AppIconBlue76x76@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconBlue76x76@2x.png"; sourceTree = ""; }; - AA4D6A9923DE4CC3007E8790 /* AppIconBlue40x40@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconBlue40x40@3x.png"; sourceTree = ""; }; - AA4D6A9A23DE4CC3007E8790 /* AppIconBlue29x29@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconBlue29x29@2x.png"; sourceTree = ""; }; - AA4D6A9B23DE4CC3007E8790 /* AppIconBlue29x29@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconBlue29x29@3x.png"; sourceTree = ""; }; - AA4D6A9D23DE4CC4007E8790 /* AppIconBlue60x60@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconBlue60x60@2x.png"; sourceTree = ""; }; - AA4D6A9E23DE4CC4007E8790 /* AppIconBlue40x40@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconBlue40x40@2x.png"; sourceTree = ""; }; - AA4D6AAE23DE4D14007E8790 /* AppIconYellow29x29@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconYellow29x29@2x.png"; sourceTree = ""; }; - AA4D6AAF23DE4D14007E8790 /* AppIconYellow29x29@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconYellow29x29@3x.png"; sourceTree = ""; }; - AA4D6AB123DE4D14007E8790 /* AppIconYellow40x40@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconYellow40x40@2x.png"; sourceTree = ""; }; - AA4D6AB223DE4D14007E8790 /* AppIconYellow60x60@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconYellow60x60@3x.png"; sourceTree = ""; }; - AA4D6AB323DE4D15007E8790 /* AppIconYellow60x60@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconYellow60x60@2x.png"; sourceTree = ""; }; - AA4D6AB523DE4D15007E8790 /* AppIconYellow40x40@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconYellow40x40@3x.png"; sourceTree = ""; }; - AA4D6AB623DE4D15007E8790 /* AppIconYellow76x76@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconYellow76x76@2x.png"; sourceTree = ""; }; - AA4D6AC223DE4D26007E8790 /* AppIconPurple60x60@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconPurple60x60@2x.png"; sourceTree = ""; }; - AA4D6AC323DE4D26007E8790 /* AppIconPurple29x29@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconPurple29x29@3x.png"; sourceTree = ""; }; - AA4D6AC423DE4D26007E8790 /* AppIconPurple60x60@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconPurple60x60@3x.png"; sourceTree = ""; }; - AA4D6AC523DE4D26007E8790 /* AppIconPurple76x76@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconPurple76x76@2x.png"; sourceTree = ""; }; - AA4D6AC723DE4D26007E8790 /* AppIconPurple40x40@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconPurple40x40@2x.png"; sourceTree = ""; }; - AA4D6AC923DE4D26007E8790 /* AppIconPurple29x29@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconPurple29x29@2x.png"; sourceTree = ""; }; - AA4D6ACA23DE4D26007E8790 /* AppIconPurple40x40@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconPurple40x40@3x.png"; sourceTree = ""; }; - AA4D6AD723DE4D32007E8790 /* AppIconGreen76x76@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconGreen76x76@2x.png"; sourceTree = ""; }; - AA4D6AD823DE4D32007E8790 /* AppIconGreen40x40@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconGreen40x40@2x.png"; sourceTree = ""; }; - AA4D6AD923DE4D32007E8790 /* AppIconGreen60x60@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconGreen60x60@2x.png"; sourceTree = ""; }; - AA4D6ADA23DE4D32007E8790 /* AppIconGreen40x40@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconGreen40x40@3x.png"; sourceTree = ""; }; - AA4D6ADC23DE4D33007E8790 /* AppIconGreen60x60@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconGreen60x60@3x.png"; sourceTree = ""; }; - AA4D6ADD23DE4D33007E8790 /* AppIconGreen29x29@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconGreen29x29@2x.png"; sourceTree = ""; }; - AA4D6ADF23DE4D33007E8790 /* AppIconGreen29x29@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconGreen29x29@3x.png"; sourceTree = ""; }; - AA4D6AF423DF0312007E8790 /* AppIconRed60x60@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconRed60x60@3x.png"; sourceTree = ""; }; - AA4D6AF523DF0312007E8790 /* AppIconRed60x60@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconRed60x60@2x.png"; sourceTree = ""; }; - AA4D6AF823DF0CF5007E8790 /* AppIconRed29x29@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconRed29x29@3x.png"; sourceTree = ""; }; - AA4D6AF923DF0CF6007E8790 /* AppIconRed29x29@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconRed29x29@2x.png"; sourceTree = ""; }; - AAF2E28023E0495400962AF8 /* AppIconBlack83.5x83.5@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconBlack83.5x83.5@2x.png"; sourceTree = ""; }; - AAF2E28223E0495E00962AF8 /* AppIconBlue83.5x83.5@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconBlue83.5x83.5@2x.png"; sourceTree = ""; }; - AAF2E28423E0496F00962AF8 /* AppIconGreen83.5x83.5@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconGreen83.5x83.5@2x.png"; sourceTree = ""; }; - AAF2E28623E0498100962AF8 /* AppIconPurple83.5x83.5@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconPurple83.5x83.5@2x.png"; sourceTree = ""; }; - AAF2E28A23E049DF00962AF8 /* AppIconYellow83.5x83.5@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "AppIconYellow83.5x83.5@2x.png"; sourceTree = ""; }; B603974829C19F6F00902A34 /* Assertions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Assertions.swift; sourceTree = ""; }; B609D5512862EAFF0088CAC2 /* InlineWKDownloadDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InlineWKDownloadDelegate.swift; sourceTree = ""; }; B60DFF062872B64B0061E7C2 /* JSAlertController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSAlertController.swift; sourceTree = ""; }; @@ -2741,13 +2715,28 @@ BDFF03242BA3D92E00F324C9 /* NetworkProtectionFeatureVisibilityTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionFeatureVisibilityTests.swift; sourceTree = ""; }; C10CB5F22A1A5BDF0048E503 /* AutofillViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillViews.swift; sourceTree = ""; }; C111B26827F579EF006558B1 /* BookmarkOrFolderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkOrFolderTests.swift; sourceTree = ""; }; + C1193F602D08642900CB3239 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = ""; }; + C1193F612D08642900CB3239 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; + C11C4D302D08648100288E85 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/InfoPlist.strings; sourceTree = ""; }; + C11C4D312D08648100288E85 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = ""; }; C12726ED2A5FF88C00215B02 /* EmailSignupPromptView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailSignupPromptView.swift; sourceTree = ""; }; C12726EF2A5FF89900215B02 /* EmailSignupPromptViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailSignupPromptViewModel.swift; sourceTree = ""; }; C12726F12A5FF8CB00215B02 /* EmailSignupPromptViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailSignupPromptViewController.swift; sourceTree = ""; }; + C12854D82D08636E00C8353F /* lv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lv; path = lv.lproj/InfoPlist.strings; sourceTree = ""; }; + C12854D92D08636E00C8353F /* lv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lv; path = lv.lproj/Localizable.strings; sourceTree = ""; }; + C129DF092D0862D7007AB046 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/InfoPlist.strings; sourceTree = ""; }; + C129DF0A2D0862D7007AB046 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = ""; }; + C132F5A32D0862B8000C81D0 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/InfoPlist.strings; sourceTree = ""; }; + C132F5A42D0862B8000C81D0 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = ""; }; C13B32D12A0E750700A59236 /* AutofillSettingStatus.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutofillSettingStatus.swift; sourceTree = ""; }; + C13C076B2D00A6B7006386CF /* VaultCredentialManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VaultCredentialManager.swift; sourceTree = ""; }; C13F3F672B7F88100083BE40 /* AuthConfirmationPromptView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthConfirmationPromptView.swift; sourceTree = ""; }; C13F3F692B7F883A0083BE40 /* AuthConfirmationPromptViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthConfirmationPromptViewController.swift; sourceTree = ""; }; C13F3F6B2B7F88470083BE40 /* AuthConfirmationPromptViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthConfirmationPromptViewModel.swift; sourceTree = ""; }; + C1406D862D0862F30082CB50 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/InfoPlist.strings; sourceTree = ""; }; + C1406D872D0862F30082CB50 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/Localizable.strings; sourceTree = ""; }; + C1453E322D0863C80024449B /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = ""; }; + C1453E332D0863C80024449B /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = ""; }; C14882D727F2011C00D59F0C /* BookmarksExporter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarksExporter.swift; sourceTree = ""; }; C14882D927F2011C00D59F0C /* BookmarksImporter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarksImporter.swift; sourceTree = ""; }; C14882E127F20D9A00D59F0C /* BookmarksExporterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarksExporterTests.swift; sourceTree = ""; }; @@ -2755,22 +2744,37 @@ C14882E527F20DAA00D59F0C /* HtmlTestDataLoader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HtmlTestDataLoader.swift; sourceTree = ""; }; C14882E627F20DAB00D59F0C /* TestDataLoader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestDataLoader.swift; sourceTree = ""; }; C14882E927F20DD000D59F0C /* MockBookmarksCoreDataStorage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockBookmarksCoreDataStorage.swift; sourceTree = ""; }; + C14D37D62D08649E00FCFC59 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = ""; }; + C14D37D72D08649E00FCFC59 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = ""; }; C14D43002B45D6CD00ACA4DC /* AutofillDebugViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutofillDebugViewController.swift; sourceTree = ""; }; C14E2F7629DE14EA002AC515 /* AutofillInterfaceUsernameTruncatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillInterfaceUsernameTruncatorTests.swift; sourceTree = ""; }; + C1588FC42D08644800C9BE70 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/InfoPlist.strings; sourceTree = ""; }; + C1588FC52D08644800C9BE70 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/Localizable.strings; sourceTree = ""; }; C158AC7A297AB5DC0008723A /* MockSecureVault.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockSecureVault.swift; sourceTree = ""; }; C159DF062A430B60007834BB /* EmailSignupViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailSignupViewController.swift; sourceTree = ""; }; C160544029D6044D00B715A1 /* AutofillInterfaceUsernameTruncator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillInterfaceUsernameTruncator.swift; sourceTree = ""; }; + C163677A2D08638C001D1094 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/InfoPlist.strings; sourceTree = ""; }; + C163677B2D08638C001D1094 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Localizable.strings; sourceTree = ""; }; C1641EAE2BC2F5140012607A /* ImportPasswordsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportPasswordsViewController.swift; sourceTree = ""; }; C1641EB02BC2F52B0012607A /* ImportPasswordsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportPasswordsView.swift; sourceTree = ""; }; C1641EB22BC2F53C0012607A /* ImportPasswordsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportPasswordsViewModel.swift; sourceTree = ""; }; + C164F9472D0861D600BAE88E /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/InfoPlist.strings; sourceTree = ""; }; + C164F9482D0861D600BAE88E /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = ""; }; + C174E08E2D08625300ACE1AF /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/InfoPlist.strings; sourceTree = ""; }; + C174E08F2D08625300ACE1AF /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/Localizable.strings; sourceTree = ""; }; + C177D9F52CFDDFEB0039CBF7 /* UIAlertControllerExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIAlertControllerExtension.swift; sourceTree = ""; }; C17B59562A03AAD30055F2D1 /* PasswordGenerationPromptViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasswordGenerationPromptViewModel.swift; sourceTree = ""; }; C17B59572A03AAD30055F2D1 /* PasswordGenerationPromptViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasswordGenerationPromptViewController.swift; sourceTree = ""; }; C17B59582A03AAD30055F2D1 /* PasswordGenerationPromptView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasswordGenerationPromptView.swift; sourceTree = ""; }; + C180CAFA2D0863E500ADB0FE /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/InfoPlist.strings; sourceTree = ""; }; + C180CAFB2D0863E500ADB0FE /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/Localizable.strings; sourceTree = ""; }; C1836CE02C359EC90016D057 /* AutofillBreakageReportCellContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillBreakageReportCellContentView.swift; sourceTree = ""; }; C1836CE42C35A0EA0016D057 /* AutofillBreakageReportTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillBreakageReportTableViewCell.swift; sourceTree = ""; }; C185ED602BD4329700BAE9DC /* ImportPasswordsStatusHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportPasswordsStatusHandler.swift; sourceTree = ""; }; C185ED632BD438AF00BAE9DC /* ImportPasswordsStatusHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportPasswordsStatusHandlerTests.swift; sourceTree = ""; }; C185ED652BD43A5500BAE9DC /* MockDDGSyncing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockDDGSyncing.swift; sourceTree = ""; }; + C18D7C412D08620D00FB3F87 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/InfoPlist.strings; sourceTree = ""; }; + C18D7C422D08620D00FB3F87 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = ""; }; C18ED4392AB6F77600BF3805 /* AutofillSettingsEnableFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillSettingsEnableFooterView.swift; sourceTree = ""; }; C18ED43B2AB8364400BF3805 /* FileTextPreviewDebugViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileTextPreviewDebugViewController.swift; sourceTree = ""; }; C1935A0D2C88D11D001AD72D /* AutofillSurveyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillSurveyView.swift; sourceTree = ""; }; @@ -2779,25 +2783,79 @@ C1935A212C89CA9F001AD72D /* AutofillSurveyManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutofillSurveyManagerTests.swift; sourceTree = ""; }; C1935A232C89CC6D001AD72D /* AutofillHeaderViewFactoryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillHeaderViewFactoryTests.swift; sourceTree = ""; }; C1963862283794A000298D4D /* BookmarksCachingSearch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarksCachingSearch.swift; sourceTree = ""; }; + C19D90D02CFE3A7F00D17DF3 /* AutofillLoginListSectionType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillLoginListSectionType.swift; sourceTree = ""; }; + C1A001092D08635100372C87 /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lt; path = lt.lproj/InfoPlist.strings; sourceTree = ""; }; + C1A0010A2D08635100372C87 /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lt; path = lt.lproj/Localizable.strings; sourceTree = ""; }; C1B0F6412AB08BE9001EAF05 /* MockPrivacyConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockPrivacyConfiguration.swift; sourceTree = ""; }; + C1B783DB2D0863110071C53B /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/InfoPlist.strings; sourceTree = ""; }; + C1B783DC2D0863110071C53B /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = ""; }; C1B7B51B28941E980098FD6A /* HomeMessageViewModelBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeMessageViewModelBuilder.swift; sourceTree = ""; }; C1B7B52128941F2A0098FD6A /* RemoteMessagingClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteMessagingClient.swift; sourceTree = ""; }; C1B7B52C2894469D0098FD6A /* DefaultVariantManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultVariantManager.swift; sourceTree = ""; }; C1B7B53328944EFA0098FD6A /* CoreDataTestUtilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreDataTestUtilities.swift; sourceTree = ""; }; + C1B7BF522D08640B0024FF56 /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/InfoPlist.strings; sourceTree = ""; }; + C1B7BF532D08640B0024FF56 /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/Localizable.strings; sourceTree = ""; }; C1B924B62ACD6E6800EE7B06 /* AutofillNeverSavedTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillNeverSavedTableViewCell.swift; sourceTree = ""; }; C1BF0BA429B63D7200482B73 /* AutofillLoginPromptHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutofillLoginPromptHelper.swift; sourceTree = ""; }; C1BF0BA729B63E1A00482B73 /* AutofillLoginPromptViewModelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutofillLoginPromptViewModelTests.swift; sourceTree = ""; }; C1BF26142C74D10F00F6405E /* SyncPromoManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncPromoManager.swift; sourceTree = ""; }; + C1C1FF412D085A280017ACCE /* CredentialProviderListDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialProviderListDetailsView.swift; sourceTree = ""; }; + C1C1FF422D085A280017ACCE /* CredentialProviderListDetailsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialProviderListDetailsViewController.swift; sourceTree = ""; }; + C1C1FF432D085A280017ACCE /* CredentialProviderListDetailsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialProviderListDetailsViewModel.swift; sourceTree = ""; }; + C1C1FF482D085A4C0017ACCE /* AutofillInterfaceEmailTruncator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillInterfaceEmailTruncator.swift; sourceTree = ""; }; + C1C1FF492D085A4C0017ACCE /* PasswordHider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordHider.swift; sourceTree = ""; }; + C1C1FF4C2D085AD70017ACCE /* ActionMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionMessageView.swift; sourceTree = ""; }; + C1C1FF4D2D085AD70017ACCE /* ActionMessageView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ActionMessageView.xib; sourceTree = ""; }; + C1C1FF4E2D085AD70017ACCE /* FaviconHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FaviconHelper.swift; sourceTree = ""; }; + C1C1FF4F2D085AD70017ACCE /* NibLoading.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NibLoading.swift; sourceTree = ""; }; + C1CAAA672CF8B74200C37EE6 /* AutofillCredentialProviderAlpha.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AutofillCredentialProviderAlpha.entitlements; sourceTree = ""; }; + C1CAAA692CF8BABF00C37EE6 /* CredentialProviderActivatedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialProviderActivatedView.swift; sourceTree = ""; }; + C1CAAA702CF8BC0B00C37EE6 /* UIViewControllerExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewControllerExtension.swift; sourceTree = ""; }; + C1CAAA722CF8BD1C00C37EE6 /* CredentialProviderActivatedViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialProviderActivatedViewModel.swift; sourceTree = ""; }; + C1CAAA772CF8BDF200C37EE6 /* UserText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserText.swift; sourceTree = ""; }; + C1CAAA792CF8BE0200C37EE6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + C1CAAA842CF8C9EA00C37EE6 /* UIResponderExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIResponderExtension.swift; sourceTree = ""; }; + C1CAAA872CF9FFE100C37EE6 /* CredentialProviderListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialProviderListViewController.swift; sourceTree = ""; }; + C1CAAA892CF9FFF300C37EE6 /* CredentialProviderListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialProviderListViewModel.swift; sourceTree = ""; }; + C1CAAA912CFCAA0500C37EE6 /* CredentialProviderListItemTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialProviderListItemTableViewCell.swift; sourceTree = ""; }; + C1CAAA992CFCAD3E00C37EE6 /* AutofillLoginItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillLoginItem.swift; sourceTree = ""; }; + C1CAAA9B2CFCB39800C37EE6 /* AutofillLoginListSorting.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillLoginListSorting.swift; sourceTree = ""; }; + C1CAAA9D2CFCB78700C37EE6 /* UIColorExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIColorExtension.swift; sourceTree = ""; }; + C1CAAA9F2CFCB7C200C37EE6 /* UImageExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UImageExtension.swift; sourceTree = ""; }; + C1CAAAA22CFCBBBD00C37EE6 /* UserAuthenticator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAuthenticator.swift; sourceTree = ""; }; + C1CAAAA52CFCBD7900C37EE6 /* LockScreenView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockScreenView.swift; sourceTree = ""; }; + C1CAAAA72CFCBE4800C37EE6 /* EmptySearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptySearchView.swift; sourceTree = ""; }; + C1CAAAA92CFCC13E00C37EE6 /* SecureVaultReporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureVaultReporter.swift; sourceTree = ""; }; + C1CAAAAB2CFCC91D00C37EE6 /* EmptyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyView.swift; sourceTree = ""; }; + C1CB0AA52D08629800335287 /* et */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = et; path = et.lproj/InfoPlist.strings; sourceTree = ""; }; + C1CB0AA62D08629800335287 /* et */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = et; path = et.lproj/Localizable.strings; sourceTree = ""; }; C1CDA3152AFB9C7F006D1476 /* AutofillNeverPromptWebsitesManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutofillNeverPromptWebsitesManager.swift; sourceTree = ""; }; C1CDA31D2AFBF811006D1476 /* AutofillNeverPromptWebsitesManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillNeverPromptWebsitesManagerTests.swift; sourceTree = ""; }; C1D21E2C293A5965006E5A05 /* AutofillLoginSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillLoginSession.swift; sourceTree = ""; }; C1D21E2E293A599C006E5A05 /* AutofillLoginSessionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillLoginSessionTests.swift; sourceTree = ""; }; + C1DCF3502D0862330055F8B0 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = ""; }; + C1DCF3512D0862330055F8B0 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; C1E42C7A2C5CD8AD00509204 /* AutofillCredentialsDebugViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutofillCredentialsDebugViewController.swift; sourceTree = ""; }; + C1E490582D08646400F86C5A /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/InfoPlist.strings; sourceTree = ""; }; + C1E490592D08646400F86C5A /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/Localizable.strings; sourceTree = ""; }; + C1E4E9A52D0861AD00AA39AF /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/InfoPlist.strings; sourceTree = ""; }; + C1E4E9A82D0861AD00AA39AF /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/Localizable.strings; sourceTree = ""; }; C1EA865F2C74CB6C00E8604D /* SyncPromoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncPromoView.swift; sourceTree = ""; }; C1EA86612C74CB8B00E8604D /* SyncPromoViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncPromoViewModel.swift; sourceTree = ""; }; + C1EF5B212CC0457B002980E6 /* AutofillCredentialProvider.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = AutofillCredentialProvider.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + C1EF5B222CC0457B002980E6 /* AuthenticationServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AuthenticationServices.framework; path = System/Library/Frameworks/AuthenticationServices.framework; sourceTree = SDKROOT; }; + C1EF5B252CC0457B002980E6 /* CredentialProviderViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialProviderViewController.swift; sourceTree = ""; }; + C1EF5B2A2CC0457B002980E6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + C1EF5B2B2CC0457B002980E6 /* AutofillCredentialProvider.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AutofillCredentialProvider.entitlements; sourceTree = ""; }; C1F341C42A6924000032057B /* EmailAddressPromptView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailAddressPromptView.swift; sourceTree = ""; }; C1F341C62A6924100032057B /* EmailAddressPromptViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailAddressPromptViewModel.swift; sourceTree = ""; }; C1F341C82A6926920032057B /* EmailAddressPromptViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailAddressPromptViewController.swift; sourceTree = ""; }; + C1F883372D08627900DFF79A /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = ""; }; + C1F883382D08627900DFF79A /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; + C1FE93E52D0863AA009F8F5E /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/InfoPlist.strings; sourceTree = ""; }; + C1FE93E62D0863AA009F8F5E /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = ""; }; + C1FEDCEA2D08633100BFBF3F /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoPlist.strings; sourceTree = ""; }; + C1FEDCEB2D08633100BFBF3F /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = ""; }; C1FFBD452C761BE20073622B /* SyncPromoManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncPromoManagerTests.swift; sourceTree = ""; }; C1FFBD472C7749A90073622B /* SyncSettingsViewController+PlatformLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SyncSettingsViewController+PlatformLinks.swift"; sourceTree = ""; }; CB1143DD2AF6D4B600C1CCD3 /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/InfoPlist.strings; sourceTree = ""; }; @@ -2813,6 +2871,7 @@ CB2A7EF028410DF700885F67 /* PixelEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PixelEvent.swift; sourceTree = ""; }; CB2A7EF3285383B300885F67 /* AppLastCompiledRulesStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLastCompiledRulesStore.swift; sourceTree = ""; }; CB2C47822AF6D55800AEDCD9 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/InfoPlist.strings; sourceTree = ""; }; + CB3C78902D08483F00A7E4ED /* InactiveBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InactiveBackground.swift; sourceTree = ""; }; CB4448752AF6D51D001F93F7 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/InfoPlist.strings; sourceTree = ""; }; CB48D3312B90CE9F00631D8B /* PageRefreshStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PageRefreshStore.swift; sourceTree = ""; }; CB4FA44D2C78AACE00A16F5A /* SpecialErrorPageUserScript.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpecialErrorPageUserScript.swift; sourceTree = ""; }; @@ -2901,7 +2960,6 @@ D6ACEA312BBD55BF008FADDF /* TabURLInterceptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabURLInterceptor.swift; sourceTree = ""; }; D6B67A112C332B6E002122EB /* DuckPlayerMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DuckPlayerMocks.swift; sourceTree = ""; }; D6B9E8D12CDA4418002B640C /* DuckPlayerOverlayUsagePixels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DuckPlayerOverlayUsagePixels.swift; sourceTree = ""; }; - D6B9E8D32CDA8369002B640C /* DuckPlayerOverlayUsagePixelsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DuckPlayerOverlayUsagePixelsTests.swift; sourceTree = ""; }; D6BFCB5E2B7524AA0051FF81 /* SubscriptionPIRView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionPIRView.swift; sourceTree = ""; }; D6BFCB602B7525160051FF81 /* SubscriptionPIRViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubscriptionPIRViewModel.swift; sourceTree = ""; }; D6D95CE22B6D9F8800960317 /* AsyncHeadlessWebViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncHeadlessWebViewModel.swift; sourceTree = ""; }; @@ -3083,7 +3141,6 @@ F47E53DA250A9A1C0037C686 /* Onboarding.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Onboarding.xcassets; sourceTree = ""; }; F4B0B78B252CAFF700830156 /* OnboardingWidgetsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OnboardingWidgetsViewController.swift; sourceTree = ""; }; F4B0B795252CB35700830156 /* OnboardingWidgetsDetailsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingWidgetsDetailsViewController.swift; sourceTree = ""; }; - F4C9FBF428340DDA002281CC /* AutofillInterfaceEmailTruncator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillInterfaceEmailTruncator.swift; sourceTree = ""; }; F4CE6D1A257EA33C00D0A6AA /* FireButtonAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FireButtonAnimator.swift; sourceTree = ""; }; F4D7220F26F29A70007D6193 /* BookmarkDetailsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkDetailsCell.swift; sourceTree = ""; }; F4D9C4F925117A0F00814B71 /* HomeMessageStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeMessageStorage.swift; sourceTree = ""; }; @@ -3129,9 +3186,11 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 315C77822CFA41A400699683 /* AIChat in Frameworks */, 9F8FE9492BAE50E50071E372 /* Lottie in Frameworks */, 853273B624FFE0BB00E3C778 /* WidgetKit.framework in Frameworks */, 0238E44F29C0FAA100615E30 /* FindInPageIOSJSSupport in Frameworks */, + 56D7792C2CFF476800B619EF /* StoreKit.framework in Frameworks */, 3760DFED299315EF0045A446 /* Waitlist in Frameworks */, F1D43AFA2B99C1D300BAB743 /* BareBonesBrowserKit in Frameworks */, F143C2EB1E4A4CD400CFDE3A /* Core.framework in Frameworks */, @@ -3143,7 +3202,6 @@ F4D7F634298C00C3006C3AE9 /* FindInPageIOSJSSupport in Frameworks */, 9F96F73B2C9144D5009E45D5 /* Onboarding in Frameworks */, 85D598872927F84C00FA3B1B /* Crashes in Frameworks */, - D664C7DD2B28A02800CBFA76 /* StoreKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3236,6 +3294,17 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + C1EF5B1E2CC0457B002980E6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C1CAAA812CF8C8F400C37EE6 /* DuckUI in Frameworks */, + C1EF5B232CC0457B002980E6 /* AuthenticationServices.framework in Frameworks */, + C1CAAA7B2CF8C8ED00C37EE6 /* Core.framework in Frameworks */, + C1CAAA832CF8C8FF00C37EE6 /* DesignResourcesKit in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; F143C2E01E4A4CD400CFDE3A /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -3251,7 +3320,9 @@ CBC83E3429B631780008E19C /* Configuration in Frameworks */, D61CDA182B7CF78300A0FBB9 /* ZIPFoundation in Frameworks */, CB6D8E982C80A9B100D0E772 /* SpecialErrorPages in Frameworks */, + 56D7793A2CFFC7E800B619EF /* PixelExperimentKit in Frameworks */, 851F74262B9A1BFD00747C42 /* Suggestions in Frameworks */, + 56D7793C2CFFC7E800B619EF /* PixelKit in Frameworks */, 98A16C2D28A11D6200A6C003 /* BrowserServicesKit in Frameworks */, 8599690F29D2F1C100DBF9FA /* DDGSync in Frameworks */, 851481882A600EFC00ABC65F /* RemoteMessaging in Frameworks */, @@ -3343,6 +3414,7 @@ 1DEAADEB2BA45B4400E25A97 /* SettingsAccessibilityView.swift */, 1DEAADED2BA45DFE00E25A97 /* SettingsDataClearingView.swift */, D65625A02C232F5E006EF297 /* SettingsDuckPlayerView.swift */, + 317CA3422CFF82DB00F88848 /* SettingsAIChatView.swift */, ); name = MainSettings; sourceTree = ""; @@ -3590,6 +3662,31 @@ name = Downloads; sourceTree = ""; }; + 310EEA2C2CFFCD9B0043CA1A /* New Group */ = { + isa = PBXGroup; + children = ( + ); + path = "New Group"; + sourceTree = ""; + }; + 310EEA2D2CFFCDB60043CA1A /* AIChat */ = { + isa = PBXGroup; + children = ( + 31E77B262D038BB9006F1C9F /* OmnibarAccessoryHandlerTests.swift */, + 310EEA2E2CFFCDBF0043CA1A /* AIChatSettingsTests.swift */, + ); + path = AIChat; + sourceTree = ""; + }; + 311C79E22CF790270021196A /* AIChat */ = { + isa = PBXGroup; + children = ( + 311711902D00E53A0063AC3D /* OmnibarAccessoryHandling.swift */, + 316AA4592CF8E31F00A2ED28 /* AIChatSettings.swift */, + ); + path = AIChat; + sourceTree = ""; + }; 3132FA2227A0776B00DD7A12 /* FilePreview */ = { isa = PBXGroup; children = ( @@ -3633,7 +3730,6 @@ children = ( 319A37132829A5450079FBCE /* Table */, 31584619281B08F5004ADB8B /* AutofillLoginListViewModel.swift */, - 31EF52E0281B3BDC0034796E /* AutofillLoginListItemViewModel.swift */, 311BD1B02836C0CA00AEF6C1 /* AutofillLoginListAuthenticator.swift */, ); name = List; @@ -3676,10 +3772,18 @@ name = Utils; sourceTree = ""; }; + 317DF6092D01E7C400DE0145 /* RoundedPageContainer */ = { + isa = PBXGroup; + children = ( + 317DF60A2D01E7D600DE0145 /* RoundedPageSheetPresentationAnimator.swift */, + 317DF6072D01E7B900DE0145 /* RoundedPageSheetContainerViewController.swift */, + ); + path = RoundedPageContainer; + sourceTree = ""; + }; 31951E9328230D8900CAF535 /* Shared */ = { isa = PBXGroup; children = ( - F4C9FBF428340DDA002281CC /* AutofillInterfaceEmailTruncator.swift */, 31A42563285A09E800049386 /* FaviconView.swift */, 31A42565285A0A6300049386 /* FaviconViewModel.swift */, C160544029D6044D00B715A1 /* AutofillInterfaceUsernameTruncator.swift */, @@ -3770,6 +3874,7 @@ 85875B5F29912A2D00115F05 /* SyncUI */, 37FCAACB2993149A000E420A /* Waitlist */, 31794BFF2821DFB600F18633 /* DuckUI */, + 315C77802CFA414400699683 /* AIChat */, ); path = LocalPackages; sourceTree = ""; @@ -4279,6 +4384,7 @@ 85F21DAE210F5E32002631A6 /* AtbUITests */, 9825F9D9293F2E5F00F220F2 /* PerformanceTests */, 85D33FCC25C97B6E002B91A6 /* IntegrationTests */, + C1EF5B242CC0457B002980E6 /* AutofillCredentialProvider */, F1AA545F1E48D90700223211 /* Frameworks */, 31E69A60280F4BAD00478327 /* LocalPackages */, 84E341931E2F7EFB00BDBA6F /* Products */, @@ -4304,6 +4410,7 @@ 9825F9D7293F2DE900F220F2 /* PerformanceTests.xctest */, 02025662298818B100E694E7 /* PacketTunnelProvider.appex */, B6DFE6CF2BC7E47500A9CE59 /* SwiftLintTool.bundle */, + C1EF5B212CC0457B002980E6 /* AutofillCredentialProvider.appex */, 98424AA92CED4F430071C7DB /* WebViewUnitTests.xctest */, ); name = Products; @@ -4312,6 +4419,7 @@ 84E341941E2F7EFB00BDBA6F /* DuckDuckGo */ = { isa = PBXGroup; children = ( + 311C79E22CF790270021196A /* AIChat */, 6FD1BAE02B87A0E8000C475C /* AdAttribution */, AA4D6A8023DE4973007E8790 /* AppIcon */, F1C5ECF31E37812900C599A4 /* Application */, @@ -4917,7 +5025,6 @@ 9F7CFF7C2C89B69A0012833E /* AppIconPickerViewModelTests.swift */, 9FDEC7B32C8FD62F00C7A692 /* OnboardingAddressBarPositionPickerViewModelTests.swift */, 9FDEC7B92C9006E000C7A692 /* BrowserComparisonModelTests.swift */, - 9F1623082C9D14F10093C4FC /* DefaultVariantManagerOnboardingTests.swift */, 9F8E0F322CCA642D001EA7C5 /* VideoPlayerViewModelTests.swift */, 9F1798562CD2443F0073018B /* AddToDockPromoViewModelTests.swift */, ); @@ -5077,7 +5184,6 @@ 9F23B7FF2C2BABE000950875 /* OnboardingIntro */, 9F5E5AAA2C3D0FAA00165F54 /* ContextualOnboarding */, 9FCFCD842C75C91A006EB7A0 /* ProgressBarView.swift */, - 9F1061642C9C013F008DD5A0 /* DefaultVariantManager+Onboarding.swift */, ); path = OnboardingExperiment; sourceTree = ""; @@ -5087,102 +5193,11 @@ children = ( AA4D6A6923DB87B1007E8790 /* AppIconManager.swift */, AA3D854823DA1DFB00788410 /* AppIcon.swift */, - AA4D6A8123DE4981007E8790 /* Black */, - AA4D6A9623DE4BB3007E8790 /* Blue */, - AA4D6AAB23DE4CC9007E8790 /* Green */, - AA4D6AAC23DE4CE5007E8790 /* Purple */, - AA4D6AF323DF0282007E8790 /* Red */, - AA4D6AAD23DE4D01007E8790 /* Yellow */, + 6F8348E22D01E401005872E3 /* AlternateAppIcons.xcassets */, ); name = AppIcon; sourceTree = ""; }; - AA4D6A8123DE4981007E8790 /* Black */ = { - isa = PBXGroup; - children = ( - AA4D6A8A23DE49A5007E8790 /* AppIconBlack29x29@2x.png */, - AA4D6A8523DE49A4007E8790 /* AppIconBlack29x29@3x.png */, - AA4D6A8223DE49A4007E8790 /* AppIconBlack40x40@2x.png */, - AA4D6A8323DE49A4007E8790 /* AppIconBlack40x40@3x.png */, - AA4D6A8423DE49A4007E8790 /* AppIconBlack60x60@2x.png */, - AA4D6A8723DE49A5007E8790 /* AppIconBlack60x60@3x.png */, - AA4D6A8923DE49A5007E8790 /* AppIconBlack76x76@2x.png */, - AAF2E28023E0495400962AF8 /* AppIconBlack83.5x83.5@2x.png */, - ); - name = Black; - sourceTree = ""; - }; - AA4D6A9623DE4BB3007E8790 /* Blue */ = { - isa = PBXGroup; - children = ( - AA4D6A9A23DE4CC3007E8790 /* AppIconBlue29x29@2x.png */, - AA4D6A9B23DE4CC3007E8790 /* AppIconBlue29x29@3x.png */, - AA4D6A9E23DE4CC4007E8790 /* AppIconBlue40x40@2x.png */, - AA4D6A9923DE4CC3007E8790 /* AppIconBlue40x40@3x.png */, - AA4D6A9D23DE4CC4007E8790 /* AppIconBlue60x60@2x.png */, - AA4D6A9723DE4CC3007E8790 /* AppIconBlue60x60@3x.png */, - AA4D6A9823DE4CC3007E8790 /* AppIconBlue76x76@2x.png */, - AAF2E28223E0495E00962AF8 /* AppIconBlue83.5x83.5@2x.png */, - ); - name = Blue; - sourceTree = ""; - }; - AA4D6AAB23DE4CC9007E8790 /* Green */ = { - isa = PBXGroup; - children = ( - AA4D6ADD23DE4D33007E8790 /* AppIconGreen29x29@2x.png */, - AA4D6ADF23DE4D33007E8790 /* AppIconGreen29x29@3x.png */, - AA4D6AD823DE4D32007E8790 /* AppIconGreen40x40@2x.png */, - AA4D6ADA23DE4D32007E8790 /* AppIconGreen40x40@3x.png */, - AA4D6AD923DE4D32007E8790 /* AppIconGreen60x60@2x.png */, - AA4D6ADC23DE4D33007E8790 /* AppIconGreen60x60@3x.png */, - AA4D6AD723DE4D32007E8790 /* AppIconGreen76x76@2x.png */, - AAF2E28423E0496F00962AF8 /* AppIconGreen83.5x83.5@2x.png */, - ); - name = Green; - sourceTree = ""; - }; - AA4D6AAC23DE4CE5007E8790 /* Purple */ = { - isa = PBXGroup; - children = ( - AA4D6AC923DE4D26007E8790 /* AppIconPurple29x29@2x.png */, - AA4D6AC323DE4D26007E8790 /* AppIconPurple29x29@3x.png */, - AA4D6AC723DE4D26007E8790 /* AppIconPurple40x40@2x.png */, - AA4D6ACA23DE4D26007E8790 /* AppIconPurple40x40@3x.png */, - AA4D6AC223DE4D26007E8790 /* AppIconPurple60x60@2x.png */, - AA4D6AC423DE4D26007E8790 /* AppIconPurple60x60@3x.png */, - AA4D6AC523DE4D26007E8790 /* AppIconPurple76x76@2x.png */, - AAF2E28623E0498100962AF8 /* AppIconPurple83.5x83.5@2x.png */, - ); - name = Purple; - sourceTree = ""; - }; - AA4D6AAD23DE4D01007E8790 /* Yellow */ = { - isa = PBXGroup; - children = ( - AA4D6AAE23DE4D14007E8790 /* AppIconYellow29x29@2x.png */, - AA4D6AAF23DE4D14007E8790 /* AppIconYellow29x29@3x.png */, - AA4D6AB123DE4D14007E8790 /* AppIconYellow40x40@2x.png */, - AA4D6AB523DE4D15007E8790 /* AppIconYellow40x40@3x.png */, - AA4D6AB323DE4D15007E8790 /* AppIconYellow60x60@2x.png */, - AA4D6AB223DE4D14007E8790 /* AppIconYellow60x60@3x.png */, - AA4D6AB623DE4D15007E8790 /* AppIconYellow76x76@2x.png */, - AAF2E28A23E049DF00962AF8 /* AppIconYellow83.5x83.5@2x.png */, - ); - name = Yellow; - sourceTree = ""; - }; - AA4D6AF323DF0282007E8790 /* Red */ = { - isa = PBXGroup; - children = ( - AA4D6AF923DF0CF6007E8790 /* AppIconRed29x29@2x.png */, - AA4D6AF823DF0CF5007E8790 /* AppIconRed29x29@3x.png */, - AA4D6AF523DF0312007E8790 /* AppIconRed60x60@2x.png */, - AA4D6AF423DF0312007E8790 /* AppIconRed60x60@3x.png */, - ); - name = Red; - sourceTree = ""; - }; B652DF02287C01EE00C12A9C /* ContentBlocking */ = { isa = PBXGroup; children = ( @@ -5377,6 +5392,16 @@ name = AutofillLoginUI; sourceTree = ""; }; + C1C1FF442D085A280017ACCE /* CredentialProviderListDetails */ = { + isa = PBXGroup; + children = ( + C1C1FF412D085A280017ACCE /* CredentialProviderListDetailsView.swift */, + C1C1FF422D085A280017ACCE /* CredentialProviderListDetailsViewController.swift */, + C1C1FF432D085A280017ACCE /* CredentialProviderListDetailsViewModel.swift */, + ); + path = CredentialProviderListDetails; + sourceTree = ""; + }; C1CAA3D52A630ECB00807703 /* EmailSignup */ = { isa = PBXGroup; children = ( @@ -5388,6 +5413,96 @@ name = EmailSignup; sourceTree = ""; }; + C1CAAA682CF8B8C400C37EE6 /* CredentialProviderActivation */ = { + isa = PBXGroup; + children = ( + C1CAAA692CF8BABF00C37EE6 /* CredentialProviderActivatedView.swift */, + C1CAAA722CF8BD1C00C37EE6 /* CredentialProviderActivatedViewModel.swift */, + ); + path = CredentialProviderActivation; + sourceTree = ""; + }; + C1CAAA6D2CF8BBBC00C37EE6 /* CredentialProvider */ = { + isa = PBXGroup; + children = ( + C1EF5B252CC0457B002980E6 /* CredentialProviderViewController.swift */, + C1CAAA682CF8B8C400C37EE6 /* CredentialProviderActivation */, + C1CAAA862CF9FFBE00C37EE6 /* CredentialProviderList */, + C1C1FF442D085A280017ACCE /* CredentialProviderListDetails */, + C1CAAA6E2CF8BBC900C37EE6 /* Extensions */, + C1CAAA742CF8BDC400C37EE6 /* Resources */, + C1CAAAA42CFCBD6B00C37EE6 /* Shared */, + ); + path = CredentialProvider; + sourceTree = ""; + }; + C1CAAA6E2CF8BBC900C37EE6 /* Extensions */ = { + isa = PBXGroup; + children = ( + C177D9F52CFDDFEB0039CBF7 /* UIAlertControllerExtension.swift */, + C1CAAA9D2CFCB78700C37EE6 /* UIColorExtension.swift */, + C1CAAA9F2CFCB7C200C37EE6 /* UImageExtension.swift */, + C1CAAA842CF8C9EA00C37EE6 /* UIResponderExtension.swift */, + C1CAAA702CF8BC0B00C37EE6 /* UIViewControllerExtension.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + C1CAAA742CF8BDC400C37EE6 /* Resources */ = { + isa = PBXGroup; + children = ( + C1CAAA772CF8BDF200C37EE6 /* UserText.swift */, + C1CAAA792CF8BE0200C37EE6 /* Assets.xcassets */, + ); + path = Resources; + sourceTree = ""; + }; + C1CAAA862CF9FFBE00C37EE6 /* CredentialProviderList */ = { + isa = PBXGroup; + children = ( + C1CAAA912CFCAA0500C37EE6 /* CredentialProviderListItemTableViewCell.swift */, + C1CAAA872CF9FFE100C37EE6 /* CredentialProviderListViewController.swift */, + C1CAAA892CF9FFF300C37EE6 /* CredentialProviderListViewModel.swift */, + C1CAAAA72CFCBE4800C37EE6 /* EmptySearchView.swift */, + C1CAAAAB2CFCC91D00C37EE6 /* EmptyView.swift */, + ); + path = CredentialProviderList; + sourceTree = ""; + }; + C1CAAA962CFCAB8000C37EE6 /* Autofill */ = { + isa = PBXGroup; + children = ( + C1C1FF482D085A4C0017ACCE /* AutofillInterfaceEmailTruncator.swift */, + C1C1FF492D085A4C0017ACCE /* PasswordHider.swift */, + C19D90D02CFE3A7F00D17DF3 /* AutofillLoginListSectionType.swift */, + C1CAAA992CFCAD3E00C37EE6 /* AutofillLoginItem.swift */, + C1CAAA9B2CFCB39800C37EE6 /* AutofillLoginListSorting.swift */, + ); + name = Autofill; + sourceTree = ""; + }; + C1CAAAA12CFCBBAC00C37EE6 /* Authenticator */ = { + isa = PBXGroup; + children = ( + C1CAAAA22CFCBBBD00C37EE6 /* UserAuthenticator.swift */, + ); + name = Authenticator; + sourceTree = ""; + }; + C1CAAAA42CFCBD6B00C37EE6 /* Shared */ = { + isa = PBXGroup; + children = ( + C1C1FF4C2D085AD70017ACCE /* ActionMessageView.swift */, + C1C1FF4D2D085AD70017ACCE /* ActionMessageView.xib */, + C1C1FF4E2D085AD70017ACCE /* FaviconHelper.swift */, + C1CAAAA52CFCBD7900C37EE6 /* LockScreenView.swift */, + C1C1FF4F2D085AD70017ACCE /* NibLoading.swift */, + C1CAAAA92CFCC13E00C37EE6 /* SecureVaultReporter.swift */, + C13C076B2D00A6B7006386CF /* VaultCredentialManager.swift */, + ); + path = Shared; + sourceTree = ""; + }; C1EA865E2C74CB5500E8604D /* Promotion */ = { isa = PBXGroup; children = ( @@ -5398,6 +5513,19 @@ name = Promotion; sourceTree = ""; }; + C1EF5B242CC0457B002980E6 /* AutofillCredentialProvider */ = { + isa = PBXGroup; + children = ( + C1CAAA6D2CF8BBBC00C37EE6 /* CredentialProvider */, + C1CAAA672CF8B74200C37EE6 /* AutofillCredentialProviderAlpha.entitlements */, + C1EF5B2A2CC0457B002980E6 /* Info.plist */, + C1E4E9A72D0861AD00AA39AF /* Localizable.strings */, + C1E4E9A42D0861AD00AA39AF /* InfoPlist.strings */, + C1EF5B2B2CC0457B002980E6 /* AutofillCredentialProvider.entitlements */, + ); + path = AutofillCredentialProvider; + sourceTree = ""; + }; C1F341C32A6923D70032057B /* EmailAddressPrompt */ = { isa = PBXGroup; children = ( @@ -5472,6 +5600,7 @@ CBAD0EFC2CFE1D48006267B8 /* Active.swift */, CBAD0EFE2CFE1D4E006267B8 /* Inactive.swift */, CBAD0F002CFE1D54006267B8 /* Background.swift */, + CB3C78902D08483F00A7E4ED /* InactiveBackground.swift */, ); path = AppStates; sourceTree = ""; @@ -5489,7 +5618,6 @@ D62EC3B72C24695800FC9D04 /* DuckPlayer */ = { isa = PBXGroup; children = ( - D6B9E8D32CDA8369002B640C /* DuckPlayerOverlayUsagePixelsTests.swift */, D6B67A112C332B6E002122EB /* DuckPlayerMocks.swift */, D62EC3BB2C2470E000FC9D04 /* DuckPlayerTests.swift */, D62EC3B82C246A5600FC9D04 /* YoutublePlayerNavigationHandlerTests.swift */, @@ -6054,6 +6182,8 @@ F143C2E51E4A4CD400CFDE3A /* Core */ = { isa = PBXGroup; children = ( + C1CAAAA12CFCBBAC00C37EE6 /* Authenticator */, + C1CAAA962CFCAB8000C37EE6 /* Autofill */, 31B2F10D2C92FEB000CD30E3 /* MarketplaceAdPostback */, F1CE42A71ECA0A520074A8DF /* Bookmarks */, 837774491F8E1ECE00E17A29 /* ContentBlocker */, @@ -6284,6 +6414,7 @@ 8512EA4E24ED30D20073EE19 /* WidgetKit.framework */, 8512EA5024ED30D20073EE19 /* SwiftUI.framework */, 02025663298818B100E694E7 /* NetworkExtension.framework */, + C1EF5B222CC0457B002980E6 /* AuthenticationServices.framework */, ); name = Frameworks; sourceTree = ""; @@ -6388,7 +6519,6 @@ 983EABB7236198F6003948D1 /* DatabaseMigration.swift */, 853C5F6021C277C7001F7A05 /* global.swift */, 85C8E61C2B0E47380029A6BD /* BookmarksDatabaseSetup.swift */, - 9821234D2B6D0A6300F08C57 /* UserAuthenticator.swift */, 9821234F2B6D233E00F08C57 /* UserSession.swift */, ); name = Application; @@ -6473,6 +6603,7 @@ F1D796ED1E7AE4090019D451 /* UserInterface */ = { isa = PBXGroup; children = ( + 317DF6092D01E7C400DE0145 /* RoundedPageContainer */, 859872221F5743AF00041CB8 /* FireAnimation */, 1E162603296840790004127F /* SwiftUI */, 982686AC2600C0850011A8D6 /* ActionMessageView.swift */, @@ -6526,6 +6657,7 @@ F1E092B31E92A6B900732CCC /* Core */ = { isa = PBXGroup; children = ( + 310EEA2C2CFFCD9B0043CA1A /* New Group */, 316790E32C9350980090B0A2 /* MarketplaceAdPostback */, 858479CA2B8795BF00D156C1 /* History */, EA7EFE662677F5BD0075464E /* PrivacyReferenceTests */, @@ -6536,6 +6668,7 @@ EE3B226929DE0EE10082298A /* FeatureFlags */, F1134EC91F40E74800B73467 /* Statistics */, F198D78F1E3976300088DA8A /* Utilities */, + 310EEA2D2CFFCDB60043CA1A /* AIChat */, ); name = Core; sourceTree = ""; @@ -6582,7 +6715,6 @@ C1D21E2C293A5965006E5A05 /* AutofillLoginSession.swift */, C1CDA3152AFB9C7F006D1476 /* AutofillNeverPromptWebsitesManager.swift */, C13B32D12A0E750700A59236 /* AutofillSettingStatus.swift */, - 319A370F28299A850079FBCE /* PasswordHider.swift */, 31C70B5428045E3500FB6AD1 /* SecureVaultReporter.swift */, C1AFFC4B2B8773060060448E /* AuthConfirmation */, F407605328131910006B1E0B /* AutofillLoginUI */, @@ -6724,6 +6856,7 @@ 85482D932462DCD100EDEDD1 /* PBXTargetDependency */, 8512EA5C24ED30D30073EE19 /* PBXTargetDependency */, 02FFD7BC2A1FC8BE007BD7D1 /* PBXTargetDependency */, + C1EF5B2D2CC0457B002980E6 /* PBXTargetDependency */, ); name = DuckDuckGo; packageProductDependencies = ( @@ -6739,6 +6872,7 @@ 9F8FE9482BAE50E50071E372 /* Lottie */, 9F96F73A2C9144D5009E45D5 /* Onboarding */, 1E5918462CA422A7008ED2B3 /* Navigation */, + 315C77812CFA41A400699683 /* AIChat */, ); productName = DuckDuckGo; productReference = 84E341921E2F7EFB00BDBA6F /* DuckDuckGo.app */; @@ -6940,6 +7074,24 @@ productReference = B6DFE6CF2BC7E47500A9CE59 /* SwiftLintTool.bundle */; productType = "com.apple.product-type.bundle"; }; + C1EF5B202CC0457B002980E6 /* AutofillCredentialProvider */ = { + isa = PBXNativeTarget; + buildConfigurationList = C1EF5B332CC0457B002980E6 /* Build configuration list for PBXNativeTarget "AutofillCredentialProvider" */; + buildPhases = ( + C1EF5B1D2CC0457B002980E6 /* Sources */, + C1EF5B1E2CC0457B002980E6 /* Frameworks */, + C1EF5B1F2CC0457B002980E6 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + C1CAAA7E2CF8C8ED00C37EE6 /* PBXTargetDependency */, + ); + name = AutofillCredentialProvider; + productName = AutofillCredentialProvider; + productReference = C1EF5B212CC0457B002980E6 /* AutofillCredentialProvider.appex */; + productType = "com.apple.product-type.app-extension"; + }; F143C2E31E4A4CD400CFDE3A /* Core */ = { isa = PBXNativeTarget; buildConfigurationList = F143C2ED1E4A4CD400CFDE3A /* Build configuration list for PBXNativeTarget "Core" */; @@ -6976,6 +7128,8 @@ CB6D8E972C80A9B100D0E772 /* SpecialErrorPages */, CB6CC7E32CD2529000320907 /* BrokenSitePrompt */, CBECDB6E2CD3DFBE005B8B87 /* PageRefreshMonitor */, + 56D779392CFFC7E800B619EF /* PixelExperimentKit */, + 56D7793B2CFFC7E800B619EF /* PixelKit */, ); productName = Core; productReference = F143C2E41E4A4CD400CFDE3A /* Core.framework */; @@ -7046,6 +7200,9 @@ B6DFE6CE2BC7E47500A9CE59 = { CreatedOnToolsVersion = 15.3; }; + C1EF5B202CC0457B002980E6 = { + CreatedOnToolsVersion = 15.4; + }; F143C2E31E4A4CD400CFDE3A = { CreatedOnToolsVersion = 8.2.1; LastSwiftMigration = 1020; @@ -7114,6 +7271,7 @@ 85482D872462DCD100EDEDD1 /* OpenAction */, 8512EA4C24ED30D20073EE19 /* WidgetsExtension */, 02025661298818B100E694E7 /* PacketTunnelProvider */, + C1EF5B202CC0457B002980E6 /* AutofillCredentialProvider */, F143C2E31E4A4CD400CFDE3A /* Core */, 98A54A8022AFCB2C00E541F4 /* Instruments */, 85F21DAC210F5E32002631A6 /* AtbUITests */, @@ -7158,21 +7316,11 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - AA4D6A8D23DE49A5007E8790 /* AppIconBlack40x40@3x.png in Resources */, F47E53DB250A9A1C0037C686 /* Onboarding.xcassets in Resources */, - AA4D6ACC23DE4D27007E8790 /* AppIconPurple60x60@2x.png in Resources */, 1E242960293F585300584836 /* cookie-icon-animated-40-light.json in Resources */, - AA4D6AA223DE4CC4007E8790 /* AppIconBlue76x76@2x.png in Resources */, - AA4D6AB823DE4D15007E8790 /* AppIconYellow29x29@2x.png in Resources */, 984147C024F026A300362052 /* Tab.storyboard in Resources */, 4B6ED9452B992FE4007F5CAA /* vpn-dark-mode.json in Resources */, 02F880642AB206740020C2DF /* PrivacyInfo.xcprivacy in Resources */, - AA4D6AE123DE4D33007E8790 /* AppIconGreen76x76@2x.png in Resources */, - AA4D6A9123DE49A5007E8790 /* AppIconBlack60x60@3x.png in Resources */, - AA4D6A8E23DE49A5007E8790 /* AppIconBlack60x60@2x.png in Resources */, - AA4D6AC023DE4D15007E8790 /* AppIconYellow76x76@2x.png in Resources */, - AA4D6AA423DE4CC4007E8790 /* AppIconBlue29x29@2x.png in Resources */, - AA4D6AE323DE4D33007E8790 /* AppIconGreen60x60@2x.png in Resources */, F41610BC29E5DF66001F709D /* DeprecatedColors.xcassets in Resources */, 9F8E0F2A2CCA5C9D001EA7C5 /* add-to-dock-demo.mp4 in Resources */, F4F7F10B25813FE200045D62 /* 02_Water_swirl_really_small.json in Resources */, @@ -7182,65 +7330,44 @@ 1EEF12452850A923003DDE57 /* shield.json in Resources */, 85DFEDF724CB1CAB00973FE7 /* ShareSheet.xcassets in Resources */, 98DA6B3322243CC3006EA9EB /* Feedback.xcassets in Resources */, - AA4D6ABD23DE4D15007E8790 /* AppIconYellow60x60@2x.png in Resources */, 1EE411F728587AC50003FE64 /* PrivacyIcon.xcassets in Resources */, 8548D96825262C33005AAE49 /* view_highlight.json in Resources */, - AA4D6AD423DE4D27007E8790 /* AppIconPurple40x40@3x.png in Resources */, 85F0E97329952D7A003D5181 /* DuckDuckGo Recovery Document.pdf in Resources */, - AA4D6AD123DE4D27007E8790 /* AppIconPurple40x40@2x.png in Resources */, 9880723725FA4E450039EF4B /* menu_dark.json in Resources */, - AAF2E28B23E049DF00962AF8 /* AppIconYellow83.5x83.5@2x.png in Resources */, 1E162615296D910F0004127F /* cookie-icon-animated-40-dark.json in Resources */, 85514FFD2372DA0100DBC528 /* ios13-home-row.mp4 in Resources */, 85F98F98296F4CB100742F4A /* SyncAssets.xcassets in Resources */, 31BC5F412C2B0B540004DF37 /* DuckPlayer.xcassets in Resources */, - AA4D6A9423DE49A5007E8790 /* AppIconBlack29x29@2x.png in Resources */, 7BDBAD0E2CBFB3F1000379B7 /* VPN.xcassets in Resources */, 98B001B3251EABB40090EC07 /* InfoPlist.strings in Resources */, - AA4D6ACE23DE4D27007E8790 /* AppIconPurple60x60@3x.png in Resources */, D65CEA702B6AC6C9008A759B /* Subscription.xcassets in Resources */, F1E4A4451EE89460006F2EAE /* Bookmarks.storyboard in Resources */, - AA4D6ABB23DE4D15007E8790 /* AppIconYellow40x40@2x.png in Resources */, 84E341A01E2F7EFB00BDBA6F /* LaunchScreen.storyboard in Resources */, 98D16973250CE707009513CC /* OmniBar.xib in Resources */, - AAF2E28523E0496F00962AF8 /* AppIconGreen83.5x83.5@2x.png in Resources */, F4F7F10C25813FE200045D62 /* 03_Airstream_divided_by_four.json in Resources */, - AAF2E28723E0498200962AF8 /* AppIconPurple83.5x83.5@2x.png in Resources */, - AA4D6AB923DE4D15007E8790 /* AppIconYellow29x29@3x.png in Resources */, 6F64AA5B2C481AAA00CF4489 /* Shortcuts.xcassets in Resources */, 984147B424F0264B00362052 /* Authentication.storyboard in Resources */, 1EE411FD2858B9300003FE64 /* dark-trackers-2.json in Resources */, - AA4D6ABC23DE4D15007E8790 /* AppIconYellow60x60@3x.png in Resources */, 98D98A9B25ED954100D8E3DF /* BrowsingMenuButton.xib in Resources */, D664C7B72B289AA200CBFA76 /* Subscription.storekit in Resources */, - AA4D6AA823DE4CC4007E8790 /* AppIconBlue40x40@2x.png in Resources */, - AA4D6AE723DE4D33007E8790 /* AppIconGreen29x29@2x.png in Resources */, 1EE412002858B9300003FE64 /* dark-shield-dot.json in Resources */, 1EE412012858B9300003FE64 /* dark-trackers-3.json in Resources */, - AA4D6ACD23DE4D27007E8790 /* AppIconPurple29x29@3x.png in Resources */, - AA4D6A8C23DE49A5007E8790 /* AppIconBlack40x40@2x.png in Resources */, 1EEF12462850A923003DDE57 /* trackers-3.json in Resources */, 85AE668E2097206E0014CF04 /* NotificationView.xib in Resources */, - AA4D6AFA23DF0CF6007E8790 /* AppIconRed29x29@3x.png in Resources */, 85A313972028E78A00327D00 /* release_notes.txt in Resources */, 9865DFFD22A84CF300D27829 /* FavoriteHomeCell.xib in Resources */, 1EE411FE2858B9300003FE64 /* dark-shield.json in Resources */, - AA4D6AD323DE4D27007E8790 /* AppIconPurple29x29@2x.png in Resources */, - AA4D6AA123DE4CC4007E8790 /* AppIconBlue60x60@3x.png in Resources */, 1DDF402B2BA05A65006850D9 /* Settings.xcassets in Resources */, 984147A824F0259000362052 /* Onboarding.storyboard in Resources */, - AA4D6AF723DF0312007E8790 /* AppIconRed60x60@2x.png in Resources */, - AA4D6AE923DE4D33007E8790 /* AppIconGreen29x29@3x.png in Resources */, 984147AE24F0261A00362052 /* Feedback.storyboard in Resources */, 984147B724F0268D00362052 /* PrivacyDashboard.storyboard in Resources */, - AA4D6AA723DE4CC4007E8790 /* AppIconBlue60x60@2x.png in Resources */, 1EEF12532851D32B003DDE57 /* trackers-2.json in Resources */, F176699F1E40BC86003D3222 /* Settings.storyboard in Resources */, 854A012F2A5563A400FCC628 /* FindInPage.xib in Resources */, 1E8AD1DD27C653F800ABA377 /* Downloads.xcassets in Resources */, F1F533841F26ABAC00D80D4F /* Localizable.strings in Resources */, 1EEF12472850A923003DDE57 /* shield-dot.json in Resources */, - AA4D6AFB23DF0CF6007E8790 /* AppIconRed29x29@2x.png in Resources */, + 6F8348E32D01E401005872E3 /* AlternateAppIcons.xcassets in Resources */, F1ED309D1EDC2EA400651986 /* TabSwitcher.storyboard in Resources */, 8524AAAC2A3888FE00EEC6D2 /* Waitlist.xcassets in Resources */, 982686B92600C0960011A8D6 /* ActionMessageView.xib in Resources */, @@ -7252,29 +7379,17 @@ 858650DB246B111900C36F8A /* DaxOnboarding.xcassets in Resources */, 984147C324F026C800362052 /* HomeRow.storyboard in Resources */, B6BA95E828924730004ABA20 /* JSAlertController.storyboard in Resources */, - AA4D6AF623DF0312007E8790 /* AppIconRed60x60@3x.png in Resources */, - AA4D6AE423DE4D33007E8790 /* AppIconGreen40x40@3x.png in Resources */, - AAF2E28323E0495E00962AF8 /* AppIconBlue83.5x83.5@2x.png in Resources */, - AA4D6AE223DE4D33007E8790 /* AppIconGreen40x40@2x.png in Resources */, 85EE7F55224667DD000FE757 /* WebContainer.storyboard in Resources */, 858566E8252E4F56007501B8 /* Debug.storyboard in Resources */, 0A6CC0EF23904D5400E4F627 /* Settings.bundle in Resources */, 85A9C37920E0E00C00073340 /* HomeRow.xcassets in Resources */, B6BA95C528894A28004ABA20 /* BrowsingMenuViewController.storyboard in Resources */, - AA4D6AE623DE4D33007E8790 /* AppIconGreen60x60@3x.png in Resources */, - AA4D6A9323DE49A5007E8790 /* AppIconBlack76x76@2x.png in Resources */, 850ABD032AC4D46C00A733DF /* SuggestionTray.storyboard in Resources */, 1E908BF229827C480008C8F3 /* autoconsent-bundle.js in Resources */, F143C2B21E49D78C00CFDE3A /* Assets.xcassets in Resources */, - AA4D6AA323DE4CC4007E8790 /* AppIconBlue40x40@3x.png in Resources */, 1EEF12542851D32B003DDE57 /* trackers-1.json in Resources */, - AAF2E28123E0495400962AF8 /* AppIconBlack83.5x83.5@2x.png in Resources */, - AA4D6ABF23DE4D15007E8790 /* AppIconYellow40x40@3x.png in Resources */, - AA4D6A8F23DE49A5007E8790 /* AppIconBlack29x29@3x.png in Resources */, - AA4D6AA523DE4CC4007E8790 /* AppIconBlue29x29@3x.png in Resources */, 9F72FE272CD223A000BA35F5 /* add-to-dock-promo.json in Resources */, 1EEF124C2850A93F003DDE57 /* Trackers.xcassets in Resources */, - AA4D6ACF23DE4D27007E8790 /* AppIconPurple76x76@2x.png in Resources */, 4B37E0502B928CA6009E81CA /* vpn-light-mode.json in Resources */, 9830A06325ED0DB900DB64DE /* BrowsingMenu.xcassets in Resources */, 98EF177D21837E35006750C1 /* new_tab_dark.json in Resources */, @@ -7358,6 +7473,17 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + C1EF5B1F2CC0457B002980E6 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C1C1FF502D085AD70017ACCE /* ActionMessageView.xib in Resources */, + C1E4E9A92D0861AD00AA39AF /* Localizable.strings in Resources */, + C1E4E9A62D0861AD00AA39AF /* InfoPlist.strings in Resources */, + C1CAAA7A2CF8BE0200C37EE6 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; F143C2E21E4A4CD400CFDE3A /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -7574,7 +7700,6 @@ 6F03CAFE2C32DD08004179A8 /* HomePageMessagesConfiguration.swift in Sources */, 851952682CE2522700578553 /* AutocompleteSuggestionsDataSource.swift in Sources */, EE9D68D12AE00CF300B55EF4 /* NetworkProtectionVPNSettingsView.swift in Sources */, - 319A371028299A850079FBCE /* PasswordHider.swift in Sources */, 982C87C42255559A00919035 /* UITableViewCellExtension.swift in Sources */, B623C1C42862CD670043013E /* WKDownloadSession.swift in Sources */, 6FD1BAE42B87A107000C475C /* AdAttributionPixelReporter.swift in Sources */, @@ -7692,7 +7817,6 @@ 6FBF0F8B2BD7C0A900136CF0 /* AllProtectedCell.swift in Sources */, 9F4CC5242C4A4F0D006A96EB /* SwiftUITestUtilities.swift in Sources */, 6FDC64032C92F4D600DB71B3 /* NewTabPageSettingsPersistentStore.swift in Sources */, - 9F1061652C9C013F008DD5A0 /* DefaultVariantManager+Onboarding.swift in Sources */, 1E4F4A5A297193DE00625985 /* MainViewController+CookiesManaged.swift in Sources */, C12324C32C4697C900FBB26B /* AutofillBreakageReportTableViewCell.swift in Sources */, 8586A10D24CBA7070049720E /* FindInPageActivity.swift in Sources */, @@ -7804,6 +7928,7 @@ D668D92B2B696840008E2FF2 /* IdentityTheftRestorationPagesFeature.swift in Sources */, 85F2FFCF2211F8E5006BB258 /* TabSwitcherViewController+KeyCommands.swift in Sources */, 3157B43327F497E90042D3D7 /* SaveLoginView.swift in Sources */, + 316AA45A2CF8E31F00A2ED28 /* AIChatSettings.swift in Sources */, F17922E01E71BB59006E3D97 /* AutocompleteViewControllerDelegate.swift in Sources */, BDE91CDC2C62AA3A0005CB74 /* DefaultMetadataCollector.swift in Sources */, D664C7C82B289AA200CBFA76 /* SubscriptionFlowView.swift in Sources */, @@ -7823,7 +7948,6 @@ 6F5CC0812C2AFFE400AFC840 /* ToggleExpandButtonStyle.swift in Sources */, D68A21462B7EC16200BB372E /* SubscriptionExternalLinkViewModel.swift in Sources */, 31DE43C22C2C480D00F8C51F /* DuckPlayerFeaturePresentationView.swift in Sources */, - F4C9FBF528340DDA002281CC /* AutofillInterfaceEmailTruncator.swift in Sources */, 1E016AB42949FEB500F21625 /* OmniBarNotificationViewModel.swift in Sources */, 6AC6DAB328804F97002723C0 /* BarsAnimator.swift in Sources */, CB4FA44E2C78AACE00A16F5A /* SpecialErrorPageUserScript.swift in Sources */, @@ -7915,6 +8039,7 @@ 8598D2E32CEB98B500C45685 /* FaviconUserScript.swift in Sources */, 8598D2E42CEB98B500C45685 /* FaviconSourcesProvider.swift in Sources */, BD862E052B30DB250073E2EE /* VPNFeedbackCategory.swift in Sources */, + 317DF60B2D01E7D600DE0145 /* RoundedPageSheetPresentationAnimator.swift in Sources */, 85AE6690209724120014CF04 /* NotificationView.swift in Sources */, BDE91CE02C6515420005CB74 /* UnifiedFeedbackFormViewModel.swift in Sources */, 1EA51376286596A000493C6A /* PrivacyIconLogic.swift in Sources */, @@ -7941,6 +8066,7 @@ 98F78B8E22419093007CACF4 /* ThemableNavigationController.swift in Sources */, CBD4F140279EBFB300B20FD7 /* SwiftUICollectionViewCell.swift in Sources */, 31CC224928369B38001654A4 /* AutofillLoginSettingsListViewController.swift in Sources */, + 311711912D00E5500063AC3D /* OmnibarAccessoryHandling.swift in Sources */, F1D796EC1E7AB8930019D451 /* SaveBookmarkActivity.swift in Sources */, F4B0B78C252CAFF700830156 /* OnboardingWidgetsViewController.swift in Sources */, 7BFD5FD52C9DA310000FF959 /* VPNAddWidgetTip.swift in Sources */, @@ -7981,7 +8107,6 @@ 6FB2A6802C2EA950004D20C8 /* FavoritesViewModel.swift in Sources */, 9817C9C321EF594700884F65 /* AutoClear.swift in Sources */, 9FE05CEE2C36424E00D9046B /* OnboardingPixelReporter.swift in Sources */, - 9821234E2B6D0A6300F08C57 /* UserAuthenticator.swift in Sources */, 310C4B47281B60E300BA79A9 /* AutofillLoginDetailsViewModel.swift in Sources */, 85EE7F572246685B000FE757 /* WebContainerViewController.swift in Sources */, CB48D3332B90CE9F00631D8B /* PageRefreshStore.swift in Sources */, @@ -8000,6 +8125,7 @@ BD862E072B30F5E30073E2EE /* VPNFeedbackSender.swift in Sources */, AA4D6A6A23DB87B1007E8790 /* AppIconManager.swift in Sources */, 8563A03C1F9288D600F04442 /* BrowserChromeManager.swift in Sources */, + 317DF6082D01E7B900DE0145 /* RoundedPageSheetContainerViewController.swift in Sources */, 980891A32237146B00313A70 /* Feedback.swift in Sources */, F1D796F01E7B07610019D451 /* BookmarksViewControllerCells.swift in Sources */, 9F9EE4D42C37BB1300D4118E /* OnboardingView+Landing.swift in Sources */, @@ -8092,10 +8218,10 @@ 1DEAADF42BA47B5300E25A97 /* WebTrackingProtectionView.swift in Sources */, F15D43201E706CC500BF2CDC /* AutocompleteViewController.swift in Sources */, BD862E092B30F63E0073E2EE /* VPNMetadataCollector.swift in Sources */, + CB3C78912D08484800A7E4ED /* InactiveBackground.swift in Sources */, D6E83C682B23B6A3006C8AFB /* FontSettings.swift in Sources */, 7BF78E022CA2CC3E0026A1FC /* TipKitAppEventHandling.swift in Sources */, 1DEAADF62BA4809400E25A97 /* CookiePopUpProtectionView.swift in Sources */, - 31EF52E1281B3BDC0034796E /* AutofillLoginListItemViewModel.swift in Sources */, C1EA86602C74CB6C00E8604D /* SyncPromoView.swift in Sources */, 1E4FAA6627D8DFC800ADC5B3 /* CompleteDownloadRowViewModel.swift in Sources */, 3712091E2C21E390003ADF3D /* RemoteMessagingStoreErrorHandling.swift in Sources */, @@ -8115,6 +8241,7 @@ 46DD3D5A2D0A29F600F33D49 /* CrashReportSenderExtensions.swift in Sources */, 1DEAADEA2BA4539800E25A97 /* SettingsAppearanceView.swift in Sources */, B623C1C22862CA9E0043013E /* DownloadSession.swift in Sources */, + 317CA3432CFF82E100F88848 /* SettingsAIChatView.swift in Sources */, 9F7CFF7F2C8A94F70012833E /* OnboardingView+AddressBarPositionContent.swift in Sources */, 985892522260B1B200EEB31B /* ProgressView.swift in Sources */, 85BA585A1F3506AE00C6E8CA /* AppSettings.swift in Sources */, @@ -8228,9 +8355,7 @@ 5694372B2BE3F2D900C0881B /* SyncErrorHandlerTests.swift in Sources */, 4B27FBB52C927435007E21A7 /* PersistentPixelTests.swift in Sources */, 987130C7294AAB9F00AB05E0 /* MenuBookmarksViewModelTests.swift in Sources */, - D6B9E8D42CDA8375002B640C /* DuckPlayerOverlayUsagePixelsTests.swift in Sources */, 858650D32469BFAD00C36F8A /* DaxDialogTests.swift in Sources */, - 9F1623092C9D14F10093C4FC /* DefaultVariantManagerOnboardingTests.swift in Sources */, 31C138B227A4097800FFD4B2 /* DownloadTestsHelper.swift in Sources */, 1E1D8B5D2994FFE100C96994 /* AutoconsentMessageProtocolTests.swift in Sources */, 85C11E532090B23A00BFFEB4 /* UserDefaultsHomeRowReminderStorageTests.swift in Sources */, @@ -8317,6 +8442,7 @@ 987130C8294AAB9F00AB05E0 /* BookmarksTestHelpers.swift in Sources */, 9F4CC51D2C48D240006A96EB /* CoreDataDatabaseTestUtilities.swift in Sources */, C185ED672BD43DA100BAE9DC /* ImportPasswordsStatusHandlerTests.swift in Sources */, + 310EEA2F2CFFCDC60043CA1A /* AIChatSettingsTests.swift in Sources */, 6FF9AD452CE766F700C5A406 /* NewTabPageControllerPixelTests.swift in Sources */, 85C503FD2CF0E7B10075DF6F /* MockFireproofing.swift in Sources */, 6F3529FF2CDCEDFF00A59170 /* OmniBarLoadingStateBearerTests.swift in Sources */, @@ -8336,6 +8462,7 @@ 9F6933192C59BB0300CD6A5D /* OnboardingPixelReporterMock.swift in Sources */, CBC88EE12C7F834300F0F8C5 /* SpecialErrorPageUserScriptTests.swift in Sources */, 56D0602D2C383FD2003BAEB5 /* OnboardingSuggestedSearchesProviderTests.swift in Sources */, + 31E77B272D038BBC006F1C9F /* OmnibarAccessoryHandlerTests.swift in Sources */, 569437242BDD405400C0881B /* SyncBookmarksAdapterTests.swift in Sources */, 858479CD2B87964500D156C1 /* HistoryManagerTests.swift in Sources */, CBCCF96828885DEE006F4A71 /* AppPrivacyConfigurationTests.swift in Sources */, @@ -8474,12 +8601,43 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + C1EF5B1D2CC0457B002980E6 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C1CAAA922CFCAA0500C37EE6 /* CredentialProviderListItemTableViewCell.swift in Sources */, + C1CAAAAC2CFCC91D00C37EE6 /* EmptyView.swift in Sources */, + C1CAAA712CF8BC0B00C37EE6 /* UIViewControllerExtension.swift in Sources */, + C1CAAA6A2CF8BABF00C37EE6 /* CredentialProviderActivatedView.swift in Sources */, + C1CAAA732CF8BD1C00C37EE6 /* CredentialProviderActivatedViewModel.swift in Sources */, + C13C076C2D00A6B7006386CF /* VaultCredentialManager.swift in Sources */, + C1EF5B262CC0457B002980E6 /* CredentialProviderViewController.swift in Sources */, + C1CAAA852CF8C9EA00C37EE6 /* UIResponderExtension.swift in Sources */, + C1CAAA782CF8BDF200C37EE6 /* UserText.swift in Sources */, + C1CAAAA62CFCBD7900C37EE6 /* LockScreenView.swift in Sources */, + C1CAAA9E2CFCB78700C37EE6 /* UIColorExtension.swift in Sources */, + C1CAAA8A2CF9FFF300C37EE6 /* CredentialProviderListViewModel.swift in Sources */, + C1CAAAA02CFCB7C200C37EE6 /* UImageExtension.swift in Sources */, + C1C1FF512D085AD70017ACCE /* FaviconHelper.swift in Sources */, + C1C1FF522D085AD70017ACCE /* ActionMessageView.swift in Sources */, + C1C1FF532D085AD70017ACCE /* NibLoading.swift in Sources */, + C177D9F62CFDDFEB0039CBF7 /* UIAlertControllerExtension.swift in Sources */, + C1CAAA882CF9FFE100C37EE6 /* CredentialProviderListViewController.swift in Sources */, + C1C1FF452D085A280017ACCE /* CredentialProviderListDetailsView.swift in Sources */, + C1C1FF462D085A280017ACCE /* CredentialProviderListDetailsViewController.swift in Sources */, + C1C1FF472D085A280017ACCE /* CredentialProviderListDetailsViewModel.swift in Sources */, + C1CAAAAA2CFCC13E00C37EE6 /* SecureVaultReporter.swift in Sources */, + C1CAAAA82CFCBE4800C37EE6 /* EmptySearchView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; F143C2DF1E4A4CD400CFDE3A /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( 858479C92B8792D800D156C1 /* HistoryManager.swift in Sources */, F16393FF1ECCB9CC00DDD653 /* FileLoader.swift in Sources */, + C1CAAAA32CFCBBBD00C37EE6 /* UserAuthenticator.swift in Sources */, F1134EAB1F3E2C6A00B73467 /* StatisticsUserDefaults.swift in Sources */, 4B27FBB32C926E51007E21A7 /* PersistentPixel.swift in Sources */, CB258D1E29A52AF900DEBA24 /* FileStore.swift in Sources */, @@ -8496,6 +8654,7 @@ 9876B75E2232B36900D81D9F /* TabInstrumentation.swift in Sources */, 026DABA428242BC80089E0B5 /* MockUserAgent.swift in Sources */, 1E05D1D829C46EDA00BF9A1F /* TimedPixel.swift in Sources */, + C1CAAA9A2CFCAD3E00C37EE6 /* AutofillLoginItem.swift in Sources */, 6F9857342CD27FA2001BE9A0 /* BoolFileMarker.swift in Sources */, C14882DC27F2011C00D59F0C /* BookmarksImporter.swift in Sources */, CBAA195A27BFE15600A4BD49 /* NSManagedObjectContextExtension.swift in Sources */, @@ -8506,6 +8665,8 @@ 85BDC3192436161C0053DB07 /* LoginFormDetectionUserScript.swift in Sources */, 37DF000A29F9C416002B7D3E /* SyncMetadataDatabase.swift in Sources */, F143C3291E4A9A0E00CFDE3A /* URLExtension.swift in Sources */, + C1C1FF4A2D085A4C0017ACCE /* AutofillInterfaceEmailTruncator.swift in Sources */, + C1C1FF4B2D085A4C0017ACCE /* PasswordHider.swift in Sources */, 85E065BC2C73A54700D73E2A /* UsageSegmentationStorage.swift in Sources */, F143C3271E4A9A0E00CFDE3A /* Logger+Multiple.swift in Sources */, 85372447220DD103009D09CD /* UIKeyCommandExtension.swift in Sources */, @@ -8534,6 +8695,7 @@ 830381C01F850AAF00863075 /* WKWebViewConfigurationExtension.swift in Sources */, 4B60ACA1252EC0B100E8D219 /* FullScreenVideoUserScript.swift in Sources */, F1A886781F29394E0096251E /* WebCacheManager.swift in Sources */, + C1CAAA9C2CFCB39800C37EE6 /* AutofillLoginListSorting.swift in Sources */, 6F03CB072C32F173004179A8 /* PixelFiring.swift in Sources */, C14882DA27F2011C00D59F0C /* BookmarksExporter.swift in Sources */, 854858E32937BC550063610B /* CollectionExtension.swift in Sources */, @@ -8581,6 +8743,7 @@ 1E05D1D629C46EBB00BF9A1F /* DailyPixel.swift in Sources */, 1CB7B82123CEA1F800AA24EA /* DateExtension.swift in Sources */, 379E877429E97C8D001C8BB0 /* BookmarksCleanupErrorHandling.swift in Sources */, + C19D90D12CFE3A7F00D17DF3 /* AutofillLoginListSectionType.swift in Sources */, 988F3DCF237D5C0F00AEE34C /* SchemeHandler.swift in Sources */, 9875E00722316B8400B1373F /* Instruments.swift in Sources */, 85E065BA2C73A4DF00D73E2A /* UsageSegmentation.swift in Sources */, @@ -8671,6 +8834,16 @@ target = B6DFE6CE2BC7E47500A9CE59 /* SwiftLintToolBundle */; targetProxy = B6DFE6D72BC7E49800A9CE59 /* PBXContainerItemProxy */; }; + C1CAAA7E2CF8C8ED00C37EE6 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F143C2E31E4A4CD400CFDE3A /* Core */; + targetProxy = C1CAAA7D2CF8C8ED00C37EE6 /* PBXContainerItemProxy */; + }; + C1EF5B2D2CC0457B002980E6 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = C1EF5B202CC0457B002980E6 /* AutofillCredentialProvider */; + targetProxy = C1EF5B2C2CC0457B002980E6 /* PBXContainerItemProxy */; + }; F143C2EA1E4A4CD400CFDE3A /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = F143C2E31E4A4CD400CFDE3A /* Core */; @@ -9231,6 +9404,68 @@ name = OmniBar.xib; sourceTree = ""; }; + C1E4E9A42D0861AD00AA39AF /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + C1E4E9A52D0861AD00AA39AF /* bg */, + C164F9472D0861D600BAE88E /* cs */, + C18D7C412D08620D00FB3F87 /* da */, + C1DCF3502D0862330055F8B0 /* de */, + C174E08E2D08625300ACE1AF /* el */, + C1F883372D08627900DFF79A /* es */, + C1CB0AA52D08629800335287 /* et */, + C132F5A32D0862B8000C81D0 /* fi */, + C129DF092D0862D7007AB046 /* fr */, + C1406D862D0862F30082CB50 /* hr */, + C1B783DB2D0863110071C53B /* hu */, + C1FEDCEA2D08633100BFBF3F /* it */, + C1A001092D08635100372C87 /* lt */, + C12854D82D08636E00C8353F /* lv */, + C163677A2D08638C001D1094 /* nb */, + C1FE93E52D0863AA009F8F5E /* nl */, + C1453E322D0863C80024449B /* pl */, + C180CAFA2D0863E500ADB0FE /* pt */, + C1B7BF522D08640B0024FF56 /* ro */, + C1193F602D08642900CB3239 /* ru */, + C1588FC42D08644800C9BE70 /* sk */, + C1E490582D08646400F86C5A /* sl */, + C11C4D302D08648100288E85 /* sv */, + C14D37D62D08649E00FCFC59 /* tr */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + C1E4E9A72D0861AD00AA39AF /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + C1E4E9A82D0861AD00AA39AF /* bg */, + C164F9482D0861D600BAE88E /* cs */, + C18D7C422D08620D00FB3F87 /* da */, + C1DCF3512D0862330055F8B0 /* de */, + C174E08F2D08625300ACE1AF /* el */, + C1F883382D08627900DFF79A /* es */, + C1CB0AA62D08629800335287 /* et */, + C132F5A42D0862B8000C81D0 /* fi */, + C129DF0A2D0862D7007AB046 /* fr */, + C1406D872D0862F30082CB50 /* hr */, + C1B783DC2D0863110071C53B /* hu */, + C1FEDCEB2D08633100BFBF3F /* it */, + C1A0010A2D08635100372C87 /* lt */, + C12854D92D08636E00C8353F /* lv */, + C163677B2D08638C001D1094 /* nb */, + C1FE93E62D0863AA009F8F5E /* nl */, + C1453E332D0863C80024449B /* pl */, + C180CAFB2D0863E500ADB0FE /* pt */, + C1B7BF532D08640B0024FF56 /* ro */, + C1193F612D08642900CB3239 /* ru */, + C1588FC52D08644800C9BE70 /* sk */, + C1E490592D08646400F86C5A /* sl */, + C11C4D312D08648100288E85 /* sv */, + C14D37D72D08649E00FCFC59 /* tr */, + ); + name = Localizable.strings; + sourceTree = ""; + }; CB1143DC2AF6D4B600C1CCD3 /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( @@ -9731,7 +9966,9 @@ 84E341BB1E2F7EFC00BDBA6F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = "AppIcon-Yellow AppIcon-Blue AppIcon-Purple AppIcon-Green AppIcon-Black"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -9755,7 +9992,9 @@ 84E341BC1E2F7EFC00BDBA6F /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = "AppIcon-Yellow AppIcon-Blue AppIcon-Purple AppIcon-Green AppIcon-Black"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; @@ -10243,6 +10482,158 @@ }; name = Release; }; + C1EF5B2F2CC0457B002980E6 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = AutofillCredentialProvider/AutofillCredentialProvider.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = HKE973VLUW; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = AutofillCredentialProvider/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = AutofillCredentialProvider; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 DuckDuckGo. All rights reserved."; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.duckduckgo.mobile.ios.CredentialExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + C1EF5B302CC0457B002980E6 /* Alpha Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = AutofillCredentialProvider/AutofillCredentialProviderAlpha.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = HKE973VLUW; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = AutofillCredentialProvider/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = AutofillCredentialProvider; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 DuckDuckGo. All rights reserved."; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.duckduckgo.mobile.ios.alpha.CredentialExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = "Alpha Debug"; + }; + C1EF5B312CC0457B002980E6 /* Alpha */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = AutofillCredentialProvider/AutofillCredentialProviderAlpha.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = AutofillCredentialProvider/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = AutofillCredentialProvider; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 DuckDuckGo. All rights reserved."; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.duckduckgo.mobile.ios.alpha.CredentialExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.duckduckgo.mobile.ios.alpha.CredentialExtension"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Alpha; + }; + C1EF5B322CC0457B002980E6 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = AutofillCredentialProvider/AutofillCredentialProvider.entitlements; + CODE_SIGN_IDENTITY = "iPhone Distribution"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = AutofillCredentialProvider/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = AutofillCredentialProvider; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 DuckDuckGo. All rights reserved."; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.duckduckgo.mobile.ios.CredentialExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = "match AppStore com.duckduckgo.mobile.ios.CredentialExtension"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; D664C7DE2B28A0FD00CBFA76 /* Alpha Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = EEB8FDB92A990AEE00EBEDCF /* Configuration-Alpha.xcconfig */; @@ -10308,7 +10699,9 @@ D664C7DF2B28A0FD00CBFA76 /* Alpha Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = "AppIcon-Yellow AppIcon-Blue AppIcon-Purple AppIcon-Green AppIcon-Black"; ASSETCATALOG_COMPILER_APPICON_NAME = "DDG-AppIcon-Alpha"; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGoAlpha.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; @@ -10697,7 +11090,9 @@ EE5A7C472A82BBB700387C84 /* Alpha */ = { isa = XCBuildConfiguration; buildSettings = { + ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = "AppIcon-Yellow AppIcon-Blue AppIcon-Purple AppIcon-Green AppIcon-Black"; ASSETCATALOG_COMPILER_APPICON_NAME = "DDG-AppIcon-Alpha"; + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES; CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGoAlpha.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; @@ -11258,6 +11653,17 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + C1EF5B332CC0457B002980E6 /* Build configuration list for PBXNativeTarget "AutofillCredentialProvider" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C1EF5B2F2CC0457B002980E6 /* Debug */, + C1EF5B302CC0457B002980E6 /* Alpha Debug */, + C1EF5B312CC0457B002980E6 /* Alpha */, + C1EF5B322CC0457B002980E6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; F143C2ED1E4A4CD400CFDE3A /* Build configuration list for PBXNativeTarget "Core" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -11308,8 +11714,8 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { - kind = exactVersion; - version = 210.0.1; + kind = revision; + revision = 6a5642a07acc85af9e1b165066e5b9d9716b7a09; }; }; 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */ = { @@ -11430,6 +11836,10 @@ package = 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; productName = Common; }; + 315C77812CFA41A400699683 /* AIChat */ = { + isa = XCSwiftPackageProductDependency; + productName = AIChat; + }; 31E69A62280F4CB600478327 /* DuckUI */ = { isa = XCSwiftPackageProductDependency; productName = DuckUI; @@ -11483,6 +11893,16 @@ package = 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; productName = Common; }; + 56D779392CFFC7E800B619EF /* PixelExperimentKit */ = { + isa = XCSwiftPackageProductDependency; + package = 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = PixelExperimentKit; + }; + 56D7793B2CFFC7E800B619EF /* PixelKit */ = { + isa = XCSwiftPackageProductDependency; + package = 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; + productName = PixelKit; + }; 851481872A600EFC00ABC65F /* RemoteMessaging */ = { isa = XCSwiftPackageProductDependency; package = 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; @@ -11592,6 +12012,15 @@ package = C14882EB27F211A000D59F0C /* XCRemoteSwiftPackageReference "SwiftSoup" */; productName = SwiftSoup; }; + C1CAAA802CF8C8F400C37EE6 /* DuckUI */ = { + isa = XCSwiftPackageProductDependency; + productName = DuckUI; + }; + C1CAAA822CF8C8FF00C37EE6 /* DesignResourcesKit */ = { + isa = XCSwiftPackageProductDependency; + package = F42D541B29DCA40B004C4FF1 /* XCRemoteSwiftPackageReference "DesignResourcesKit" */; + productName = DesignResourcesKit; + }; CB6CC7E32CD2529000320907 /* BrokenSitePrompt */ = { isa = XCSwiftPackageProductDependency; productName = BrokenSitePrompt; diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index a5389de6b5..e5d32e83b5 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -41,8 +41,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/content-scope-scripts", "state" : { - "revision" : "c4bb146afdf0c7a93fb9a7d95b1cb255708a470d", - "version" : "6.41.0" + "revision" : "93ea6c3e771bc0b743b38cefbff548c10add9898", + "version" : "6.42.0" } }, { @@ -59,8 +59,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/duckduckgo-autofill.git", "state" : { - "revision" : "c992041d16ec10d790e6204dce9abf9966d1363c", - "version" : "15.1.0" + "revision" : "88982a3802ac504e2f1a118a73bfdf2d8f4a7735", + "version" : "16.0.0" } }, { @@ -122,8 +122,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/duckduckgo/privacy-dashboard", "state" : { - "revision" : "49db79829dcb166b3524afdbc1c680890452ce1c", - "version" : "7.2.1" + "revision" : "022c845b06ace6a4aa712a4fa3e79da32193d5c6", + "version" : "7.4.0" } }, { diff --git a/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/AutofillCredentialProvider.xcscheme b/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/AutofillCredentialProvider.xcscheme new file mode 100644 index 0000000000..7708563eda --- /dev/null +++ b/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/AutofillCredentialProvider.xcscheme @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DuckDuckGo/AIChat/AIChatSettings.swift b/DuckDuckGo/AIChat/AIChatSettings.swift new file mode 100644 index 0000000000..e45021d903 --- /dev/null +++ b/DuckDuckGo/AIChat/AIChatSettings.swift @@ -0,0 +1,150 @@ +// +// AIChatSettings.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import BrowserServicesKit +import AIChat +import Foundation +import Core + +/// This struct serves as a wrapper for PrivacyConfigurationManaging, enabling the retrieval of data relevant to AIChat. +/// It also fire pixels when necessary data is missing. +struct AIChatSettings: AIChatSettingsProvider { + enum SettingsValue: String { + case aiChatURL + + var defaultValue: String { + switch self { + /// https://app.asana.com/0/1208541424548398/1208567543352020/f + case .aiChatURL: return "https://duckduckgo.com/?q=DuckDuckGo+AI+Chat&ia=chat&duckai=4" + } + } + } + + private let privacyConfigurationManager: PrivacyConfigurationManaging + private var remoteSettings: PrivacyConfigurationData.PrivacyFeature.FeatureSettings { + privacyConfigurationManager.privacyConfig.settings(for: .aiChat) + } + private let internalUserDecider: InternalUserDecider + private let userDefaults: UserDefaults + private let notificationCenter: NotificationCenter + + init(privacyConfigurationManager: PrivacyConfigurationManaging, internalUserDecider: InternalUserDecider, userDefaults: UserDefaults = .standard, + notificationCenter: NotificationCenter = .default) { + self.internalUserDecider = internalUserDecider + self.privacyConfigurationManager = privacyConfigurationManager + self.userDefaults = userDefaults + self.notificationCenter = notificationCenter + } + + // MARK: - Public + + var aiChatURL: URL { + guard let url = URL(string: getSettingsData(.aiChatURL)) else { + return URL(string: SettingsValue.aiChatURL.defaultValue)! + } + return url + } + + var isAIChatBrowsingMenuUserSettingsEnabled: Bool { + userDefaults.showAIChatBrowsingMenu && isAIChatBrowsingMenubarShortcutFeatureEnabled + } + + var isAIChatAddressBarUserSettingsEnabled: Bool { + userDefaults.showAIChatAddressBar && isAIChatAddressBarShortcutFeatureEnabled + } + + var isAIChatFeatureEnabled: Bool { + privacyConfigurationManager.privacyConfig.isEnabled(featureKey: .aiChat) || internalUserDecider.isInternalUser + } + + var isAIChatAddressBarShortcutFeatureEnabled: Bool { + return isFeatureEnabled(for: .addressBarShortcut) + } + + var isAIChatBrowsingMenubarShortcutFeatureEnabled: Bool { + return isFeatureEnabled(for: .browsingToolbarShortcut) + } + + func enableAIChatBrowsingMenuUserSettings(enable: Bool) { + userDefaults.showAIChatBrowsingMenu = enable + triggerSettingsChangedNotification() + } + + func enableAIChatAddressBarUserSettings(enable: Bool) { + userDefaults.showAIChatAddressBar = enable + triggerSettingsChangedNotification() + } + + // MARK: - Private + + private func triggerSettingsChangedNotification() { + notificationCenter.post(name: .aiChatSettingsChanged, object: nil) + } + + private func isFeatureEnabled(for subfeature: AIChatSubfeature) -> Bool { + let isSubfeatureFlagEnabled = privacyConfigurationManager.privacyConfig.isSubfeatureEnabled(subfeature) + let isInternalUser = internalUserDecider.isInternalUser + return (isSubfeatureFlagEnabled || isInternalUser) + } + + private func getSettingsData(_ value: SettingsValue) -> String { + if let value = remoteSettings[value.rawValue] as? String { + return value + } else { + Pixel.fire(pixel: .aiChatNoRemoteSettingsFound(settings: value.rawValue)) + return value.defaultValue + } + } +} + +private extension UserDefaults { + enum Keys { + static let showAIChatBrowsingMenu = "aichat.settings.showAIChatBrowsingMenu" + static let showAIChatAddressBar = "aichat.settings.showAIChatAddressBar" + } + + static let showAIChatBrowsingMenuDefaultValue = true + static let showAIChatAddressBarDefaultValue = true + + @objc dynamic var showAIChatBrowsingMenu: Bool { + get { + value(forKey: Keys.showAIChatBrowsingMenu) as? Bool ?? Self.showAIChatBrowsingMenuDefaultValue + } + + set { + guard newValue != showAIChatBrowsingMenu else { return } + set(newValue, forKey: Keys.showAIChatBrowsingMenu) + } + } + + @objc dynamic var showAIChatAddressBar: Bool { + get { + value(forKey: Keys.showAIChatAddressBar) as? Bool ?? Self.showAIChatAddressBarDefaultValue + } + + set { + guard newValue != showAIChatAddressBar else { return } + set(newValue, forKey: Keys.showAIChatAddressBar) + } + } +} + +public extension NSNotification.Name { + static let aiChatSettingsChanged = Notification.Name("com.duckduckgo.aichat.settings.changed") +} diff --git a/DuckDuckGo/AIChat/OmnibarAccessoryHandling.swift b/DuckDuckGo/AIChat/OmnibarAccessoryHandling.swift new file mode 100644 index 0000000000..0aab700967 --- /dev/null +++ b/DuckDuckGo/AIChat/OmnibarAccessoryHandling.swift @@ -0,0 +1,38 @@ +// +// OmnibarAccessoryHandling.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import AIChat + +protocol OmnibarAccessoryHandling { + func omnibarAccessory(for url: URL?) -> OmniBar.AccessoryType +} + +struct OmnibarAccessoryHandler: OmnibarAccessoryHandling { + let settings: AIChatSettingsProvider + + func omnibarAccessory(for url: URL?) -> OmniBar.AccessoryType { + guard settings.isAIChatFeatureEnabled, + settings.isAIChatAddressBarUserSettingsEnabled else { + return .share + } + + return (url?.isDuckDuckGoSearch == true) ? .chat : .share + } +} diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-black.appiconset/Contents.json b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-black.appiconset/Contents.json new file mode 100644 index 0000000000..1d545cdbaf --- /dev/null +++ b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-black.appiconset/Contents.json @@ -0,0 +1,38 @@ +{ + "images" : [ + { + "filename" : "Icon-Color-Black-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Color-Black-Dark-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "Icon-Tinted-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-black.appiconset/Icon-Color-Black-1024x1024.png b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-black.appiconset/Icon-Color-Black-1024x1024.png new file mode 100644 index 0000000000..5a8ede897c Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-black.appiconset/Icon-Color-Black-1024x1024.png differ diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-black.appiconset/Icon-Color-Black-Dark-1024x1024.png b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-black.appiconset/Icon-Color-Black-Dark-1024x1024.png new file mode 100644 index 0000000000..2d666cd7aa Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-black.appiconset/Icon-Color-Black-Dark-1024x1024.png differ diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-black.appiconset/Icon-Tinted-1024x1024.png b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-black.appiconset/Icon-Tinted-1024x1024.png new file mode 100644 index 0000000000..45bf7060cc Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-black.appiconset/Icon-Tinted-1024x1024.png differ diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-blue.appiconset/Contents.json b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-blue.appiconset/Contents.json new file mode 100644 index 0000000000..e974cd5cd3 --- /dev/null +++ b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-blue.appiconset/Contents.json @@ -0,0 +1,38 @@ +{ + "images" : [ + { + "filename" : "Icon-Color-Blue-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Color-Blue-Dark-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "Icon-Tinted-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-blue.appiconset/Icon-Color-Blue-1024x1024.png b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-blue.appiconset/Icon-Color-Blue-1024x1024.png new file mode 100644 index 0000000000..edcac2d868 Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-blue.appiconset/Icon-Color-Blue-1024x1024.png differ diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-blue.appiconset/Icon-Color-Blue-Dark-1024x1024.png b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-blue.appiconset/Icon-Color-Blue-Dark-1024x1024.png new file mode 100644 index 0000000000..7c105b714a Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-blue.appiconset/Icon-Color-Blue-Dark-1024x1024.png differ diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-blue.appiconset/Icon-Tinted-1024x1024.png b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-blue.appiconset/Icon-Tinted-1024x1024.png new file mode 100644 index 0000000000..45bf7060cc Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-blue.appiconset/Icon-Tinted-1024x1024.png differ diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-green.appiconset/Contents.json b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-green.appiconset/Contents.json new file mode 100644 index 0000000000..9c17898573 --- /dev/null +++ b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-green.appiconset/Contents.json @@ -0,0 +1,38 @@ +{ + "images" : [ + { + "filename" : "Icon-Color-Green-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Color-Green-Dark-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "Icon-Tinted-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-green.appiconset/Icon-Color-Green-1024x1024.png b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-green.appiconset/Icon-Color-Green-1024x1024.png new file mode 100644 index 0000000000..2dc0747ed0 Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-green.appiconset/Icon-Color-Green-1024x1024.png differ diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-green.appiconset/Icon-Color-Green-Dark-1024x1024.png b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-green.appiconset/Icon-Color-Green-Dark-1024x1024.png new file mode 100644 index 0000000000..a46c1523c9 Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-green.appiconset/Icon-Color-Green-Dark-1024x1024.png differ diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-green.appiconset/Icon-Tinted-1024x1024.png b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-green.appiconset/Icon-Tinted-1024x1024.png new file mode 100644 index 0000000000..45bf7060cc Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-green.appiconset/Icon-Tinted-1024x1024.png differ diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-purple.appiconset/Contents.json b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-purple.appiconset/Contents.json new file mode 100644 index 0000000000..cf165a6885 --- /dev/null +++ b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-purple.appiconset/Contents.json @@ -0,0 +1,38 @@ +{ + "images" : [ + { + "filename" : "Icon-Color-Purple-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Color-Purple-Dark-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "Icon-Tinted-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-purple.appiconset/Icon-Color-Purple-1024x1024.png b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-purple.appiconset/Icon-Color-Purple-1024x1024.png new file mode 100644 index 0000000000..81b0630705 Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-purple.appiconset/Icon-Color-Purple-1024x1024.png differ diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-purple.appiconset/Icon-Color-Purple-Dark-1024x1024.png b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-purple.appiconset/Icon-Color-Purple-Dark-1024x1024.png new file mode 100644 index 0000000000..bb5a224e80 Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-purple.appiconset/Icon-Color-Purple-Dark-1024x1024.png differ diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-purple.appiconset/Icon-Tinted-1024x1024.png b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-purple.appiconset/Icon-Tinted-1024x1024.png new file mode 100644 index 0000000000..45bf7060cc Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-purple.appiconset/Icon-Tinted-1024x1024.png differ diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-yellow.appiconset/Contents.json b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-yellow.appiconset/Contents.json new file mode 100644 index 0000000000..2ff9a347d2 --- /dev/null +++ b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-yellow.appiconset/Contents.json @@ -0,0 +1,38 @@ +{ + "images" : [ + { + "filename" : "Icon-Color-Yellow-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Color-Yellow-Dark-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "Icon-Tinted-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-yellow.appiconset/Icon-Color-Yellow-1024x1024.png b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-yellow.appiconset/Icon-Color-Yellow-1024x1024.png new file mode 100644 index 0000000000..cb2f684ed1 Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-yellow.appiconset/Icon-Color-Yellow-1024x1024.png differ diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-yellow.appiconset/Icon-Color-Yellow-Dark-1024x1024.png b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-yellow.appiconset/Icon-Color-Yellow-Dark-1024x1024.png new file mode 100644 index 0000000000..ba0cde6ae1 Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-yellow.appiconset/Icon-Color-Yellow-Dark-1024x1024.png differ diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-yellow.appiconset/Icon-Tinted-1024x1024.png b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-yellow.appiconset/Icon-Tinted-1024x1024.png new file mode 100644 index 0000000000..45bf7060cc Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/AppIcon-yellow.appiconset/Icon-Tinted-1024x1024.png differ diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/Contents.json b/DuckDuckGo/AlternateAppIcons.xcassets/Contents.json new file mode 100644 index 0000000000..73c00596a7 --- /dev/null +++ b/DuckDuckGo/AlternateAppIcons.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DuckDuckGo/AppIconBlack60x60@2x.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-black-medium.imageset/AppIconBlack60x60@2x.png similarity index 100% rename from DuckDuckGo/AppIconBlack60x60@2x.png rename to DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-black-medium.imageset/AppIconBlack60x60@2x.png diff --git a/DuckDuckGo/AppIconBlack60x60@3x.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-black-medium.imageset/AppIconBlack60x60@3x.png similarity index 100% rename from DuckDuckGo/AppIconBlack60x60@3x.png rename to DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-black-medium.imageset/AppIconBlack60x60@3x.png diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-black-medium.imageset/Contents.json b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-black-medium.imageset/Contents.json new file mode 100644 index 0000000000..621c3b0889 --- /dev/null +++ b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-black-medium.imageset/Contents.json @@ -0,0 +1,54 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "AppIconBlack60x60@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Color-Black-Dark-120x120.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "AppIconBlack60x60@3x.png", + "idiom" : "universal", + "scale" : "3x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Color-Black-Dark-180x180.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-black-medium.imageset/Icon-Color-Black-Dark-120x120.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-black-medium.imageset/Icon-Color-Black-Dark-120x120.png new file mode 100644 index 0000000000..22fbe29808 Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-black-medium.imageset/Icon-Color-Black-Dark-120x120.png differ diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-black-medium.imageset/Icon-Color-Black-Dark-180x180.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-black-medium.imageset/Icon-Color-Black-Dark-180x180.png new file mode 100644 index 0000000000..d116270856 Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-black-medium.imageset/Icon-Color-Black-Dark-180x180.png differ diff --git a/DuckDuckGo/AppIconBlack29x29@2x.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-black-small.imageset/AppIconBlack29x29@2x.png similarity index 100% rename from DuckDuckGo/AppIconBlack29x29@2x.png rename to DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-black-small.imageset/AppIconBlack29x29@2x.png diff --git a/DuckDuckGo/AppIconBlack29x29@3x.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-black-small.imageset/AppIconBlack29x29@3x.png similarity index 100% rename from DuckDuckGo/AppIconBlack29x29@3x.png rename to DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-black-small.imageset/AppIconBlack29x29@3x.png diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-black-small.imageset/Contents.json b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-black-small.imageset/Contents.json new file mode 100644 index 0000000000..4049b32a3e --- /dev/null +++ b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-black-small.imageset/Contents.json @@ -0,0 +1,54 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "AppIconBlack29x29@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Color-Black-Dark-58x58.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "AppIconBlack29x29@3x.png", + "idiom" : "universal", + "scale" : "3x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Color-Black-Dark-29x29.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-black-small.imageset/Icon-Color-Black-Dark-29x29.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-black-small.imageset/Icon-Color-Black-Dark-29x29.png new file mode 100644 index 0000000000..a72c110a7e Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-black-small.imageset/Icon-Color-Black-Dark-29x29.png differ diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-black-small.imageset/Icon-Color-Black-Dark-58x58.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-black-small.imageset/Icon-Color-Black-Dark-58x58.png new file mode 100644 index 0000000000..2728c8aeb2 Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-black-small.imageset/Icon-Color-Black-Dark-58x58.png differ diff --git a/DuckDuckGo/AppIconBlue60x60@2x.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-blue-medium.imageset/AppIconBlue60x60@2x.png similarity index 100% rename from DuckDuckGo/AppIconBlue60x60@2x.png rename to DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-blue-medium.imageset/AppIconBlue60x60@2x.png diff --git a/DuckDuckGo/AppIconBlue60x60@3x.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-blue-medium.imageset/AppIconBlue60x60@3x.png similarity index 100% rename from DuckDuckGo/AppIconBlue60x60@3x.png rename to DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-blue-medium.imageset/AppIconBlue60x60@3x.png diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-blue-medium.imageset/Contents.json b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-blue-medium.imageset/Contents.json new file mode 100644 index 0000000000..22de91e68b --- /dev/null +++ b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-blue-medium.imageset/Contents.json @@ -0,0 +1,54 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "AppIconBlue60x60@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Color-Blue-Dark-120x120.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "AppIconBlue60x60@3x.png", + "idiom" : "universal", + "scale" : "3x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Color-Blue-Dark-180x180.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-blue-medium.imageset/Icon-Color-Blue-Dark-120x120.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-blue-medium.imageset/Icon-Color-Blue-Dark-120x120.png new file mode 100644 index 0000000000..081d9337a6 Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-blue-medium.imageset/Icon-Color-Blue-Dark-120x120.png differ diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-blue-medium.imageset/Icon-Color-Blue-Dark-180x180.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-blue-medium.imageset/Icon-Color-Blue-Dark-180x180.png new file mode 100644 index 0000000000..06db81cabb Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-blue-medium.imageset/Icon-Color-Blue-Dark-180x180.png differ diff --git a/DuckDuckGo/AppIconBlue29x29@2x.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-blue-small.imageset/AppIconBlue29x29@2x.png similarity index 100% rename from DuckDuckGo/AppIconBlue29x29@2x.png rename to DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-blue-small.imageset/AppIconBlue29x29@2x.png diff --git a/DuckDuckGo/AppIconBlue29x29@3x.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-blue-small.imageset/AppIconBlue29x29@3x.png similarity index 100% rename from DuckDuckGo/AppIconBlue29x29@3x.png rename to DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-blue-small.imageset/AppIconBlue29x29@3x.png diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-blue-small.imageset/Contents.json b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-blue-small.imageset/Contents.json new file mode 100644 index 0000000000..c2b4bd994c --- /dev/null +++ b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-blue-small.imageset/Contents.json @@ -0,0 +1,54 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "AppIconBlue29x29@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Color-Blue-Dark-58x58.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "AppIconBlue29x29@3x.png", + "idiom" : "universal", + "scale" : "3x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Color-Blue-Dark-29x29.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-blue-small.imageset/Icon-Color-Blue-Dark-29x29.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-blue-small.imageset/Icon-Color-Blue-Dark-29x29.png new file mode 100644 index 0000000000..16c30a116f Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-blue-small.imageset/Icon-Color-Blue-Dark-29x29.png differ diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-blue-small.imageset/Icon-Color-Blue-Dark-58x58.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-blue-small.imageset/Icon-Color-Blue-Dark-58x58.png new file mode 100644 index 0000000000..5957248456 Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-blue-small.imageset/Icon-Color-Blue-Dark-58x58.png differ diff --git a/DuckDuckGo/AppIconGreen60x60@2x.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-green-medium.imageset/AppIconGreen60x60@2x.png similarity index 100% rename from DuckDuckGo/AppIconGreen60x60@2x.png rename to DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-green-medium.imageset/AppIconGreen60x60@2x.png diff --git a/DuckDuckGo/AppIconGreen60x60@3x.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-green-medium.imageset/AppIconGreen60x60@3x.png similarity index 100% rename from DuckDuckGo/AppIconGreen60x60@3x.png rename to DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-green-medium.imageset/AppIconGreen60x60@3x.png diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-green-medium.imageset/Contents.json b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-green-medium.imageset/Contents.json new file mode 100644 index 0000000000..1afb2a13f3 --- /dev/null +++ b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-green-medium.imageset/Contents.json @@ -0,0 +1,54 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "AppIconGreen60x60@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Color-Green-Dark-120x120.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "AppIconGreen60x60@3x.png", + "idiom" : "universal", + "scale" : "3x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Color-Green-Dark-180x180.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-green-medium.imageset/Icon-Color-Green-Dark-120x120.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-green-medium.imageset/Icon-Color-Green-Dark-120x120.png new file mode 100644 index 0000000000..2c34fcad54 Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-green-medium.imageset/Icon-Color-Green-Dark-120x120.png differ diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-green-medium.imageset/Icon-Color-Green-Dark-180x180.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-green-medium.imageset/Icon-Color-Green-Dark-180x180.png new file mode 100644 index 0000000000..46f99ade9c Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-green-medium.imageset/Icon-Color-Green-Dark-180x180.png differ diff --git a/DuckDuckGo/AppIconGreen29x29@2x.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-green-small.imageset/AppIconGreen29x29@2x.png similarity index 100% rename from DuckDuckGo/AppIconGreen29x29@2x.png rename to DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-green-small.imageset/AppIconGreen29x29@2x.png diff --git a/DuckDuckGo/AppIconGreen29x29@3x.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-green-small.imageset/AppIconGreen29x29@3x.png similarity index 100% rename from DuckDuckGo/AppIconGreen29x29@3x.png rename to DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-green-small.imageset/AppIconGreen29x29@3x.png diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-green-small.imageset/Contents.json b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-green-small.imageset/Contents.json new file mode 100644 index 0000000000..e115ec3212 --- /dev/null +++ b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-green-small.imageset/Contents.json @@ -0,0 +1,54 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "AppIconGreen29x29@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Color-Green-Dark-58x58.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "AppIconGreen29x29@3x.png", + "idiom" : "universal", + "scale" : "3x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Color-Green-Dark-87x87.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-green-small.imageset/Icon-Color-Green-Dark-58x58.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-green-small.imageset/Icon-Color-Green-Dark-58x58.png new file mode 100644 index 0000000000..bd9fd8f3e0 Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-green-small.imageset/Icon-Color-Green-Dark-58x58.png differ diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-green-small.imageset/Icon-Color-Green-Dark-87x87.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-green-small.imageset/Icon-Color-Green-Dark-87x87.png new file mode 100644 index 0000000000..a7c456de6e Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-green-small.imageset/Icon-Color-Green-Dark-87x87.png differ diff --git a/DuckDuckGo/AppIconPurple60x60@2x.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-purple-medium.imageset/AppIconPurple60x60@2x.png similarity index 100% rename from DuckDuckGo/AppIconPurple60x60@2x.png rename to DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-purple-medium.imageset/AppIconPurple60x60@2x.png diff --git a/DuckDuckGo/AppIconPurple60x60@3x.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-purple-medium.imageset/AppIconPurple60x60@3x.png similarity index 100% rename from DuckDuckGo/AppIconPurple60x60@3x.png rename to DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-purple-medium.imageset/AppIconPurple60x60@3x.png diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-purple-medium.imageset/Contents.json b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-purple-medium.imageset/Contents.json new file mode 100644 index 0000000000..c9ab25ff10 --- /dev/null +++ b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-purple-medium.imageset/Contents.json @@ -0,0 +1,54 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "AppIconPurple60x60@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Color-Purple-Dark-120x120.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "AppIconPurple60x60@3x.png", + "idiom" : "universal", + "scale" : "3x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Color-Purple-Dark-180x180.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-purple-medium.imageset/Icon-Color-Purple-Dark-120x120.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-purple-medium.imageset/Icon-Color-Purple-Dark-120x120.png new file mode 100644 index 0000000000..1c131df20c Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-purple-medium.imageset/Icon-Color-Purple-Dark-120x120.png differ diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-purple-medium.imageset/Icon-Color-Purple-Dark-180x180.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-purple-medium.imageset/Icon-Color-Purple-Dark-180x180.png new file mode 100644 index 0000000000..5ca22cf901 Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-purple-medium.imageset/Icon-Color-Purple-Dark-180x180.png differ diff --git a/DuckDuckGo/AppIconPurple29x29@2x.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-purple-small.imageset/AppIconPurple29x29@2x.png similarity index 100% rename from DuckDuckGo/AppIconPurple29x29@2x.png rename to DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-purple-small.imageset/AppIconPurple29x29@2x.png diff --git a/DuckDuckGo/AppIconPurple29x29@3x.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-purple-small.imageset/AppIconPurple29x29@3x.png similarity index 100% rename from DuckDuckGo/AppIconPurple29x29@3x.png rename to DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-purple-small.imageset/AppIconPurple29x29@3x.png diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-purple-small.imageset/Contents.json b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-purple-small.imageset/Contents.json new file mode 100644 index 0000000000..653913f90a --- /dev/null +++ b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-purple-small.imageset/Contents.json @@ -0,0 +1,54 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "AppIconPurple29x29@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Color-Purple-Dark-58x58.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "AppIconPurple29x29@3x.png", + "idiom" : "universal", + "scale" : "3x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Color-Purple-Dark-87x87.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-purple-small.imageset/Icon-Color-Purple-Dark-58x58.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-purple-small.imageset/Icon-Color-Purple-Dark-58x58.png new file mode 100644 index 0000000000..4225bab8f7 Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-purple-small.imageset/Icon-Color-Purple-Dark-58x58.png differ diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-purple-small.imageset/Icon-Color-Purple-Dark-87x87.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-purple-small.imageset/Icon-Color-Purple-Dark-87x87.png new file mode 100644 index 0000000000..5523b26a91 Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-purple-small.imageset/Icon-Color-Purple-Dark-87x87.png differ diff --git a/DuckDuckGo/AppIconRed60x60@2x.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-red-medium.imageset/AppIconRed60x60@2x.png similarity index 100% rename from DuckDuckGo/AppIconRed60x60@2x.png rename to DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-red-medium.imageset/AppIconRed60x60@2x.png diff --git a/DuckDuckGo/AppIconRed60x60@3x.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-red-medium.imageset/AppIconRed60x60@3x.png similarity index 100% rename from DuckDuckGo/AppIconRed60x60@3x.png rename to DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-red-medium.imageset/AppIconRed60x60@3x.png diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-red-medium.imageset/Contents.json b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-red-medium.imageset/Contents.json new file mode 100644 index 0000000000..693e00868b --- /dev/null +++ b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-red-medium.imageset/Contents.json @@ -0,0 +1,54 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "AppIconRed60x60@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Dark-120x120.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "AppIconRed60x60@3x.png", + "idiom" : "universal", + "scale" : "3x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Dark-180x180.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-red-medium.imageset/Icon-Dark-120x120.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-red-medium.imageset/Icon-Dark-120x120.png new file mode 100644 index 0000000000..b692327908 Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-red-medium.imageset/Icon-Dark-120x120.png differ diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-red-medium.imageset/Icon-Dark-180x180.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-red-medium.imageset/Icon-Dark-180x180.png new file mode 100644 index 0000000000..c98ae2f573 Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-red-medium.imageset/Icon-Dark-180x180.png differ diff --git a/DuckDuckGo/AppIconRed29x29@2x.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-red-small.imageset/AppIconRed29x29@2x.png similarity index 100% rename from DuckDuckGo/AppIconRed29x29@2x.png rename to DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-red-small.imageset/AppIconRed29x29@2x.png diff --git a/DuckDuckGo/AppIconRed29x29@3x.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-red-small.imageset/AppIconRed29x29@3x.png similarity index 100% rename from DuckDuckGo/AppIconRed29x29@3x.png rename to DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-red-small.imageset/AppIconRed29x29@3x.png diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-red-small.imageset/Contents.json b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-red-small.imageset/Contents.json new file mode 100644 index 0000000000..d7874103ce --- /dev/null +++ b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-red-small.imageset/Contents.json @@ -0,0 +1,54 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "AppIconRed29x29@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Dark-58x58.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "AppIconRed29x29@3x.png", + "idiom" : "universal", + "scale" : "3x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Dark-87x87.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-red-small.imageset/Icon-Dark-58x58.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-red-small.imageset/Icon-Dark-58x58.png new file mode 100644 index 0000000000..f1913c9271 Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-red-small.imageset/Icon-Dark-58x58.png differ diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-red-small.imageset/Icon-Dark-87x87.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-red-small.imageset/Icon-Dark-87x87.png new file mode 100644 index 0000000000..a0b0f2c2ec Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-red-small.imageset/Icon-Dark-87x87.png differ diff --git a/DuckDuckGo/AppIconYellow60x60@2x.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-yellow-medium.imageset/AppIconYellow60x60@2x.png similarity index 100% rename from DuckDuckGo/AppIconYellow60x60@2x.png rename to DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-yellow-medium.imageset/AppIconYellow60x60@2x.png diff --git a/DuckDuckGo/AppIconYellow60x60@3x.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-yellow-medium.imageset/AppIconYellow60x60@3x.png similarity index 100% rename from DuckDuckGo/AppIconYellow60x60@3x.png rename to DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-yellow-medium.imageset/AppIconYellow60x60@3x.png diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-yellow-medium.imageset/Contents.json b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-yellow-medium.imageset/Contents.json new file mode 100644 index 0000000000..0bde764054 --- /dev/null +++ b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-yellow-medium.imageset/Contents.json @@ -0,0 +1,54 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "AppIconYellow60x60@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Color-Yellow-Dark-120x120.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "AppIconYellow60x60@3x.png", + "idiom" : "universal", + "scale" : "3x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Color-Yellow-Dark-180x180.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-yellow-medium.imageset/Icon-Color-Yellow-Dark-120x120.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-yellow-medium.imageset/Icon-Color-Yellow-Dark-120x120.png new file mode 100644 index 0000000000..df99a69e12 Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-yellow-medium.imageset/Icon-Color-Yellow-Dark-120x120.png differ diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-yellow-medium.imageset/Icon-Color-Yellow-Dark-180x180.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-yellow-medium.imageset/Icon-Color-Yellow-Dark-180x180.png new file mode 100644 index 0000000000..b0e3e5936d Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-yellow-medium.imageset/Icon-Color-Yellow-Dark-180x180.png differ diff --git a/DuckDuckGo/AppIconYellow29x29@2x.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-yellow-small.imageset/AppIconYellow29x29@2x.png similarity index 100% rename from DuckDuckGo/AppIconYellow29x29@2x.png rename to DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-yellow-small.imageset/AppIconYellow29x29@2x.png diff --git a/DuckDuckGo/AppIconYellow29x29@3x.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-yellow-small.imageset/AppIconYellow29x29@3x.png similarity index 100% rename from DuckDuckGo/AppIconYellow29x29@3x.png rename to DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-yellow-small.imageset/AppIconYellow29x29@3x.png diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-yellow-small.imageset/Contents.json b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-yellow-small.imageset/Contents.json new file mode 100644 index 0000000000..b201978ee7 --- /dev/null +++ b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-yellow-small.imageset/Contents.json @@ -0,0 +1,54 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "AppIconYellow29x29@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Color-Yellow-Dark-58x58.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "AppIconYellow29x29@3x.png", + "idiom" : "universal", + "scale" : "3x" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Color-Yellow-Dark-87x87.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-yellow-small.imageset/Icon-Color-Yellow-Dark-58x58.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-yellow-small.imageset/Icon-Color-Yellow-Dark-58x58.png new file mode 100644 index 0000000000..231c3ce94b Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-yellow-small.imageset/Icon-Color-Yellow-Dark-58x58.png differ diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-yellow-small.imageset/Icon-Color-Yellow-Dark-87x87.png b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-yellow-small.imageset/Icon-Color-Yellow-Dark-87x87.png new file mode 100644 index 0000000000..28e98866d4 Binary files /dev/null and b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/AppIcon-yellow-small.imageset/Icon-Color-Yellow-Dark-87x87.png differ diff --git a/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/Contents.json b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/Contents.json new file mode 100644 index 0000000000..73c00596a7 --- /dev/null +++ b/DuckDuckGo/AlternateAppIcons.xcassets/UIAssets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DuckDuckGo/AppDelegate.swift b/DuckDuckGo/AppDelegate.swift index 3d941e7035..c176b05ae0 100644 --- a/DuckDuckGo/AppDelegate.swift +++ b/DuckDuckGo/AppDelegate.swift @@ -36,6 +36,8 @@ import RemoteMessaging import SyncDataProviders import Subscription import NetworkProtection +import PixelKit +import PixelExperimentKit import WebKit import os.log @@ -182,6 +184,16 @@ import os.log crashCollection.startAttachingCrashLogMessages { pixelParameters, payloads, sendReport in pixelParameters.forEach { params in Pixel.fire(pixel: .dbCrashDetected, withAdditionalParameters: params, includedParameters: []) + + // Each crash comes with an `appVersion` parameter representing the version that the crash occurred on. + // This is to disambiguate the situation where a crash occurs, but isn't sent until the next update. + // If for some reason the parameter can't be found, fall back to the current version. + if let crashAppVersion = params[PixelParameters.appVersion] { + let dailyParameters = [PixelParameters.appVersion: crashAppVersion] + DailyPixel.fireDaily(.dbCrashDetectedDaily, withAdditionalParameters: dailyParameters) + } else { + DailyPixel.fireDaily(.dbCrashDetectedDaily) + } } // Async dispatch because rootViewController may otherwise be nil here @@ -296,6 +308,33 @@ import os.log ).wrappedValue ) ?? defaultEnvironment + var dryRun = false +#if DEBUG + dryRun = true +#endif + let isPhone = UIDevice.current.userInterfaceIdiom == .phone + let source = isPhone ? PixelKit.Source.iOS : PixelKit.Source.iPadOS + PixelKit.setUp(dryRun: dryRun, + appVersion: AppVersion.shared.versionNumber, + source: source.rawValue, + defaultHeaders: [:], + defaults: UserDefaults(suiteName: "\(Global.groupIdPrefix).app-configuration") ?? UserDefaults()) { (pixelName: String, headers: [String: String], parameters: [String: String], _, _, onComplete: @escaping PixelKit.CompletionBlock) in + + let url = URL.pixelUrl(forPixelNamed: pixelName) + let apiHeaders = APIRequestV2.HeadersV2(additionalHeaders: headers) + let request = APIRequestV2(url: url, method: .get, queryItems: parameters, headers: apiHeaders) + Task { + do { + _ = try await DefaultAPIService().fetch(request: request) + onComplete(true, nil) + } catch { + onComplete(false, error) + } + } + } + PixelKit.configureExperimentKit(featureFlagger: AppDependencyProvider.shared.featureFlagger, + eventTracker: ExperimentEventTracker(store: UserDefaults(suiteName: "\(Global.groupIdPrefix).app-configuration") ?? UserDefaults())) + let syncErrorHandler = SyncErrorHandler() syncDataProviders = SyncDataProviders( @@ -304,7 +343,8 @@ import os.log settingHandlers: [FavoritesDisplayModeSyncHandler()], favoritesDisplayModeStorage: FavoritesDisplayModeStorage(), syncErrorHandler: syncErrorHandler, - faviconStoring: Favicons.shared + faviconStoring: Favicons.shared, + tld: AppDependencyProvider.shared.storageCache.tld ) let syncService = DDGSync( @@ -1070,7 +1110,7 @@ import os.log autofillPixelReporter = AutofillPixelReporter( userDefaults: .standard, autofillEnabled: AppDependencyProvider.shared.appSettings.autofillCredentialsEnabled, - eventMapping: EventMapping {event, _, params, _ in + eventMapping: EventMapping {[weak self] event, _, params, _ in switch event { case .autofillActiveUser: Pixel.fire(pixel: .autofillActiveUser) @@ -1080,8 +1120,16 @@ import os.log Pixel.fire(pixel: .autofillOnboardedUser) case .autofillToggledOn: Pixel.fire(pixel: .autofillToggledOn, withAdditionalParameters: params ?? [:]) + if let autofillExtensionToggled = self?.autofillUsageMonitor.autofillExtensionEnabled { + Pixel.fire(pixel: autofillExtensionToggled ? .autofillExtensionToggledOn : .autofillExtensionToggledOff, + withAdditionalParameters: params ?? [:]) + } case .autofillToggledOff: Pixel.fire(pixel: .autofillToggledOff, withAdditionalParameters: params ?? [:]) + if let autofillExtensionToggled = self?.autofillUsageMonitor.autofillExtensionEnabled { + Pixel.fire(pixel: autofillExtensionToggled ? .autofillExtensionToggledOn : .autofillExtensionToggledOff, + withAdditionalParameters: params ?? [:]) + } case .autofillLoginsStacked: Pixel.fire(pixel: .autofillLoginsStacked, withAdditionalParameters: params ?? [:]) default: diff --git a/DuckDuckGo/AppDependencyProvider.swift b/DuckDuckGo/AppDependencyProvider.swift index 0ee74aa535..00b6f9fc29 100644 --- a/DuckDuckGo/AppDependencyProvider.swift +++ b/DuckDuckGo/AppDependencyProvider.swift @@ -27,6 +27,8 @@ import Common import NetworkProtection import RemoteMessaging import PageRefreshMonitor +import PixelKit +import PixelExperimentKit protocol DependencyProvider { @@ -58,10 +60,8 @@ protocol DependencyProvider { final class AppDependencyProvider: DependencyProvider { static var shared: DependencyProvider = AppDependencyProvider() - let appSettings: AppSettings = AppUserDefaults() let variantManager: VariantManager = DefaultVariantManager() - let internalUserDecider: InternalUserDecider = ContentBlocking.shared.privacyConfigurationManager.internalUserDecider let featureFlagger: FeatureFlagger @@ -93,13 +93,14 @@ final class AppDependencyProvider: DependencyProvider { let persistentPixel: PersistentPixelFiring = PersistentPixel() private init() { + let featureFlaggerOverrides = FeatureFlagLocalOverrides(keyValueStore: UserDefaults(suiteName: FeatureFlag.localOverrideStoreName)!, + actionHandler: FeatureFlagOverridesPublishingHandler() + ) + let experimentManager = ExperimentCohortsManager(store: ExperimentsDataStore(), fireCohortAssigned: PixelKit.fireExperimentEnrollmentPixel(subfeatureID:experiment:)) featureFlagger = DefaultFeatureFlagger(internalUserDecider: internalUserDecider, privacyConfigManager: ContentBlocking.shared.privacyConfigurationManager, - localOverrides: FeatureFlagLocalOverrides( - keyValueStore: UserDefaults(suiteName: FeatureFlag.localOverrideStoreName)!, - actionHandler: FeatureFlagOverridesPublishingHandler() - ), - experimentManager: ExperimentCohortsManager(store: ExperimentsDataStore()), + localOverrides: featureFlaggerOverrides, + experimentManager: experimentManager, for: FeatureFlag.self) configurationManager = ConfigurationManager(store: configurationStore) diff --git a/DuckDuckGo/AppIcon.swift b/DuckDuckGo/AppIcon.swift index e36164e6d1..c65fac87fc 100644 --- a/DuckDuckGo/AppIcon.swift +++ b/DuckDuckGo/AppIcon.swift @@ -20,16 +20,22 @@ import UIKit enum AppIcon: String, CaseIterable { - case red - case yellow - case green - case blue - case purple - case black + case red = "AppIcon-red" + case yellow = "AppIcon-yellow" + case green = "AppIcon-green" + case blue = "AppIcon-blue" + case purple = "AppIcon-purple" + case black = "AppIcon-black" - /// Returns a user facing string representation of the app icon. - var name: String { - rawValue + var accessibilityName: String { + switch self { + case .red: "red" + case .yellow: "yellow" + case .green: "green" + case .blue: "blue" + case .purple: "purple" + case .black: "black" + } } static var defaultAppIcon: AppIcon { @@ -37,29 +43,11 @@ enum AppIcon: String, CaseIterable { } var smallImage: UIImage? { - var image: UIImage? - switch self { - case .red: image = UIImage(named: "AppIconRed29x29") - case .yellow: image = UIImage(named: "AppIconYellow29x29") - case .green: image = UIImage(named: "AppIconGreen29x29") - case .blue: image = UIImage(named: "AppIconBlue29x29") - case .purple: image = UIImage(named: "AppIconPurple29x29") - case .black: image = UIImage(named: "AppIconBlack29x29") - } - return image + UIImage(named: "\(rawValue)-small") } var mediumImage: UIImage? { - var image: UIImage? - switch self { - case .red: image = UIImage(named: "AppIconRed60x60") - case .yellow: image = UIImage(named: "AppIconYellow60x60") - case .green: image = UIImage(named: "AppIconGreen60x60") - case .blue: image = UIImage(named: "AppIconBlue60x60") - case .purple: image = UIImage(named: "AppIconPurple60x60") - case .black: image = UIImage(named: "AppIconBlack60x60") - } - return image + UIImage(named: "\(rawValue)-medium") } } diff --git a/DuckDuckGo/AppIconBlack40x40@2x.png b/DuckDuckGo/AppIconBlack40x40@2x.png deleted file mode 100644 index 34aee4e457..0000000000 Binary files a/DuckDuckGo/AppIconBlack40x40@2x.png and /dev/null differ diff --git a/DuckDuckGo/AppIconBlack40x40@3x.png b/DuckDuckGo/AppIconBlack40x40@3x.png deleted file mode 100644 index 3e9b6a668e..0000000000 Binary files a/DuckDuckGo/AppIconBlack40x40@3x.png and /dev/null differ diff --git a/DuckDuckGo/AppIconBlack76x76@2x.png b/DuckDuckGo/AppIconBlack76x76@2x.png deleted file mode 100644 index 5a442d7d38..0000000000 Binary files a/DuckDuckGo/AppIconBlack76x76@2x.png and /dev/null differ diff --git a/DuckDuckGo/AppIconBlack83.5x83.5@2x.png b/DuckDuckGo/AppIconBlack83.5x83.5@2x.png deleted file mode 100644 index b6befdfdd6..0000000000 Binary files a/DuckDuckGo/AppIconBlack83.5x83.5@2x.png and /dev/null differ diff --git a/DuckDuckGo/AppIconBlue40x40@2x.png b/DuckDuckGo/AppIconBlue40x40@2x.png deleted file mode 100644 index 1cfdb45530..0000000000 Binary files a/DuckDuckGo/AppIconBlue40x40@2x.png and /dev/null differ diff --git a/DuckDuckGo/AppIconBlue40x40@3x.png b/DuckDuckGo/AppIconBlue40x40@3x.png deleted file mode 100644 index 39231b53bb..0000000000 Binary files a/DuckDuckGo/AppIconBlue40x40@3x.png and /dev/null differ diff --git a/DuckDuckGo/AppIconBlue76x76@2x.png b/DuckDuckGo/AppIconBlue76x76@2x.png deleted file mode 100644 index 83f5db1aa8..0000000000 Binary files a/DuckDuckGo/AppIconBlue76x76@2x.png and /dev/null differ diff --git a/DuckDuckGo/AppIconBlue83.5x83.5@2x.png b/DuckDuckGo/AppIconBlue83.5x83.5@2x.png deleted file mode 100644 index daf69bd42f..0000000000 Binary files a/DuckDuckGo/AppIconBlue83.5x83.5@2x.png and /dev/null differ diff --git a/DuckDuckGo/AppIconGreen40x40@2x.png b/DuckDuckGo/AppIconGreen40x40@2x.png deleted file mode 100644 index 4c00a1094d..0000000000 Binary files a/DuckDuckGo/AppIconGreen40x40@2x.png and /dev/null differ diff --git a/DuckDuckGo/AppIconGreen40x40@3x.png b/DuckDuckGo/AppIconGreen40x40@3x.png deleted file mode 100644 index d3f27c71ab..0000000000 Binary files a/DuckDuckGo/AppIconGreen40x40@3x.png and /dev/null differ diff --git a/DuckDuckGo/AppIconGreen76x76@2x.png b/DuckDuckGo/AppIconGreen76x76@2x.png deleted file mode 100644 index 35167d5066..0000000000 Binary files a/DuckDuckGo/AppIconGreen76x76@2x.png and /dev/null differ diff --git a/DuckDuckGo/AppIconGreen83.5x83.5@2x.png b/DuckDuckGo/AppIconGreen83.5x83.5@2x.png deleted file mode 100644 index 02a02bf658..0000000000 Binary files a/DuckDuckGo/AppIconGreen83.5x83.5@2x.png and /dev/null differ diff --git a/DuckDuckGo/AppIconPurple40x40@2x.png b/DuckDuckGo/AppIconPurple40x40@2x.png deleted file mode 100644 index 14c2cf4308..0000000000 Binary files a/DuckDuckGo/AppIconPurple40x40@2x.png and /dev/null differ diff --git a/DuckDuckGo/AppIconPurple40x40@3x.png b/DuckDuckGo/AppIconPurple40x40@3x.png deleted file mode 100644 index 732b889f44..0000000000 Binary files a/DuckDuckGo/AppIconPurple40x40@3x.png and /dev/null differ diff --git a/DuckDuckGo/AppIconPurple76x76@2x.png b/DuckDuckGo/AppIconPurple76x76@2x.png deleted file mode 100644 index ef975cb399..0000000000 Binary files a/DuckDuckGo/AppIconPurple76x76@2x.png and /dev/null differ diff --git a/DuckDuckGo/AppIconPurple83.5x83.5@2x.png b/DuckDuckGo/AppIconPurple83.5x83.5@2x.png deleted file mode 100644 index c94cfebe4c..0000000000 Binary files a/DuckDuckGo/AppIconPurple83.5x83.5@2x.png and /dev/null differ diff --git a/DuckDuckGo/AppIconSettingsCell.swift b/DuckDuckGo/AppIconSettingsCell.swift index 0afd421d12..5492136a9f 100644 --- a/DuckDuckGo/AppIconSettingsCell.swift +++ b/DuckDuckGo/AppIconSettingsCell.swift @@ -26,7 +26,7 @@ class AppIconSettingsCell: UICollectionViewCell { var appIcon: AppIcon! { didSet { imageView.image = appIcon.mediumImage - accessibilityLabel = appIcon.name + accessibilityLabel = appIcon.accessibilityName } } @IBOutlet weak var imageView: UIImageView! diff --git a/DuckDuckGo/AppIconYellow40x40@2x.png b/DuckDuckGo/AppIconYellow40x40@2x.png deleted file mode 100644 index 8f6b0086c4..0000000000 Binary files a/DuckDuckGo/AppIconYellow40x40@2x.png and /dev/null differ diff --git a/DuckDuckGo/AppIconYellow40x40@3x.png b/DuckDuckGo/AppIconYellow40x40@3x.png deleted file mode 100644 index 606f70cf77..0000000000 Binary files a/DuckDuckGo/AppIconYellow40x40@3x.png and /dev/null differ diff --git a/DuckDuckGo/AppIconYellow76x76@2x.png b/DuckDuckGo/AppIconYellow76x76@2x.png deleted file mode 100644 index 34f7898e02..0000000000 Binary files a/DuckDuckGo/AppIconYellow76x76@2x.png and /dev/null differ diff --git a/DuckDuckGo/AppIconYellow83.5x83.5@2x.png b/DuckDuckGo/AppIconYellow83.5x83.5@2x.png deleted file mode 100644 index 96634519b6..0000000000 Binary files a/DuckDuckGo/AppIconYellow83.5x83.5@2x.png and /dev/null differ diff --git a/DuckDuckGo/AppLifecycle/AppStateTransitions.swift b/DuckDuckGo/AppLifecycle/AppStateTransitions.swift index 8e30596fee..e7a5df5fbd 100644 --- a/DuckDuckGo/AppLifecycle/AppStateTransitions.swift +++ b/DuckDuckGo/AppLifecycle/AppStateTransitions.swift @@ -41,7 +41,9 @@ extension Launched { return Active(application: application) case .openURL: return self - case .launching, .suspending, .backgrounding: + case .backgrounding: + return InactiveBackground() + case .launching, .suspending: return handleUnexpectedEvent(event) } } @@ -54,7 +56,9 @@ extension Active { switch event { case .suspending(let application): return Inactive(application: application) - case .launching, .activating, .backgrounding, .openURL: + case .openURL: + return self + case .launching, .activating, .backgrounding: return handleUnexpectedEvent(event) } } @@ -69,7 +73,9 @@ extension Inactive { return Background(application: application) case .activating(let application): return Active(application: application) - case .launching, .suspending, .openURL: + case .openURL: + return self + case .launching, .suspending: return handleUnexpectedEvent(event) } } @@ -84,13 +90,52 @@ extension Background { return Active(application: application) case .openURL: return self - case .launching, .suspending, .backgrounding: + case .backgrounding: + return DoubleBackground() + case .launching, .suspending: return handleUnexpectedEvent(event) } } } +extension DoubleBackground { + + func apply(event: AppEvent) -> any AppState { + // report event so we know what events can be called at this moment, but do not let SM be stuck in this state just not to be flooded with these events + _ = handleUnexpectedEvent(event) + + switch event { + case .activating(let application): + return Active(application: application) + case .suspending(let application): + return Inactive(application: application) + case .launching, .backgrounding, .openURL: + return self + } + + } + +} + +extension InactiveBackground { + + func apply(event: AppEvent) -> any AppState { + // report event so we know what events can be called at this moment, but do not let SM be stuck in this state just not to be flooded with these events + _ = handleUnexpectedEvent(event) + + switch event { + case .activating(let application): + return Active(application: application) + case .suspending(let application): + return Inactive(application: application) + case .launching, .backgrounding, .openURL: + return self + } + } + +} + extension AppEvent { var rawValue: String { diff --git a/DuckDuckGo/AppLifecycle/AppStates/Background.swift b/DuckDuckGo/AppLifecycle/AppStates/Background.swift index 9332e41b6b..71b0ab4c1a 100644 --- a/DuckDuckGo/AppLifecycle/AppStates/Background.swift +++ b/DuckDuckGo/AppLifecycle/AppStates/Background.swift @@ -26,3 +26,7 @@ struct Background: AppState { } } + +struct DoubleBackground: AppState { + +} diff --git a/DuckDuckGo/AppLifecycle/AppStates/InactiveBackground.swift b/DuckDuckGo/AppLifecycle/AppStates/InactiveBackground.swift new file mode 100644 index 0000000000..e9c87477d9 --- /dev/null +++ b/DuckDuckGo/AppLifecycle/AppStates/InactiveBackground.swift @@ -0,0 +1,22 @@ +// +// InactiveBackground.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +struct InactiveBackground: AppState { + +} diff --git a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/1024pt.png b/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/1024pt.png deleted file mode 100644 index dd1be128f8..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/1024pt.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/20pt@2x.png b/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/20pt@2x.png deleted file mode 100644 index d224984325..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/20pt@2x.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/29pt-1.png b/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/29pt-1.png deleted file mode 100644 index cb22b9305b..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/29pt-1.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/29pt.png b/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/29pt.png deleted file mode 100644 index cb22b9305b..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/29pt.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/29pt@2x-1.png b/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/29pt@2x-1.png deleted file mode 100644 index 2367ddcca0..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/29pt@2x-1.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/29pt@2x.png b/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/29pt@2x.png deleted file mode 100644 index 2367ddcca0..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/29pt@2x.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/29pt@3x.png b/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/29pt@3x.png deleted file mode 100644 index 8b6c16506f..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/29pt@3x.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/40pt@2x-1.png b/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/40pt@2x-1.png deleted file mode 100644 index 6370e3f011..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/40pt@2x-1.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/40pt@2x.png b/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/40pt@2x.png deleted file mode 100644 index 6370e3f011..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/40pt@2x.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/40pt@3x.png b/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/40pt@3x.png deleted file mode 100644 index 320d0f7902..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/40pt@3x.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/50pt.png b/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/50pt.png deleted file mode 100644 index ef31c4a98c..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/50pt.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/50pt@2x.png b/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/50pt@2x.png deleted file mode 100644 index 73231dcbab..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/50pt@2x.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/60pt@2x-1.png b/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/60pt@2x-1.png deleted file mode 100644 index 320d0f7902..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/60pt@2x-1.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/60pt@2x.png b/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/60pt@2x.png deleted file mode 100644 index 320d0f7902..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/60pt@2x.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/60pt@3x-1.png b/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/60pt@3x-1.png deleted file mode 100644 index a094e8181b..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/60pt@3x-1.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/60pt@3x.png b/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/60pt@3x.png deleted file mode 100644 index a094e8181b..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/60pt@3x.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/76pt.png b/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/76pt.png deleted file mode 100644 index ac5691fcb9..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/76pt.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/76pt@2x.png b/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/76pt@2x.png deleted file mode 100644 index 00f9623cd9..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/76pt@2x.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/83,5pt@2x.png b/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/83,5pt@2x.png deleted file mode 100644 index 183eb856f5..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/83,5pt@2x.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/Contents.json b/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/Contents.json index affbe23b1d..e91cbd33df 100644 --- a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,322 +1,33 @@ { "images" : [ { - "idiom" : "iphone", - "scale" : "2x", - "size" : "20x20" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "20x20" - }, - { - "filename" : "29pt.png", - "idiom" : "iphone", - "scale" : "1x", - "size" : "29x29" - }, - { - "filename" : "29pt@2x.png", - "idiom" : "iphone", - "scale" : "2x", - "size" : "29x29" - }, - { - "filename" : "29pt@3x.png", - "idiom" : "iphone", - "scale" : "3x", - "size" : "29x29" - }, - { - "filename" : "40pt@2x.png", - "idiom" : "iphone", - "scale" : "2x", - "size" : "40x40" - }, - { - "filename" : "40pt@3x.png", - "idiom" : "iphone", - "scale" : "3x", - "size" : "40x40" - }, - { - "idiom" : "iphone", - "scale" : "1x", - "size" : "57x57" - }, - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "57x57" - }, - { - "filename" : "60pt@2x.png", - "idiom" : "iphone", - "scale" : "2x", - "size" : "60x60" - }, - { - "filename" : "60pt@3x.png", - "idiom" : "iphone", - "scale" : "3x", - "size" : "60x60" - }, - { - "idiom" : "ipad", - "scale" : "1x", - "size" : "20x20" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "20x20" - }, - { - "filename" : "29pt-1.png", - "idiom" : "ipad", - "scale" : "1x", - "size" : "29x29" - }, - { - "filename" : "29pt@2x-1.png", - "idiom" : "ipad", - "scale" : "2x", - "size" : "29x29" - }, - { - "filename" : "20pt@2x.png", - "idiom" : "ipad", - "scale" : "1x", - "size" : "40x40" - }, - { - "filename" : "40pt@2x-1.png", - "idiom" : "ipad", - "scale" : "2x", - "size" : "40x40" - }, - { - "filename" : "50pt.png", - "idiom" : "ipad", - "scale" : "1x", - "size" : "50x50" - }, - { - "filename" : "50pt@2x.png", - "idiom" : "ipad", - "scale" : "2x", - "size" : "50x50" - }, - { - "idiom" : "ipad", - "scale" : "1x", - "size" : "72x72" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "72x72" - }, - { - "filename" : "76pt.png", - "idiom" : "ipad", - "scale" : "1x", - "size" : "76x76" - }, - { - "filename" : "76pt@2x.png", - "idiom" : "ipad", - "scale" : "2x", - "size" : "76x76" - }, - { - "filename" : "83,5pt@2x.png", - "idiom" : "ipad", - "scale" : "2x", - "size" : "83.5x83.5" - }, - { - "filename" : "60pt@2x-1.png", - "idiom" : "car", - "scale" : "2x", - "size" : "60x60" - }, - { - "filename" : "60pt@3x-1.png", - "idiom" : "car", - "scale" : "3x", - "size" : "60x60" - }, - { - "filename" : "1024pt.png", - "idiom" : "ios-marketing", - "scale" : "1x", + "filename" : "Icon-Light-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", "size" : "1024x1024" }, { - "idiom" : "mac", - "scale" : "1x", - "size" : "16x16" - }, - { - "idiom" : "mac", - "scale" : "2x", - "size" : "16x16" - }, - { - "idiom" : "mac", - "scale" : "1x", - "size" : "32x32" - }, - { - "idiom" : "mac", - "scale" : "2x", - "size" : "32x32" - }, - { - "idiom" : "mac", - "scale" : "1x", - "size" : "128x128" - }, - { - "idiom" : "mac", - "scale" : "2x", - "size" : "128x128" - }, - { - "idiom" : "mac", - "scale" : "1x", - "size" : "256x256" - }, - { - "idiom" : "mac", - "scale" : "2x", - "size" : "256x256" - }, - { - "idiom" : "mac", - "scale" : "1x", - "size" : "512x512" - }, - { - "idiom" : "mac", - "scale" : "2x", - "size" : "512x512" - }, - { - "idiom" : "watch", - "role" : "notificationCenter", - "scale" : "2x", - "size" : "24x24", - "subtype" : "38mm" - }, - { - "idiom" : "watch", - "role" : "notificationCenter", - "scale" : "2x", - "size" : "27.5x27.5", - "subtype" : "42mm" - }, - { - "idiom" : "watch", - "role" : "companionSettings", - "scale" : "2x", - "size" : "29x29" - }, - { - "idiom" : "watch", - "role" : "companionSettings", - "scale" : "3x", - "size" : "29x29" - }, - { - "idiom" : "watch", - "role" : "notificationCenter", - "scale" : "2x", - "size" : "33x33", - "subtype" : "45mm" - }, - { - "idiom" : "watch", - "role" : "appLauncher", - "scale" : "2x", - "size" : "40x40", - "subtype" : "38mm" - }, - { - "idiom" : "watch", - "role" : "appLauncher", - "scale" : "2x", - "size" : "44x44", - "subtype" : "40mm" - }, - { - "idiom" : "watch", - "role" : "appLauncher", - "scale" : "2x", - "size" : "46x46", - "subtype" : "41mm" - }, - { - "idiom" : "watch", - "role" : "appLauncher", - "scale" : "2x", - "size" : "50x50", - "subtype" : "44mm" - }, - { - "idiom" : "watch", - "role" : "appLauncher", - "scale" : "2x", - "size" : "51x51", - "subtype" : "45mm" - }, - { - "idiom" : "watch", - "role" : "appLauncher", - "scale" : "2x", - "size" : "54x54", - "subtype" : "49mm" - }, - { - "idiom" : "watch", - "role" : "quickLook", - "scale" : "2x", - "size" : "86x86", - "subtype" : "38mm" - }, - { - "idiom" : "watch", - "role" : "quickLook", - "scale" : "2x", - "size" : "98x98", - "subtype" : "42mm" - }, - { - "idiom" : "watch", - "role" : "quickLook", - "scale" : "2x", - "size" : "108x108", - "subtype" : "44mm" - }, - { - "idiom" : "watch", - "role" : "quickLook", - "scale" : "2x", - "size" : "117x117", - "subtype" : "45mm" - }, - { - "idiom" : "watch", - "role" : "quickLook", - "scale" : "2x", - "size" : "129x129", - "subtype" : "49mm" + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "filename" : "Icon-Dark-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" }, { - "idiom" : "watch-marketing", - "scale" : "1x", + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "filename" : "Icon-Tinted-1024x1024.png", + "idiom" : "universal", + "platform" : "ios", "size" : "1024x1024" } ], diff --git a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/Icon-Dark-1024x1024.png b/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/Icon-Dark-1024x1024.png new file mode 100644 index 0000000000..e2e01ac0c5 Binary files /dev/null and b/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/Icon-Dark-1024x1024.png differ diff --git a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/Icon-Light-1024x1024.png b/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/Icon-Light-1024x1024.png new file mode 100644 index 0000000000..dd13311edb Binary files /dev/null and b/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/Icon-Light-1024x1024.png differ diff --git a/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/Icon-Tinted-1024x1024.png b/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/Icon-Tinted-1024x1024.png new file mode 100644 index 0000000000..45bf7060cc Binary files /dev/null and b/DuckDuckGo/Assets.xcassets/AppIcon.appiconset/Icon-Tinted-1024x1024.png differ diff --git a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/29 1.png b/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/29 1.png deleted file mode 100644 index 0904196f44..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/29 1.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/29.png b/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/29.png deleted file mode 100644 index 0904196f44..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/29.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/29@2x 1.png b/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/29@2x 1.png deleted file mode 100644 index 7f792888f9..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/29@2x 1.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/29@2x.png b/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/29@2x.png deleted file mode 100644 index 7f792888f9..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/29@2x.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/29@3x.png b/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/29@3x.png deleted file mode 100644 index 41f1b91565..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/29@3x.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/40 1.png b/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/40 1.png deleted file mode 100644 index 5107820e2c..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/40 1.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/40@2x 1.png b/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/40@2x 1.png deleted file mode 100644 index 7591436357..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/40@2x 1.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/40@2x.png b/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/40@2x.png deleted file mode 100644 index 7591436357..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/40@2x.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/40@3x.png b/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/40@3x.png deleted file mode 100644 index f41ad07ec5..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/40@3x.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/50.png b/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/50.png deleted file mode 100644 index 19215d311c..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/50.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/50@2x.png b/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/50@2x.png deleted file mode 100644 index 07ccfd7d36..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/50@2x.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/60@2x 1.png b/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/60@2x 1.png deleted file mode 100644 index d9b663fda9..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/60@2x 1.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/60@2x.png b/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/60@2x.png deleted file mode 100644 index d9b663fda9..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/60@2x.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/60@3x 1.png b/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/60@3x 1.png deleted file mode 100644 index bfc30c8ba7..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/60@3x 1.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/60@3x.png b/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/60@3x.png deleted file mode 100644 index bfc30c8ba7..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/60@3x.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/76.png b/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/76.png deleted file mode 100644 index 7cceafaec6..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/76.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/76@2x.png b/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/76@2x.png deleted file mode 100644 index 8e1caa7049..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/76@2x.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/83-5@2x.png b/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/83-5@2x.png deleted file mode 100644 index 68f891674b..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/83-5@2x.png and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/Contents.json b/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/Contents.json index 186dd9b2f0..cff1680b35 100644 --- a/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/Contents.json +++ b/DuckDuckGo/Assets.xcassets/DDG-AppIcon-Alpha.appiconset/Contents.json @@ -1,322 +1,9 @@ { "images" : [ - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "20x20" - }, - { - "idiom" : "iphone", - "scale" : "3x", - "size" : "20x20" - }, - { - "filename" : "29.png", - "idiom" : "iphone", - "scale" : "1x", - "size" : "29x29" - }, - { - "filename" : "29@2x.png", - "idiom" : "iphone", - "scale" : "2x", - "size" : "29x29" - }, - { - "filename" : "29@3x.png", - "idiom" : "iphone", - "scale" : "3x", - "size" : "29x29" - }, - { - "filename" : "40@2x.png", - "idiom" : "iphone", - "scale" : "2x", - "size" : "40x40" - }, - { - "filename" : "40@3x.png", - "idiom" : "iphone", - "scale" : "3x", - "size" : "40x40" - }, - { - "idiom" : "iphone", - "scale" : "1x", - "size" : "57x57" - }, - { - "idiom" : "iphone", - "scale" : "2x", - "size" : "57x57" - }, - { - "filename" : "60@2x.png", - "idiom" : "iphone", - "scale" : "2x", - "size" : "60x60" - }, - { - "filename" : "60@3x.png", - "idiom" : "iphone", - "scale" : "3x", - "size" : "60x60" - }, - { - "idiom" : "ipad", - "scale" : "1x", - "size" : "20x20" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "20x20" - }, - { - "filename" : "29 1.png", - "idiom" : "ipad", - "scale" : "1x", - "size" : "29x29" - }, - { - "filename" : "29@2x 1.png", - "idiom" : "ipad", - "scale" : "2x", - "size" : "29x29" - }, - { - "filename" : "40 1.png", - "idiom" : "ipad", - "scale" : "1x", - "size" : "40x40" - }, - { - "filename" : "40@2x 1.png", - "idiom" : "ipad", - "scale" : "2x", - "size" : "40x40" - }, - { - "filename" : "50.png", - "idiom" : "ipad", - "scale" : "1x", - "size" : "50x50" - }, - { - "filename" : "50@2x.png", - "idiom" : "ipad", - "scale" : "2x", - "size" : "50x50" - }, - { - "idiom" : "ipad", - "scale" : "1x", - "size" : "72x72" - }, - { - "idiom" : "ipad", - "scale" : "2x", - "size" : "72x72" - }, - { - "filename" : "76.png", - "idiom" : "ipad", - "scale" : "1x", - "size" : "76x76" - }, - { - "filename" : "76@2x.png", - "idiom" : "ipad", - "scale" : "2x", - "size" : "76x76" - }, - { - "filename" : "83-5@2x.png", - "idiom" : "ipad", - "scale" : "2x", - "size" : "83.5x83.5" - }, - { - "filename" : "60@2x 1.png", - "idiom" : "car", - "scale" : "2x", - "size" : "60x60" - }, - { - "filename" : "60@3x 1.png", - "idiom" : "car", - "scale" : "3x", - "size" : "60x60" - }, { "filename" : "1024.png", - "idiom" : "ios-marketing", - "scale" : "1x", - "size" : "1024x1024" - }, - { - "idiom" : "mac", - "scale" : "1x", - "size" : "16x16" - }, - { - "idiom" : "mac", - "scale" : "2x", - "size" : "16x16" - }, - { - "idiom" : "mac", - "scale" : "1x", - "size" : "32x32" - }, - { - "idiom" : "mac", - "scale" : "2x", - "size" : "32x32" - }, - { - "idiom" : "mac", - "scale" : "1x", - "size" : "128x128" - }, - { - "idiom" : "mac", - "scale" : "2x", - "size" : "128x128" - }, - { - "idiom" : "mac", - "scale" : "1x", - "size" : "256x256" - }, - { - "idiom" : "mac", - "scale" : "2x", - "size" : "256x256" - }, - { - "idiom" : "mac", - "scale" : "1x", - "size" : "512x512" - }, - { - "idiom" : "mac", - "scale" : "2x", - "size" : "512x512" - }, - { - "idiom" : "watch", - "role" : "notificationCenter", - "scale" : "2x", - "size" : "24x24", - "subtype" : "38mm" - }, - { - "idiom" : "watch", - "role" : "notificationCenter", - "scale" : "2x", - "size" : "27.5x27.5", - "subtype" : "42mm" - }, - { - "idiom" : "watch", - "role" : "companionSettings", - "scale" : "2x", - "size" : "29x29" - }, - { - "idiom" : "watch", - "role" : "companionSettings", - "scale" : "3x", - "size" : "29x29" - }, - { - "idiom" : "watch", - "role" : "notificationCenter", - "scale" : "2x", - "size" : "33x33", - "subtype" : "45mm" - }, - { - "idiom" : "watch", - "role" : "appLauncher", - "scale" : "2x", - "size" : "40x40", - "subtype" : "38mm" - }, - { - "idiom" : "watch", - "role" : "appLauncher", - "scale" : "2x", - "size" : "44x44", - "subtype" : "40mm" - }, - { - "idiom" : "watch", - "role" : "appLauncher", - "scale" : "2x", - "size" : "46x46", - "subtype" : "41mm" - }, - { - "idiom" : "watch", - "role" : "appLauncher", - "scale" : "2x", - "size" : "50x50", - "subtype" : "44mm" - }, - { - "idiom" : "watch", - "role" : "appLauncher", - "scale" : "2x", - "size" : "51x51", - "subtype" : "45mm" - }, - { - "idiom" : "watch", - "role" : "appLauncher", - "scale" : "2x", - "size" : "54x54", - "subtype" : "49mm" - }, - { - "idiom" : "watch", - "role" : "quickLook", - "scale" : "2x", - "size" : "86x86", - "subtype" : "38mm" - }, - { - "idiom" : "watch", - "role" : "quickLook", - "scale" : "2x", - "size" : "98x98", - "subtype" : "42mm" - }, - { - "idiom" : "watch", - "role" : "quickLook", - "scale" : "2x", - "size" : "108x108", - "subtype" : "44mm" - }, - { - "idiom" : "watch", - "role" : "quickLook", - "scale" : "2x", - "size" : "117x117", - "subtype" : "45mm" - }, - { - "idiom" : "watch", - "role" : "quickLook", - "scale" : "2x", - "size" : "129x129", - "subtype" : "49mm" - }, - { - "idiom" : "watch-marketing", - "scale" : "1x", + "idiom" : "universal", + "platform" : "ios", "size" : "1024x1024" } ], diff --git a/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/AIChat-24.imageset/AIChat-24.pdf b/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/AIChat-24.imageset/AIChat-24.pdf new file mode 100644 index 0000000000..b806da0f59 Binary files /dev/null and b/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/AIChat-24.imageset/AIChat-24.pdf differ diff --git a/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/AIChat-24.imageset/Contents.json b/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/AIChat-24.imageset/Contents.json new file mode 100644 index 0000000000..014a3657bf --- /dev/null +++ b/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/AIChat-24.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "AIChat-24.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/DuckDuckGo/AuthConfirmationPromptViewModel.swift b/DuckDuckGo/AuthConfirmationPromptViewModel.swift index 0ac7cadc50..385f190d8d 100644 --- a/DuckDuckGo/AuthConfirmationPromptViewModel.swift +++ b/DuckDuckGo/AuthConfirmationPromptViewModel.swift @@ -29,7 +29,8 @@ protocol AuthConfirmationPromptViewModelDelegate: AnyObject { final class AuthConfirmationPromptViewModel: ObservableObject { weak var delegate: AuthConfirmationPromptViewModelDelegate? - private let authenticator = AutofillLoginListAuthenticator(reason: UserText.autofillDeleteAllPasswordsAuthenticationReason) + private let authenticator = AutofillLoginListAuthenticator(reason: UserText.autofillDeleteAllPasswordsAuthenticationReason, + cancelTitle: UserText.autofillLoginListAuthenticationCancelButton) var contentHeight: CGFloat = AutofillViews.deleteAllPromptMinHeight { didSet { diff --git a/DuckDuckGo/AutoClearSettingsViewController.swift b/DuckDuckGo/AutoClearSettingsViewController.swift index 14c4178ac0..a5c4eb934a 100644 --- a/DuckDuckGo/AutoClearSettingsViewController.swift +++ b/DuckDuckGo/AutoClearSettingsViewController.swift @@ -55,7 +55,12 @@ class AutoClearSettingsViewController: UITableViewController { decorate() } - + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + Pixel.fire(pixel: .settingsDataClearingClearDataOpen) + } + private func loadClearDataSettings() -> AutoClearSettingsModel? { return AutoClearSettingsModel(settings: appSettings) } @@ -152,6 +157,8 @@ class AutoClearSettingsViewController: UITableViewController { } @IBAction func onClearDataToggled(_ sender: UISwitch) { + Pixel.fire(pixel: sender.isOn ? .settingsAutomaticallyClearDataOn : .settingsAutomaticallyClearDataOff) + if sender.isOn { clearDataSettings = AutoClearSettingsModel() tableView.insertSections(.init(integersIn: Sections.action.rawValue...Sections.timing.rawValue), with: .fade) diff --git a/DuckDuckGo/Autoconsent/autoconsent-bundle.js b/DuckDuckGo/Autoconsent/autoconsent-bundle.js index 9d01909f60..1f3db588b9 100644 --- a/DuckDuckGo/Autoconsent/autoconsent-bundle.js +++ b/DuckDuckGo/Autoconsent/autoconsent-bundle.js @@ -1,4 +1,4 @@ -!function(){"use strict";var e=class e{static setBase(t){e.base=t}static findElement(t,o=null,i=!1){let n=null;return n=null!=o?Array.from(o.querySelectorAll(t.selector)):null!=e.base?Array.from(e.base.querySelectorAll(t.selector)):Array.from(document.querySelectorAll(t.selector)),null!=t.textFilter&&(n=n.filter((e=>{const o=e.textContent.toLowerCase();if(Array.isArray(t.textFilter)){let e=!1;for(const i of t.textFilter)if(-1!==o.indexOf(i.toLowerCase())){e=!0;break}return e}return null!=t.textFilter&&-1!==o.indexOf(t.textFilter.toLowerCase())}))),null!=t.styleFilters&&(n=n.filter((e=>{const o=window.getComputedStyle(e);let i=!0;for(const e of t.styleFilters){const t=o[e.option];i=e.negated?i&&t!==e.value:i&&t===e.value}return i}))),null!=t.displayFilter&&(n=n.filter((e=>t.displayFilter?0!==e.offsetHeight:0===e.offsetHeight))),null!=t.iframeFilter&&(n=n.filter((()=>t.iframeFilter?window.location!==window.parent.location:window.location===window.parent.location))),null!=t.childFilter&&(n=n.filter((o=>{const i=e.base;e.setBase(o);const n=e.find(t.childFilter);return e.setBase(i),null!=n.target}))),i?n:(n.length>1&&console.warn("Multiple possible targets: ",n,t,o),n[0])}static find(t,o=!1){const i=[];if(null!=t.parent){const n=e.findElement(t.parent,null,o);if(null!=n){if(n instanceof Array)return n.forEach((n=>{const s=e.findElement(t.target,n,o);s instanceof Array?s.forEach((e=>{i.push({parent:n,target:e})})):i.push({parent:n,target:s})})),i;{const s=e.findElement(t.target,n,o);s instanceof Array?s.forEach((e=>{i.push({parent:n,target:e})})):i.push({parent:n,target:s})}}}else{const n=e.findElement(t.target,null,o);n instanceof Array?n.forEach((e=>{i.push({parent:null,target:e})})):i.push({parent:null,target:n})}return 0===i.length&&i.push({parent:null,target:null}),o?i:(1!==i.length&&console.warn("Multiple results found, even though multiple false",i),i[0])}};e.base=null;var t=e;function o(e){const o=t.find(e);return"css"===e.type?!!o.target:"checkbox"===e.type?!!o.target&&o.target.checked:void 0}async function i(e,c){switch(e.type){case"click":return async function(e){const o=t.find(e);null!=o.target&&o.target.click();return s(n)}(e);case"list":return async function(e,t){for(const o of e.actions)await i(o,t)}(e,c);case"consent":return async function(e,t){for(const n of e.consents){const e=-1!==t.indexOf(n.type);if(n.matcher&&n.toggleAction){o(n.matcher)!==e&&await i(n.toggleAction)}else e?await i(n.trueAction):await i(n.falseAction)}}(e,c);case"ifcss":return async function(e,o){const n=t.find(e);n.target?e.falseAction&&await i(e.falseAction,o):e.trueAction&&await i(e.trueAction,o)}(e,c);case"waitcss":return async function(e){await new Promise((o=>{let i=e.retries||10;const n=e.waitTime||250,s=()=>{const c=t.find(e);(e.negated&&c.target||!e.negated&&!c.target)&&i>0?(i-=1,setTimeout(s,n)):o()};s()}))}(e);case"foreach":return async function(e,o){const n=t.find(e,!0),s=t.base;for(const s of n)s.target&&(t.setBase(s.target),await i(e.action,o));t.setBase(s)}(e,c);case"hide":return async function(e){const o=t.find(e);o.target&&o.target.classList.add("Autoconsent-Hidden")}(e);case"slide":return async function(e){const o=t.find(e),i=t.find(e.dragTarget);if(o.target){const e=o.target.getBoundingClientRect(),t=i.target.getBoundingClientRect();let n=t.top-e.top,s=t.left-e.left;"y"===this.config.axis.toLowerCase()&&(s=0),"x"===this.config.axis.toLowerCase()&&(n=0);const c=window.screenX+e.left+e.width/2,r=window.screenY+e.top+e.height/2,a=e.left+e.width/2,l=e.top+e.height/2,p=document.createEvent("MouseEvents");p.initMouseEvent("mousedown",!0,!0,window,0,c,r,a,l,!1,!1,!1,!1,0,o.target);const d=document.createEvent("MouseEvents");d.initMouseEvent("mousemove",!0,!0,window,0,c+s,r+n,a+s,l+n,!1,!1,!1,!1,0,o.target);const u=document.createEvent("MouseEvents");u.initMouseEvent("mouseup",!0,!0,window,0,c+s,r+n,a+s,l+n,!1,!1,!1,!1,0,o.target),o.target.dispatchEvent(p),await this.waitTimeout(10),o.target.dispatchEvent(d),await this.waitTimeout(10),o.target.dispatchEvent(u)}}(e);case"close":return async function(){window.close()}();case"wait":return async function(e){await s(e.waitTime)}(e);case"eval":return async function(e){return console.log("eval!",e.code),new Promise((t=>{try{e.async?(window.eval(e.code),setTimeout((()=>{t(window.eval("window.__consentCheckResult"))}),e.timeout||250)):t(window.eval(e.code))}catch(o){console.warn("eval error",o,e.code),t(!1)}}))}(e);default:throw new Error("Unknown action type: "+e.type)}}var n=0;function s(e){return new Promise((t=>{setTimeout((()=>{t()}),e)}))}function c(){return crypto&&void 0!==crypto.randomUUID?crypto.randomUUID():Math.random().toString()}var r=class{constructor(e,t=1e3){this.id=e,this.promise=new Promise(((e,t)=>{this.resolve=e,this.reject=t})),this.timer=window.setTimeout((()=>{this.reject(new Error("timeout"))}),t)}},a={pending:new Map,sendContentMessage:null};var l={EVAL_0:()=>console.log(1),EVAL_CONSENTMANAGER_1:()=>window.__cmp&&"object"==typeof __cmp("getCMPData"),EVAL_CONSENTMANAGER_2:()=>!__cmp("consentStatus").userChoiceExists,EVAL_CONSENTMANAGER_3:()=>__cmp("setConsent",0),EVAL_CONSENTMANAGER_4:()=>__cmp("setConsent",1),EVAL_CONSENTMANAGER_5:()=>__cmp("consentStatus").userChoiceExists,EVAL_COOKIEBOT_1:()=>!!window.Cookiebot,EVAL_COOKIEBOT_2:()=>!window.Cookiebot.hasResponse&&!0===window.Cookiebot.dialog?.visible,EVAL_COOKIEBOT_3:()=>window.Cookiebot.withdraw()||!0,EVAL_COOKIEBOT_4:()=>window.Cookiebot.hide()||!0,EVAL_COOKIEBOT_5:()=>!0===window.Cookiebot.declined,EVAL_KLARO_1:()=>{const e=globalThis.klaroConfig||globalThis.klaro?.getManager&&globalThis.klaro.getManager().config;if(!e)return!0;const t=(e.services||e.apps).filter((e=>!e.required)).map((e=>e.name));if(klaro&&klaro.getManager){const e=klaro.getManager();return t.every((t=>!e.consents[t]))}if(klaroConfig&&"cookie"===klaroConfig.storageMethod){const e=klaroConfig.cookieName||klaroConfig.storageName,o=JSON.parse(decodeURIComponent(document.cookie.split(";").find((t=>t.trim().startsWith(e))).split("=")[1]));return Object.keys(o).filter((e=>t.includes(e))).every((e=>!1===o[e]))}},EVAL_KLARO_OPEN_POPUP:()=>{klaro.show(void 0,!0)},EVAL_KLARO_TRY_API_OPT_OUT:()=>{if(window.klaro&&"function"==typeof klaro.show&&"function"==typeof klaro.getManager)try{return klaro.getManager().changeAll(!1),klaro.getManager().saveAndApplyConsents(),!0}catch(e){return console.warn(e),!1}return!1},EVAL_ONETRUST_1:()=>window.OnetrustActiveGroups.split(",").filter((e=>e.length>0)).length<=1,EVAL_TRUSTARC_TOP:()=>window&&window.truste&&"0"===window.truste.eu.bindMap.prefCookie,EVAL_TRUSTARC_FRAME_TEST:()=>window&&window.QueryString&&"0"===window.QueryString.preferences,EVAL_TRUSTARC_FRAME_GTM:()=>window&&window.QueryString&&"1"===window.QueryString.gtm,EVAL_ABC_TEST:()=>document.cookie.includes("trackingconsent"),EVAL_ADROLL_0:()=>!document.cookie.includes("__adroll_fpc"),EVAL_ALMACMP_0:()=>document.cookie.includes('"name":"Google","consent":false'),EVAL_AFFINITY_SERIF_COM_0:()=>document.cookie.includes("serif_manage_cookies_viewed")&&!document.cookie.includes("serif_allow_analytics"),EVAL_ARBEITSAGENTUR_TEST:()=>document.cookie.includes("cookie_consent=denied"),EVAL_AXEPTIO_0:()=>document.cookie.includes("axeptio_authorized_vendors=%2C%2C"),EVAL_BAHN_TEST:()=>1===utag.gdpr.getSelectedCategories().length,EVAL_BING_0:()=>document.cookie.includes("AD=0"),EVAL_BLOCKSY_0:()=>document.cookie.includes("blocksy_cookies_consent_accepted=no"),EVAL_BORLABS_0:()=>!JSON.parse(decodeURIComponent(document.cookie.split(";").find((e=>-1!==e.indexOf("borlabs-cookie"))).split("=",2)[1])).consents.statistics,EVAL_BUNDESREGIERUNG_DE_0:()=>document.cookie.match("cookie-allow-tracking=0"),EVAL_CANVA_0:()=>!document.cookie.includes("gtm_fpc_engagement_event"),EVAL_CC_BANNER2_0:()=>!!document.cookie.match(/sncc=[^;]+D%3Dtrue/),EVAL_CLICKIO_0:()=>document.cookie.includes("__lxG__consent__v2_daisybit="),EVAL_CLINCH_0:()=>document.cookie.includes("ctc_rejected=1"),EVAL_COOKIECONSENT2_TEST:()=>document.cookie.includes("cc_cookie="),EVAL_COOKIECONSENT3_TEST:()=>document.cookie.includes("cc_cookie="),EVAL_COINBASE_0:()=>JSON.parse(decodeURIComponent(document.cookie.match(/cm_(eu|default)_preferences=([0-9a-zA-Z\\{\\}\\[\\]%:]*);?/)[2])).consent.length<=1,EVAL_COMPLIANZ_BANNER_0:()=>document.cookie.includes("cmplz_banner-status=dismissed"),EVAL_COOKIE_LAW_INFO_0:()=>CLI.disableAllCookies()||CLI.reject_close()||!0,EVAL_COOKIE_LAW_INFO_1:()=>-1===document.cookie.indexOf("cookielawinfo-checkbox-non-necessary=yes"),EVAL_COOKIE_LAW_INFO_DETECT:()=>!!window.CLI,EVAL_COOKIE_MANAGER_POPUP_0:()=>!1===JSON.parse(document.cookie.split(";").find((e=>e.trim().startsWith("CookieLevel"))).split("=")[1]).social,EVAL_COOKIEALERT_0:()=>document.querySelector("body").removeAttribute("style")||!0,EVAL_COOKIEALERT_1:()=>document.querySelector("body").removeAttribute("style")||!0,EVAL_COOKIEALERT_2:()=>!0===window.CookieConsent.declined,EVAL_COOKIEFIRST_0:()=>{return!1===(e=JSON.parse(decodeURIComponent(document.cookie.split(";").find((e=>-1!==e.indexOf("cookiefirst"))).trim()).split("=")[1])).performance&&!1===e.functional&&!1===e.advertising;var e},EVAL_COOKIEFIRST_1:()=>document.querySelectorAll("button[data-cookiefirst-accent-color=true][role=checkbox]:not([disabled])").forEach((e=>"true"===e.getAttribute("aria-checked")&&e.click()))||!0,EVAL_COOKIEINFORMATION_0:()=>CookieInformation.declineAllCategories()||!0,EVAL_COOKIEINFORMATION_1:()=>CookieInformation.submitAllCategories()||!0,EVAL_COOKIEINFORMATION_2:()=>document.cookie.includes("CookieInformationConsent="),EVAL_COOKIEYES_0:()=>document.cookie.includes("advertisement:no"),EVAL_DAILYMOTION_0:()=>!!document.cookie.match("dm-euconsent-v2"),EVAL_DNDBEYOND_TEST:()=>document.cookie.includes("cookie-consent=denied"),EVAL_DSGVO_0:()=>!document.cookie.includes("sp_dsgvo_cookie_settings"),EVAL_DUNELM_0:()=>document.cookie.includes("cc_functional=0")&&document.cookie.includes("cc_targeting=0"),EVAL_ETSY_0:()=>document.querySelectorAll(".gdpr-overlay-body input").forEach((e=>{e.checked=!1}))||!0,EVAL_ETSY_1:()=>document.querySelector(".gdpr-overlay-view button[data-wt-overlay-close]").click()||!0,EVAL_EU_COOKIE_COMPLIANCE_0:()=>-1===document.cookie.indexOf("cookie-agreed=2"),EVAL_EU_COOKIE_LAW_0:()=>!document.cookie.includes("euCookie"),EVAL_EZOIC_0:()=>ezCMP.handleAcceptAllClick(),EVAL_EZOIC_1:()=>!!document.cookie.match(/ez-consent-tcf/),EVAL_FIDES_DETECT_POPUP:()=>window.Fides?.initialized,EVAL_GOOGLE_0:()=>!!document.cookie.match(/SOCS=CAE/),EVAL_HEMA_TEST_0:()=>document.cookie.includes("cookies_rejected=1"),EVAL_IUBENDA_0:()=>document.querySelectorAll(".purposes-item input[type=checkbox]:not([disabled])").forEach((e=>{e.checked&&e.click()}))||!0,EVAL_IUBENDA_1:()=>!!document.cookie.match(/_iub_cs-\d+=/),EVAL_IWINK_TEST:()=>document.cookie.includes("cookie_permission_granted=no"),EVAL_JQUERY_COOKIEBAR_0:()=>!document.cookie.includes("cookies-state=accepted"),EVAL_KETCH_TEST:()=>document.cookie.includes("_ketch_consent_v1_"),EVAL_MEDIAVINE_0:()=>document.querySelectorAll('[data-name="mediavine-gdpr-cmp"] input[type=checkbox]').forEach((e=>e.checked&&e.click()))||!0,EVAL_MICROSOFT_0:()=>Array.from(document.querySelectorAll("div > button")).filter((e=>e.innerText.match("Reject|Ablehnen")))[0].click()||!0,EVAL_MICROSOFT_1:()=>Array.from(document.querySelectorAll("div > button")).filter((e=>e.innerText.match("Accept|Annehmen")))[0].click()||!0,EVAL_MICROSOFT_2:()=>!!document.cookie.match("MSCC|GHCC"),EVAL_MOOVE_0:()=>document.querySelectorAll("#moove_gdpr_cookie_modal input").forEach((e=>{e.disabled||(e.checked="moove_gdpr_strict_cookies"===e.name||"moove_gdpr_strict_cookies"===e.id)}))||!0,EVAL_ONENINETWO_0:()=>document.cookie.includes("CC_ADVERTISING=NO")&&document.cookie.includes("CC_ANALYTICS=NO"),EVAL_OPENAI_TEST:()=>document.cookie.includes("oai-allow-ne=false"),EVAL_OPERA_0:()=>document.cookie.includes("cookie_consent_essential=true")&&!document.cookie.includes("cookie_consent_marketing=true"),EVAL_PAYPAL_0:()=>!0===document.cookie.includes("cookie_prefs"),EVAL_PRIMEBOX_0:()=>!document.cookie.includes("cb-enabled=accepted"),EVAL_PUBTECH_0:()=>document.cookie.includes("euconsent-v2")&&(document.cookie.match(/.YAAAAAAAAAAA/)||document.cookie.match(/.aAAAAAAAAAAA/)||document.cookie.match(/.YAAACFgAAAAA/)),EVAL_REDDIT_0:()=>document.cookie.includes("eu_cookie={%22opted%22:true%2C%22nonessential%22:false}"),EVAL_ROBLOX_TEST:()=>document.cookie.includes("RBXcb"),EVAL_SKYSCANNER_TEST:()=>document.cookie.match(/gdpr=[^;]*adverts:::false/)&&!document.cookie.match(/gdpr=[^;]*init:::true/),EVAL_SIRDATA_UNBLOCK_SCROLL:()=>(document.documentElement.classList.forEach((e=>{e.startsWith("sd-cmp-")&&document.documentElement.classList.remove(e)})),!0),EVAL_SNIGEL_0:()=>!!document.cookie.match("snconsent"),EVAL_STEAMPOWERED_0:()=>2===JSON.parse(decodeURIComponent(document.cookie.split(";").find((e=>e.trim().startsWith("cookieSettings"))).split("=")[1])).preference_state,EVAL_SVT_TEST:()=>document.cookie.includes('cookie-consent-1={"optedIn":true,"functionality":false,"statistics":false}'),EVAL_TAKEALOT_0:()=>document.body.classList.remove("freeze")||(document.body.style="")||!0,EVAL_TARTEAUCITRON_0:()=>tarteaucitron.userInterface.respondAll(!1)||!0,EVAL_TARTEAUCITRON_1:()=>tarteaucitron.userInterface.respondAll(!0)||!0,EVAL_TARTEAUCITRON_2:()=>document.cookie.match(/tarteaucitron=[^;]*/)?.[0].includes("false"),EVAL_TAUNTON_TEST:()=>document.cookie.includes("taunton_user_consent_submitted=true"),EVAL_TEALIUM_0:()=>void 0!==window.utag&&"object"==typeof utag.gdpr,EVAL_TEALIUM_1:()=>utag.gdpr.setConsentValue(!1)||!0,EVAL_TEALIUM_DONOTSELL:()=>utag.gdpr.dns?.setDnsState(!1)||!0,EVAL_TEALIUM_2:()=>utag.gdpr.setConsentValue(!0)||!0,EVAL_TEALIUM_3:()=>1!==utag.gdpr.getConsentState(),EVAL_TEALIUM_DONOTSELL_CHECK:()=>1!==utag.gdpr.dns?.getDnsState(),EVAL_TESLA_TEST:()=>document.cookie.includes("tsla-cookie-consent=rejected"),EVAL_TESTCMP_STEP:()=>!!document.querySelector("#reject-all"),EVAL_TESTCMP_0:()=>"button_clicked"===window.results.results[0],EVAL_TESTCMP_COSMETIC_0:()=>"banner_hidden"===window.results.results[0],EVAL_THEFREEDICTIONARY_0:()=>cmpUi.showPurposes()||cmpUi.rejectAll()||!0,EVAL_THEFREEDICTIONARY_1:()=>cmpUi.allowAll()||!0,EVAL_THEVERGE_0:()=>document.cookie.includes("_duet_gdpr_acknowledged=1"),EVAL_TWCC_TEST:()=>document.cookie.includes("twCookieConsent="),EVAL_UBUNTU_COM_0:()=>document.cookie.includes("_cookies_accepted=essential"),EVAL_UK_COOKIE_CONSENT_0:()=>!document.cookie.includes("catAccCookies"),EVAL_USERCENTRICS_API_0:()=>"object"==typeof UC_UI,EVAL_USERCENTRICS_API_1:()=>!!UC_UI.closeCMP(),EVAL_USERCENTRICS_API_2:()=>!!UC_UI.denyAllConsents(),EVAL_USERCENTRICS_API_3:()=>!!UC_UI.acceptAllConsents(),EVAL_USERCENTRICS_API_4:()=>!!UC_UI.closeCMP(),EVAL_USERCENTRICS_API_5:()=>!0===UC_UI.areAllConsentsAccepted(),EVAL_USERCENTRICS_API_6:()=>!1===UC_UI.areAllConsentsAccepted(),EVAL_USERCENTRICS_BUTTON_0:()=>JSON.parse(localStorage.getItem("usercentrics")).consents.every((e=>e.isEssential||!e.consentStatus)),EVAL_WAITROSE_0:()=>Array.from(document.querySelectorAll("label[id$=cookies-deny-label]")).forEach((e=>e.click()))||!0,EVAL_WAITROSE_1:()=>document.cookie.includes("wtr_cookies_advertising=0")&&document.cookie.includes("wtr_cookies_analytics=0"),EVAL_WP_COOKIE_NOTICE_0:()=>document.cookie.includes("wpl_viewed_cookie=no"),EVAL_XE_TEST:()=>document.cookie.includes("xeConsentState={%22performance%22:false%2C%22marketing%22:false%2C%22compliance%22:false}"),EVAL_XING_0:()=>document.cookie.includes("userConsent=%7B%22marketing%22%3Afalse"),EVAL_YOUTUBE_DESKTOP_0:()=>!!document.cookie.match(/SOCS=CAE/),EVAL_YOUTUBE_MOBILE_0:()=>!!document.cookie.match(/SOCS=CAE/)};var p={main:!0,frame:!1,urlPattern:""},d=class{constructor(e){this.runContext=p,this.autoconsent=e}get hasSelfTest(){throw new Error("Not Implemented")}get isIntermediate(){throw new Error("Not Implemented")}get isCosmetic(){throw new Error("Not Implemented")}mainWorldEval(e){const t=l[e];if(!t)return console.warn("Snippet not found",e),Promise.resolve(!1);const o=this.autoconsent.config.logs;if(this.autoconsent.config.isMainWorld){o.evals&&console.log("inline eval:",e,t);let i=!1;try{i=!!t.call(globalThis)}catch(t){o.evals&&console.error("error evaluating rule",e,t)}return Promise.resolve(i)}const i=`(${t.toString()})()`;return o.evals&&console.log("async eval:",e,i),function(e,t){const o=c();a.sendContentMessage({type:"eval",id:o,code:e,snippetId:t});const i=new r(o);return a.pending.set(i.id,i),i.promise}(i,e).catch((t=>(o.evals&&console.error("error evaluating rule",e,t),!1)))}checkRunContext(){const e={...p,...this.runContext},t=window.top===window;return!(t&&!e.main)&&(!(!t&&!e.frame)&&!(e.urlPattern&&!window.location.href.match(e.urlPattern)))}detectCmp(){throw new Error("Not Implemented")}async detectPopup(){return!1}optOut(){throw new Error("Not Implemented")}optIn(){throw new Error("Not Implemented")}openCmp(){throw new Error("Not Implemented")}async test(){return Promise.resolve(!0)}click(e,t=!1){return this.autoconsent.domActions.click(e,t)}elementExists(e){return this.autoconsent.domActions.elementExists(e)}elementVisible(e,t){return this.autoconsent.domActions.elementVisible(e,t)}waitForElement(e,t){return this.autoconsent.domActions.waitForElement(e,t)}waitForVisible(e,t,o){return this.autoconsent.domActions.waitForVisible(e,t,o)}waitForThenClick(e,t,o){return this.autoconsent.domActions.waitForThenClick(e,t,o)}wait(e){return this.autoconsent.domActions.wait(e)}hide(e,t){return this.autoconsent.domActions.hide(e,t)}prehide(e){return this.autoconsent.domActions.prehide(e)}undoPrehide(){return this.autoconsent.domActions.undoPrehide()}querySingleReplySelector(e,t){return this.autoconsent.domActions.querySingleReplySelector(e,t)}querySelectorChain(e){return this.autoconsent.domActions.querySelectorChain(e)}elementSelector(e){return this.autoconsent.domActions.elementSelector(e)}},u=class extends d{constructor(e,t){super(t),this.rule=e,this.name=e.name,this.runContext=e.runContext||p}get hasSelfTest(){return!!this.rule.test}get isIntermediate(){return!!this.rule.intermediate}get isCosmetic(){return!!this.rule.cosmetic}get prehideSelectors(){return this.rule.prehideSelectors}async detectCmp(){return!!this.rule.detectCmp&&this._runRulesParallel(this.rule.detectCmp)}async detectPopup(){return!!this.rule.detectPopup&&this._runRulesSequentially(this.rule.detectPopup)}async optOut(){const e=this.autoconsent.config.logs;return!!this.rule.optOut&&(e.lifecycle&&console.log("Initiated optOut()",this.rule.optOut),this._runRulesSequentially(this.rule.optOut))}async optIn(){const e=this.autoconsent.config.logs;return!!this.rule.optIn&&(e.lifecycle&&console.log("Initiated optIn()",this.rule.optIn),this._runRulesSequentially(this.rule.optIn))}async openCmp(){return!!this.rule.openCmp&&this._runRulesSequentially(this.rule.openCmp)}async test(){return this.hasSelfTest?this._runRulesSequentially(this.rule.test):super.test()}async evaluateRuleStep(e){const t=[],o=this.autoconsent.config.logs;if(e.exists&&t.push(this.elementExists(e.exists)),e.visible&&t.push(this.elementVisible(e.visible,e.check)),e.eval){const o=this.mainWorldEval(e.eval);t.push(o)}if(e.waitFor&&t.push(this.waitForElement(e.waitFor,e.timeout)),e.waitForVisible&&t.push(this.waitForVisible(e.waitForVisible,e.timeout,e.check)),e.click&&t.push(this.click(e.click,e.all)),e.waitForThenClick&&t.push(this.waitForThenClick(e.waitForThenClick,e.timeout,e.all)),e.wait&&t.push(this.wait(e.wait)),e.hide&&t.push(this.hide(e.hide,e.method)),e.if){if(!e.if.exists&&!e.if.visible)return console.error("invalid conditional rule",e.if),!1;const i=await this.evaluateRuleStep(e.if);o.rulesteps&&console.log("Condition is",i),i?t.push(this._runRulesSequentially(e.then)):e.else?t.push(this._runRulesSequentially(e.else)):t.push(!0)}if(e.any){for(const t of e.any)if(await this.evaluateRuleStep(t))return!0;return!1}if(0===t.length)return o.errors&&console.warn("Unrecognized rule",e),!1;return(await Promise.all(t)).reduce(((e,t)=>e&&t),!0)}async _runRulesParallel(e){const t=e.map((e=>this.evaluateRuleStep(e)));return(await Promise.all(t)).every((e=>!!e))}async _runRulesSequentially(e){const t=this.autoconsent.config.logs;for(const o of e){t.rulesteps&&console.log("Running rule...",o);const e=await this.evaluateRuleStep(o);if(t.rulesteps&&console.log("...rule result",e),!e&&!o.optional)return!1}return!0}},h=class{constructor(e,t){this.name=e,this.config=t,this.methods=new Map,this.runContext=p,this.isCosmetic=!1,t.methods.forEach((e=>{e.action&&this.methods.set(e.name,e.action)})),this.hasSelfTest=!1}get isIntermediate(){return!1}checkRunContext(){return!0}async detectCmp(){return this.config.detectors.map((e=>o(e.presentMatcher))).some((e=>!!e))}async detectPopup(){return this.config.detectors.map((e=>o(e.showingMatcher))).some((e=>!!e))}async executeAction(e,t){return!this.methods.has(e)||i(this.methods.get(e),t)}async optOut(){return await this.executeAction("HIDE_CMP"),await this.executeAction("OPEN_OPTIONS"),await this.executeAction("HIDE_CMP"),await this.executeAction("DO_CONSENT",[]),await this.executeAction("SAVE_CONSENT"),!0}async optIn(){return await this.executeAction("HIDE_CMP"),await this.executeAction("OPEN_OPTIONS"),await this.executeAction("HIDE_CMP"),await this.executeAction("DO_CONSENT",["D","A","B","E","F","X"]),await this.executeAction("SAVE_CONSENT"),!0}async openCmp(){return await this.executeAction("HIDE_CMP"),await this.executeAction("OPEN_OPTIONS"),!0}async test(){return!0}};function m(e="autoconsent-css-rules"){const t=`style#${e}`,o=document.querySelector(t);if(o&&o instanceof HTMLStyleElement)return o;{const t=document.head||document.getElementsByTagName("head")[0]||document.documentElement,o=document.createElement("style");return o.id=e,t.appendChild(o),o}}function A(e){return`${"opacity"===e?"opacity: 0":"display: none"} !important; z-index: -1 !important; pointer-events: none !important;`}function g(e,t,o="display"){const i=`${t} { ${A(o)} } `;return e instanceof HTMLStyleElement&&(e.innerText+=i,t.length>0)}async function f(e,t,o){const i=await e();return!i&&t>0?new Promise((i=>{setTimeout((async()=>{i(f(e,t-1,o))}),o)})):Promise.resolve(i)}function k(e){if(!e)return!1;if(null!==e.offsetParent)return!0;{const t=window.getComputedStyle(e);if("fixed"===t.position&&"none"!==t.display)return!0}return!1}function b(e){const t={enabled:!0,autoAction:"optOut",disabledCmps:[],enablePrehide:!0,enableCosmeticRules:!0,detectRetries:20,isMainWorld:!1,prehideTimeout:2e3,enableFilterList:!1,logs:{lifecycle:!1,rulesteps:!1,evals:!1,errors:!0,messages:!1}},o=(i=t,globalThis.structuredClone?structuredClone(i):JSON.parse(JSON.stringify(i)));var i;for(const i of Object.keys(t))void 0!==e[i]&&(o[i]=e[i]);return o}var y="#truste-show-consent",w="#truste-consent-track",v=[class extends d{constructor(e){super(e),this.name="TrustArc-top",this.prehideSelectors=[".trustarc-banner-container",`.truste_popframe,.truste_overlay,.truste_box_overlay,${w}`],this.runContext={main:!0,frame:!1},this._shortcutButton=null,this._optInDone=!1}get hasSelfTest(){return!0}get isIntermediate(){return!this._optInDone&&!this._shortcutButton}get isCosmetic(){return!1}async detectCmp(){const e=this.elementExists(`${y},${w}`);return e&&(this._shortcutButton=document.querySelector("#truste-consent-required")),e}async detectPopup(){return this.elementVisible(`#truste-consent-content,#trustarc-banner-overlay,${w}`,"any")}openFrame(){this.click(y)}async optOut(){return this._shortcutButton?(this._shortcutButton.click(),!0):(g(m(),`.truste_popframe, .truste_overlay, .truste_box_overlay, ${w}`),this.click(y),setTimeout((()=>{m().remove()}),1e4),!0)}async optIn(){return this._optInDone=!0,this.click("#truste-consent-button")}async openCmp(){return!0}async test(){return await this.wait(500),await this.mainWorldEval("EVAL_TRUSTARC_TOP")}},class extends d{constructor(){super(...arguments),this.name="TrustArc-frame",this.runContext={main:!1,frame:!0,urlPattern:"^https://consent-pref\\.trustarc\\.com/\\?"}}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return!0}async detectPopup(){return this.elementVisible("#defaultpreferencemanager","any")&&this.elementVisible(".mainContent","any")}async navigateToSettings(){return await f((async()=>this.elementExists(".shp")||this.elementVisible(".advance","any")||this.elementExists(".switch span:first-child")),10,500),this.elementExists(".shp")&&this.click(".shp"),await this.waitForElement(".prefPanel",5e3),this.elementVisible(".advance","any")&&this.click(".advance"),await f((()=>this.elementVisible(".switch span:first-child","any")),5,1e3)}async optOut(){if(await this.mainWorldEval("EVAL_TRUSTARC_FRAME_TEST"))return!0;let e=3e3;return await this.mainWorldEval("EVAL_TRUSTARC_FRAME_GTM")&&(e=1500),await f((()=>"complete"===document.readyState),20,100),await this.waitForElement(".mainContent[aria-hidden=false]",e),!!this.click(".rejectAll")||(this.elementExists(".prefPanel")&&await this.waitForElement('.prefPanel[style="visibility: visible;"]',e),this.click("#catDetails0")?(this.click(".submit"),this.waitForThenClick("#gwt-debug-close_id",e),!0):this.click(".required")?(this.waitForThenClick("#gwt-debug-close_id",e),!0):(await this.navigateToSettings(),this.click(".switch span:nth-child(1):not(.active)",!0),this.click(".submit"),this.waitForThenClick("#gwt-debug-close_id",10*e),!0))}async optIn(){return this.click(".call")||(await this.navigateToSettings(),this.click(".switch span:nth-child(2)",!0),this.click(".submit"),this.waitForElement("#gwt-debug-close_id",3e5).then((()=>{this.click("#gwt-debug-close_id")}))),!0}async test(){return await this.wait(500),await this.mainWorldEval("EVAL_TRUSTARC_FRAME_TEST")}},class extends d{constructor(){super(...arguments),this.name="Cybotcookiebot",this.prehideSelectors=["#CybotCookiebotDialog,#CybotCookiebotDialogBodyUnderlay,#dtcookie-container,#cookiebanner,#cb-cookieoverlay,.modal--cookie-banner,#cookiebanner_outer,#CookieBanner"]}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return await this.mainWorldEval("EVAL_COOKIEBOT_1")}async detectPopup(){return this.mainWorldEval("EVAL_COOKIEBOT_2")}async optOut(){await this.wait(500);let e=await this.mainWorldEval("EVAL_COOKIEBOT_3");return await this.wait(500),e=e&&await this.mainWorldEval("EVAL_COOKIEBOT_4"),e}async optIn(){return this.elementExists("#dtcookie-container")?this.click(".h-dtcookie-accept"):(this.click(".CybotCookiebotDialogBodyLevelButton:not(:checked):enabled",!0),this.click("#CybotCookiebotDialogBodyLevelButtonAccept"),this.click("#CybotCookiebotDialogBodyButtonAccept"),!0)}async test(){return await this.wait(500),await this.mainWorldEval("EVAL_COOKIEBOT_5")}},class extends d{constructor(){super(...arguments),this.name="Sourcepoint-frame",this.prehideSelectors=["div[id^='sp_message_container_'],.message-overlay","#sp_privacy_manager_container"],this.ccpaNotice=!1,this.ccpaPopup=!1,this.runContext={main:!0,frame:!0}}get hasSelfTest(){return!1}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){const e=new URL(location.href);return e.searchParams.has("message_id")&&"ccpa-notice.sp-prod.net"===e.hostname?(this.ccpaNotice=!0,!0):"ccpa-pm.sp-prod.net"===e.hostname?(this.ccpaPopup=!0,!0):("/index.html"===e.pathname||"/privacy-manager/index.html"===e.pathname||"/ccpa_pm/index.html"===e.pathname)&&(e.searchParams.has("message_id")||e.searchParams.has("requestUUID")||e.searchParams.has("consentUUID"))}async detectPopup(){return!!this.ccpaNotice||(this.ccpaPopup?await this.waitForElement(".priv-save-btn",2e3):(await this.waitForElement(".sp_choice_type_11,.sp_choice_type_12,.sp_choice_type_13,.sp_choice_type_ACCEPT_ALL,.sp_choice_type_SAVE_AND_EXIT",2e3),!this.elementExists(".sp_choice_type_9")))}async optIn(){return await this.waitForElement(".sp_choice_type_11,.sp_choice_type_ACCEPT_ALL",2e3),!!this.click(".sp_choice_type_11")||!!this.click(".sp_choice_type_ACCEPT_ALL")}isManagerOpen(){return"/privacy-manager/index.html"===location.pathname||"/ccpa_pm/index.html"===location.pathname}async optOut(){const e=this.autoconsent.config.logs;if(this.ccpaPopup){const e=document.querySelectorAll(".priv-purpose-container .sp-switch-arrow-block a.neutral.on .right");for(const t of e)t.click();const t=document.querySelectorAll(".priv-purpose-container .sp-switch-arrow-block a.switch-bg.on");for(const e of t)e.click();return this.click(".priv-save-btn")}if(!this.isManagerOpen()){if(!await this.waitForElement(".sp_choice_type_12,.sp_choice_type_13"))return!1;if(!this.elementExists(".sp_choice_type_12"))return this.click(".sp_choice_type_13");this.click(".sp_choice_type_12"),await f((()=>this.isManagerOpen()),200,100)}await this.waitForElement(".type-modal",2e4),this.waitForThenClick(".ccpa-stack .pm-switch[aria-checked=true] .slider",500,!0);try{const e=".sp_choice_type_REJECT_ALL",t=".reject-toggle",o=await Promise.race([this.waitForElement(e,2e3).then((e=>e?0:-1)),this.waitForElement(t,2e3).then((e=>e?1:-1)),this.waitForElement(".pm-features",2e3).then((e=>e?2:-1))]);if(0===o)return await this.waitForVisible(e),this.click(e);1===o?this.click(t):2===o&&(await this.waitForElement(".pm-features",1e4),this.click(".checked > span",!0),this.click(".chevron"))}catch(t){e.errors&&console.warn(t)}return this.click(".sp_choice_type_SAVE_AND_EXIT")}},class extends d{constructor(){super(...arguments),this.name="consentmanager.net",this.prehideSelectors=["#cmpbox,#cmpbox2"],this.apiAvailable=!1}get hasSelfTest(){return this.apiAvailable}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.apiAvailable=await this.mainWorldEval("EVAL_CONSENTMANAGER_1"),!!this.apiAvailable||this.elementExists("#cmpbox")}async detectPopup(){return this.apiAvailable?(await this.wait(500),await this.mainWorldEval("EVAL_CONSENTMANAGER_2")):this.elementVisible("#cmpbox .cmpmore","any")}async optOut(){return await this.wait(500),this.apiAvailable?await this.mainWorldEval("EVAL_CONSENTMANAGER_3"):!!this.click(".cmpboxbtnno")||(this.elementExists(".cmpwelcomeprpsbtn")?(this.click(".cmpwelcomeprpsbtn > a[aria-checked=true]",!0),this.click(".cmpboxbtnsave"),!0):(this.click(".cmpboxbtncustom"),await this.waitForElement(".cmptblbox",2e3),this.click(".cmptdchoice > a[aria-checked=true]",!0),this.click(".cmpboxbtnyescustomchoices"),this.hide("#cmpwrapper,#cmpbox","display"),!0))}async optIn(){return this.apiAvailable?await this.mainWorldEval("EVAL_CONSENTMANAGER_4"):this.click(".cmpboxbtnyes")}async test(){if(this.apiAvailable)return await this.mainWorldEval("EVAL_CONSENTMANAGER_5")}},class extends d{constructor(){super(...arguments),this.name="Evidon"}get hasSelfTest(){return!1}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists("#_evidon_banner")}async detectPopup(){return this.elementVisible("#_evidon_banner","any")}async optOut(){return this.click("#_evidon-decline-button")||(g(m(),"#evidon-prefdiag-overlay,#evidon-prefdiag-background,#_evidon-background"),await this.waitForThenClick("#_evidon-option-button"),await this.waitForElement("#evidon-prefdiag-overlay",5e3),await this.wait(500),await this.waitForThenClick("#evidon-prefdiag-decline")),!0}async optIn(){return this.click("#_evidon-accept-button")}},class extends d{constructor(){super(...arguments),this.name="Onetrust",this.prehideSelectors=["#onetrust-banner-sdk,#onetrust-consent-sdk,.onetrust-pc-dark-filter,.js-consent-banner"],this.runContext={urlPattern:"^(?!.*https://www\\.nba\\.com/)"}}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists("#onetrust-banner-sdk,#onetrust-pc-sdk")}async detectPopup(){return this.elementVisible("#onetrust-banner-sdk,#onetrust-pc-sdk","any")}async optOut(){return this.elementVisible("#onetrust-reject-all-handler,.ot-pc-refuse-all-handler,.js-reject-cookies","any")?this.click("#onetrust-reject-all-handler,.ot-pc-refuse-all-handler,.js-reject-cookies"):(this.elementExists("#onetrust-pc-btn-handler")?this.click("#onetrust-pc-btn-handler"):this.click(".ot-sdk-show-settings,button.js-cookie-settings"),await this.waitForElement("#onetrust-consent-sdk",2e3),await this.wait(1e3),this.click("#onetrust-consent-sdk input.category-switch-handler:checked,.js-editor-toggle-state:checked",!0),await this.wait(1e3),await this.waitForElement(".save-preference-btn-handler,.js-consent-save",2e3),this.click(".save-preference-btn-handler,.js-consent-save"),await this.waitForVisible("#onetrust-banner-sdk",5e3,"none"),!0)}async optIn(){return this.click("#onetrust-accept-btn-handler,#accept-recommended-btn-handler,.js-accept-cookies")}async test(){return await f((()=>this.mainWorldEval("EVAL_ONETRUST_1")),10,500)}},class extends d{constructor(){super(...arguments),this.name="Klaro",this.prehideSelectors=[".klaro"],this.settingsOpen=!1}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists(".klaro > .cookie-modal")?(this.settingsOpen=!0,!0):this.elementExists(".klaro > .cookie-notice")}async detectPopup(){return this.elementVisible(".klaro > .cookie-notice,.klaro > .cookie-modal","any")}async optOut(){return!!await this.mainWorldEval("EVAL_KLARO_TRY_API_OPT_OUT")||(!!this.click(".klaro .cn-decline")||(await this.mainWorldEval("EVAL_KLARO_OPEN_POPUP"),!!this.click(".klaro .cn-decline")||(this.click(".cm-purpose:not(.cm-toggle-all) > input:not(.half-checked,.required,.only-required),.cm-purpose:not(.cm-toggle-all) > div > input:not(.half-checked,.required,.only-required)",!0),this.click(".cm-btn-accept,.cm-button"))))}async optIn(){return!!this.click(".klaro .cm-btn-accept-all")||(this.settingsOpen?(this.click(".cm-purpose:not(.cm-toggle-all) > input.half-checked",!0),this.click(".cm-btn-accept")):this.click(".klaro .cookie-notice .cm-btn-success"))}async test(){return await this.mainWorldEval("EVAL_KLARO_1")}},class extends d{constructor(){super(...arguments),this.name="Uniconsent"}get prehideSelectors(){return[".unic",".modal:has(.unic)"]}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists(".unic .unic-box,.unic .unic-bar,.unic .unic-modal")}async detectPopup(){return this.elementVisible(".unic .unic-box,.unic .unic-bar,.unic .unic-modal","any")}async optOut(){if(await this.waitForElement(".unic button",1e3),document.querySelectorAll(".unic button").forEach((e=>{const t=e.textContent;(t.includes("Manage Options")||t.includes("Optionen verwalten"))&&e.click()})),await this.waitForElement(".unic input[type=checkbox]",1e3)){await this.waitForElement(".unic button",1e3),document.querySelectorAll(".unic input[type=checkbox]").forEach((e=>{e.checked&&e.click()}));for(const e of document.querySelectorAll(".unic button")){const t=e.textContent;for(const o of["Confirm Choices","Save Choices","Auswahl speichern"])if(t.includes(o))return e.click(),await this.wait(500),!0}}return!1}async optIn(){return this.waitForThenClick(".unic #unic-agree")}async test(){await this.wait(1e3);return!this.elementExists(".unic .unic-box,.unic .unic-bar")}},class extends d{constructor(){super(...arguments),this.prehideSelectors=[".cmp-root"],this.name="Conversant"}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists(".cmp-root .cmp-receptacle")}async detectPopup(){return this.elementVisible(".cmp-root .cmp-receptacle","any")}async optOut(){if(!await this.waitForThenClick(".cmp-main-button:not(.cmp-main-button--primary)"))return!1;if(!await this.waitForElement(".cmp-view-tab-tabs"))return!1;await this.waitForThenClick(".cmp-view-tab-tabs > :first-child"),await this.waitForThenClick(".cmp-view-tab-tabs > .cmp-view-tab--active:first-child");for(const e of Array.from(document.querySelectorAll(".cmp-accordion-item"))){e.querySelector(".cmp-accordion-item-title").click(),await f((()=>!!e.querySelector(".cmp-accordion-item-content.cmp-active")),10,50);const t=e.querySelector(".cmp-accordion-item-content.cmp-active");t.querySelectorAll(".cmp-toggle-actions .cmp-toggle-deny:not(.cmp-toggle-deny--active)").forEach((e=>e.click())),t.querySelectorAll(".cmp-toggle-actions .cmp-toggle-checkbox:not(.cmp-toggle-checkbox--active)").forEach((e=>e.click()))}return await this.click(".cmp-main-button:not(.cmp-main-button--primary)"),!0}async optIn(){return this.waitForThenClick(".cmp-main-button.cmp-main-button--primary")}async test(){return document.cookie.includes("cmp-data=0")}},class extends d{constructor(){super(...arguments),this.name="tiktok.com",this.runContext={urlPattern:"tiktok"}}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}getShadowRoot(){const e=document.querySelector("tiktok-cookie-banner");return e?e.shadowRoot:null}async detectCmp(){return this.elementExists("tiktok-cookie-banner")}async detectPopup(){return k(this.getShadowRoot().querySelector(".tiktok-cookie-banner"))}async optOut(){const e=this.autoconsent.config.logs,t=this.getShadowRoot().querySelector(".button-wrapper button:first-child");return t?(e.rulesteps&&console.log("[clicking]",t),t.click(),!0):(e.errors&&console.log("no decline button found"),!1)}async optIn(){const e=this.autoconsent.config.logs,t=this.getShadowRoot().querySelector(".button-wrapper button:last-child");return t?(e.rulesteps&&console.log("[clicking]",t),t.click(),!0):(e.errors&&console.log("no accept button found"),!1)}async test(){const e=document.cookie.match(/cookie-consent=([^;]+)/);if(!e)return!1;const t=JSON.parse(decodeURIComponent(e[1]));return Object.values(t).every((e=>"boolean"!=typeof e||!1===e))}},class extends d{constructor(){super(...arguments),this.name="airbnb",this.runContext={urlPattern:"^https://(www\\.)?airbnb\\.[^/]+/"},this.prehideSelectors=["div[data-testid=main-cookies-banner-container]",'div:has(> div:first-child):has(> div:last-child):has(> section [data-testid="strictly-necessary-cookies"])']}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists("div[data-testid=main-cookies-banner-container]")}async detectPopup(){return this.elementVisible("div[data-testid=main-cookies-banner-container","any")}async optOut(){let e;for(await this.waitForThenClick("div[data-testid=main-cookies-banner-container] button._snbhip0");e=document.querySelector("[data-testid=modal-container] button[aria-checked=true]:not([disabled])");)e.click();return this.waitForThenClick("button[data-testid=save-btn]")}async optIn(){return this.waitForThenClick("div[data-testid=main-cookies-banner-container] button._148dgdpk")}async test(){return await f((()=>!!document.cookie.match("OptanonAlertBoxClosed")),20,200)}},class extends d{constructor(){super(...arguments),this.name="tumblr-com",this.runContext={urlPattern:"^https://(www\\.)?tumblr\\.com/"}}get hasSelfTest(){return!1}get isIntermediate(){return!1}get isCosmetic(){return!1}get prehideSelectors(){return["#cmp-app-container"]}async detectCmp(){return this.elementExists("#cmp-app-container")}async detectPopup(){return this.elementVisible("#cmp-app-container","any")}async optOut(){let e=document.querySelector("#cmp-app-container iframe"),t=e.contentDocument?.querySelector(".cmp-components-button.is-secondary");return!!t&&(t.click(),await f((()=>{const e=document.querySelector("#cmp-app-container iframe");return!!e.contentDocument?.querySelector(".cmp__dialog input")}),5,500),e=document.querySelector("#cmp-app-container iframe"),t=e.contentDocument?.querySelector(".cmp-components-button.is-secondary"),!!t&&(t.click(),!0))}async optIn(){const e=document.querySelector("#cmp-app-container iframe").contentDocument.querySelector(".cmp-components-button.is-primary");return!!e&&(e.click(),!0)}},class extends d{constructor(){super(...arguments),this.name="Admiral"}get hasSelfTest(){return!1}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists("div > div[class*=Card] > div[class*=Frame] > div[class*=Pills] > button[class*=Pills__StyledPill]")}async detectPopup(){return this.elementVisible("div > div[class*=Card] > div[class*=Frame] > div[class*=Pills] > button[class*=Pills__StyledPill]","any")}async optOut(){const e="xpath///button[contains(., 'Afvis alle') or contains(., 'Reject all') or contains(., 'Odbaci sve') or contains(., 'Rechazar todo') or contains(., 'Atmesti visus') or contains(., 'Odmítnout vše') or contains(., 'Απόρριψη όλων') or contains(., 'Rejeitar tudo') or contains(., 'Tümünü reddet') or contains(., 'Отклонить все') or contains(., 'Noraidīt visu') or contains(., 'Avvisa alla') or contains(., 'Odrzuć wszystkie') or contains(., 'Alles afwijzen') or contains(., 'Отхвърляне на всички') or contains(., 'Rifiuta tutto') or contains(., 'Zavrni vse') or contains(., 'Az összes elutasítása') or contains(., 'Respingeți tot') or contains(., 'Alles ablehnen') or contains(., 'Tout rejeter') or contains(., 'Odmietnuť všetko') or contains(., 'Lükka kõik tagasi') or contains(., 'Hylkää kaikki')]";if(await this.waitForElement(e,500))return this.click(e);const t="xpath///button[contains(., 'Spara & avsluta') or contains(., 'Save & exit') or contains(., 'Uložit a ukončit') or contains(., 'Enregistrer et quitter') or contains(., 'Speichern & Verlassen') or contains(., 'Tallenna ja poistu') or contains(., 'Išsaugoti ir išeiti') or contains(., 'Opslaan & afsluiten') or contains(., 'Guardar y salir') or contains(., 'Shrani in zapri') or contains(., 'Uložiť a ukončiť') or contains(., 'Kaydet ve çıkış yap') or contains(., 'Сохранить и выйти') or contains(., 'Salvesta ja välju') or contains(., 'Salva ed esci') or contains(., 'Gem & afslut') or contains(., 'Αποθήκευση και έξοδος') or contains(., 'Saglabāt un iziet') or contains(., 'Mentés és kilépés') or contains(., 'Guardar e sair') or contains(., 'Zapisz & zakończ') or contains(., 'Salvare și ieșire') or contains(., 'Spremi i izađi') or contains(., 'Запазване и изход')]";if(await this.waitForThenClick("xpath///button[contains(., 'Zwecke') or contains(., 'Σκοποί') or contains(., 'Purposes') or contains(., 'Цели') or contains(., 'Eesmärgid') or contains(., 'Tikslai') or contains(., 'Svrhe') or contains(., 'Cele') or contains(., 'Účely') or contains(., 'Finalidades') or contains(., 'Mērķi') or contains(., 'Scopuri') or contains(., 'Fines') or contains(., 'Ändamål') or contains(., 'Finalités') or contains(., 'Doeleinden') or contains(., 'Tarkoitukset') or contains(., 'Scopi') or contains(., 'Amaçlar') or contains(., 'Nameni') or contains(., 'Célok') or contains(., 'Formål')]")&&await this.waitForVisible(t)){return this.elementSelector(t)[0].parentElement.parentElement.querySelectorAll("input[type=checkbox]:checked").forEach((e=>e.click())),this.click(t)}return!1}async optIn(){return this.click("xpath///button[contains(., 'Sprejmi vse') or contains(., 'Prihvati sve') or contains(., 'Godkänn alla') or contains(., 'Prijať všetko') or contains(., 'Принять все') or contains(., 'Aceptar todo') or contains(., 'Αποδοχή όλων') or contains(., 'Zaakceptuj wszystkie') or contains(., 'Accetta tutto') or contains(., 'Priimti visus') or contains(., 'Pieņemt visu') or contains(., 'Tümünü kabul et') or contains(., 'Az összes elfogadása') or contains(., 'Accept all') or contains(., 'Приемане на всички') or contains(., 'Accepter alle') or contains(., 'Hyväksy kaikki') or contains(., 'Tout accepter') or contains(., 'Alles accepteren') or contains(., 'Aktsepteeri kõik') or contains(., 'Přijmout vše') or contains(., 'Alles akzeptieren') or contains(., 'Aceitar tudo') or contains(., 'Acceptați tot')]")}}],_=class{constructor(e){this.autoconsentInstance=e}click(e,t=!1){const o=this.elementSelector(e);return this.autoconsentInstance.config.logs.rulesteps&&console.log("[click]",e,t,o),o.length>0&&(t?o.forEach((e=>e.click())):o[0].click()),o.length>0}elementExists(e){return this.elementSelector(e).length>0}elementVisible(e,t){const o=this.elementSelector(e),i=new Array(o.length);return o.forEach(((e,t)=>{i[t]=k(e)})),"none"===t?i.every((e=>!e)):0!==i.length&&("any"===t?i.some((e=>e)):i.every((e=>e)))}waitForElement(e,t=1e4){const o=Math.ceil(t/200);return this.autoconsentInstance.config.logs.rulesteps&&console.log("[waitForElement]",e),f((()=>this.elementSelector(e).length>0),o,200)}waitForVisible(e,t=1e4,o="any"){const i=Math.ceil(t/200);return this.autoconsentInstance.config.logs.rulesteps&&console.log("[waitForVisible]",e),f((()=>this.elementVisible(e,o)),i,200)}async waitForThenClick(e,t=1e4,o=!1){return await this.waitForElement(e,t),this.click(e,o)}wait(e){return this.autoconsentInstance.config.logs.rulesteps&&this.autoconsentInstance.config.logs.waits&&console.log("[wait]",e),new Promise((t=>{setTimeout((()=>{t(!0)}),e)}))}hide(e,t){this.autoconsentInstance.config.logs.rulesteps&&console.log("[hide]",e);return g(m(),e,t)}prehide(e){const t=m("autoconsent-prehide");return this.autoconsentInstance.config.logs.lifecycle&&console.log("[prehide]",t,location.href),g(t,e,"opacity")}undoPrehide(){const e=m("autoconsent-prehide");return this.autoconsentInstance.config.logs.lifecycle&&console.log("[undoprehide]",e,location.href),e&&e.remove(),!!e}async createOrUpdateStyleSheet(e,t){return t||(t=new CSSStyleSheet),t=await t.replace(e)}removeStyleSheet(e){return!!e&&(e.replace(""),!0)}querySingleReplySelector(e,t=document){if(e.startsWith("aria/"))return[];if(e.startsWith("xpath/")){const o=e.slice(6),i=document.evaluate(o,t,null,XPathResult.ANY_TYPE,null);let n=null;const s=[];for(;n=i.iterateNext();)s.push(n);return s}return e.startsWith("text/")||e.startsWith("pierce/")?[]:t.shadowRoot?Array.from(t.shadowRoot.querySelectorAll(e)):Array.from(t.querySelectorAll(e))}querySelectorChain(e){let t,o=document;for(const i of e){if(t=this.querySingleReplySelector(i,o),0===t.length)return[];o=t[0]}return t}elementSelector(e){return"string"==typeof e?this.querySingleReplySelector(e):this.querySelectorChain(e)}};function C(){return{chars:new Map,code:void 0}}var x=new Uint8Array(0),S=class{constructor(e,t=3e4){this.trie=function(e){const t=C();for(let o=0;o figure.wp-block-image:has(> img[class^="wp-image-"][src^="https://www.sinhasannews.com/"][width="','"]:not([style^="width: 1px; height: 1px; position: absolute; left: -10000px; top: -"])',"acs, document.createElement, %2Fl%5C.parentNode%5C.insertBefore%5C(s%2F","%2Fvisit%2F%22%5D%5Btitle%5E%3D%22https%3A%2F%2F%22%5D, %5Btitle%5D",", OptanonConsent, groups%3DC0001%253A1%252CC0002%253A0%252CC000","rmnt, script, %2Fh%3DdecodeURIComponent%7CpopundersPerIP%2F",'.project-description [href^="/linkout?remoteUrl="][href*="',':not([style^="position: absolute; left: -5000px"])',"href-sanitizer, a%5Bhref%5E%3D%22https%3A%2F%2F","ra, oncontextmenu%7Condragstart%7Conselectstart",", OptanonAlertBoxClosed, %24currentDate%24","acs, document.querySelectorAll, popMagic","acs, addEventListener, google_ad_client","aost, String.prototype.charCodeAt, ai_","aopr, app_vars.force_disable_adblock","acs, document.addEventListener, ","taboola-below-article-thumbnails","acs, document.getElementById, ","no-fetch-if, googlesyndication","aopr, document.dispatchEvent","no-xhr-if, googlesyndication",", document.createElement, ","acs, String.fromCharCode, ","%2522%253Afalse%252C%2522",", document.oncontextmenu","%2522%253Atrue%252C%2522","aeld, DOMContentLoaded, ","nosiif, visibility, 1000","set-local-storage-item, ","%2522%3Afalse%252C%2522","trusted-click-element, ","set, blurred, false","acs, eval, replace","decodeURIComponent",'[target="_blank"]',"%22%3Afalse%2C%22","^script:has-text(",'[href^="https://','[href^="http://','[href="https://','[src^="https://','[data-testid="',"modal-backdrop","rmnt, script, ","BlockDetected","trusted-set-",".prototype.","contextmenu","no-fetch-if","otification",":has-text(","background",'[class*="','[class^="',"body,html","container","Container","decodeURI","div[class",'div[id^="',"div[style","document.","no-xhr-if","placehold",'[href*="',"#wpsafe-","AAAAAAAA","Detector","disclaim","nano-sib","nextFunc","noopFunc","nostif, ","nowebrtc",'.com/"]',"300x250","article","consent","Consent","content","display","keydown","message","Message","overlay","privacy","sidebar","sponsor","wrapper","-child","[data-","accept","Accept","aopr, ","banner","bottom","cookie","Cookie","google","nosiif","notice","nowoif","policy","Policy","script","widget",":has(",":not(","block","Block","click","deskt","disab","fixed","frame","modal","popup","video",".com","2%3A","aeld","body","butt","foot","gdpr","html","icky","ight","show","tion","true"," > ","%3D","%7C","age","box","div","ent","out","rap","set","__",", ",'"]',"%2","%5",'="',"00","ac","ad","Ad","al","an","ar","at","e-","ed","en","er","he","id","in","la","le","lo","od","ol","om","on","op","or","re","s_","s-","se","st","t-","te","ti","un","_","-",";",":",".","(",")","[","]","*","/","#","^","0","1","2","3","4","5","6","7","8","9","b","B","c","C","d","D","e","E","f","F","g","G","h","H","I","j","J","k","l","L","m","M","n","N","O","p","P","q","Q","R","s","S","t","T","u","U","v","V","w","W","x","y","Y","z"],T=["sandbox allow-forms allow-same-origin allow-scripts allow-modals allow-orientation-lock allow-pointer-lock allow-presentation allow-top-navigation","script-src 'self' 'unsafe-inline' 'unsafe-eval' "," *.google.com *.gstatic.com *.googleapis.com",".com *.google.com *.googletagmanager.com *.","script-src 'self' '*' 'unsafe-inline'","default-src 'unsafe-inline' 'self'","script-src 'self' 'unsafe-eval' "," *.google.com *.gstatic.com *.","t-src 'self' 'unsafe-inline' ","script-src * 'unsafe-inline'",".com *.googleapis.com *."," *.googletagmanager.com",".com *.bootstrapcdn.com","default-src 'self' *.","frame-src 'self' *"," *.cloudflare.com","child-src 'none';","worker-src 'none'","'unsafe-inline'"," data: blob:","*.googleapis","connect-src ","unsafe-eval'","child-src *"," *.gstatic","script-src","style-src ","frame-src","facebook","https://"," 'self'"," allow-",".com *.",".net *.","addthis","captcha","gstatic","youtube","defaul","disqus","google","https:","jquery","data:","http:","media","scrip","-src",".com",".net","n.cc"," *.","age","box","str","vic","yti"," '"," *","*.","al","am","an","as","cd","el","es","il","im","in","or","pi","st","ur","wi","wp"," ","-",";",":",".","'","*","/","3","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y"],O=["/homad-global-configs.schneevonmorgen.com/global_config","/videojs-vast-vpaid@2.0.2/bin/videojs_5.vast.vpaid.min","/etc.clientlibs/logitech-common/clientlibs/onetrust.","/pagead/managed/js/adsense/*/show_ads_impl","/pagead/managed/js/gpt/*/pubads_impl","/wrappermessagingwithoutdetection","/pagead/js/adsbygoogle.js","a-z]{8,15}\\.(?:com|net)\\/","/js/sdkloader/ima3.js","/js/sdkloader/ima3_d","/videojs-contrib-ads","/wp-content/plugins/","/wp-content/uploads/","/wp-content/themes/","/detroitchicago/","*/satellitelib-","/appmeasurement","/413gkwmt/init","/cdn-cgi/trace","/^https?:\\/\\/","[a-zA-Z0-9]{","/^https:\\/\\/","notification","\\/[a-z0-9]{","fingerprint","impression","[0-9a-z]{","/plugins/","affiliate","analytics","telemetry","(.+?\\.)?","/assets/","/images/","/pagead/","pageview","template","tracking","/public","300x250","ampaign","captcha","collect","consent","content","counter","default","metrics","privacy","[a-z]{","/embed","728x90","banner","bundle","client","cookie","detect","dn-cgi","google","iframe","module","prebid","script","source","widget",".aspx",".cgi?",".com/",".html","/api/","/beac","/img/","/java","/stat","0x600","block","click","count","event","manag","media","pixel","popup","tegra","theme","track","type=","video","visit",".css",".gif",".jpg",".min",".php",".png","/jqu","/js/","/lib","/log","/web","/wp-","468x","data","gdpr","gi-b","http","ight","mail","play","plug","publ","show","stat","uild","view",".js","/ad","=*&","age","com","ext","jax","key","log","new","sdk","tag","web","ync",":/","*/","*^","/_","/?","/*","/d","/f","/g","/h","/l","/n","/r","/u","/w","ac","ad","al","am","an","ap","ar","as","at","bo","ce","ch","co","de","e/","ec","ed","el","en","er","et","fi","g/","ic","id","im","in","is","it","js","la","le","li","lo","ma","mo","mp","ol","om","on","op","or","ot","re","ro","s_","s-","s?","s/","sp","ss","st","t/","ti","tm","tr","ub","un","ur","us","ut","ve","_","-",",","?",".","}","*","/","\\","&","^","=","0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"],P=["securepubads.g.doubleclick",".actonservice.com","googlesyndication","imasdk.googleapis",".cloudfront.net","googletagmanag","-1.xx.fbcdn","analytics.","marketing.","tracking.","metrics.","images.",".co.uk","a8clk.","stats.","a8cv.","click","media","track",".com",".net",".xyz","www.",".io",".jp","a8.","app","cdn","new","web",".b",".c",".d",".f",".h",".k",".m",".n",".p",".s",".t","10","24","a-","a1","a2","a4","ab","ac","ad","af","ag","ah","ai","ak","al","am","an","ap","ar","as","at","au","av","aw","ax","ay","az","be","bi","bl","bo","br","bu","ca","ce","ch","ci","ck","cl","cr","ct","cu","de","di","dn","do","dr","ds","du","dy","e-","eb","ec","ed","ef","eg","el","em","en","ep","er","es","et","eu","ev","ew","ex","ey","fe","ff","fi","fo","fr","ft","ge","gh","gi","gn","go","gr","gu","he","ho","ib","ic","id","ie","if","ig","ik","il","im","in","ip","ir","is","it","iv","ix","iz","jo","ks","la","le","li","ll","lo","lu","ly","ma","me","mo","mp","my","no","ok","ol","om","on","oo","op","or","ot","ou","ph","pl","po","pr","pu","qu","re","ri","ro","ru","s-","sc","se","sh","si","sk","sn","so","sp","ss","st","su","sw","sy","t-","ta","te","th","ti","tn","to","tr","ts","tu","ty","ub","ud","ul","um","un","up","ur","us","ut","ve","vi","vo","wa","we","wh","wn","-",".","0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"],R=["google-analytics.com/analytics.js","googlesyndication_adsbygoogle.js","googletagmanager.com/gtm.js","googletagservices_gpt.js","googletagmanager_gtm.js","fuckadblock.js-3.2.0","amazon_apstag.js","google-analytics","fingerprint2.js","noop-1s.mp4:10","google-ima.js","noop-0.1s.mp3","prebid-ads.js","nobab2.js:10","noopmp3-0.1s","noop-1s.mp4","hd-main.js","noopmp4-1s","32x32.png","noop.html","noopframe","noop.txt","nooptext","1x1.gif","2x2.png","noop.js","noopjs",".com/",".js:5","noop",":10",".js","ads","bea","_a",":5",".0","ar","ch","ic","in","le","ma","on","re","st","_","-",":",".","/","0","1","2","3","4","5","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","r","s","t","u","v","w","x","y","z"],z=[",redirect=google-ima","/js/sdkloader/ima3.j","/wp-content/plugins/",",redirect-rule=noop",".actonservice.com^",".com^$third-party","googlesyndication","imasdk.googleapis",".cloudfront.net^",",redirect-rule=","$script,domain=",",3p,denyallow=",",redirect=noop","xmlhttprequest","^$third-party","||smetrics.","third-party","marketing.","$document","analytics",",domain=","/assets/","metrics.","subdocum","tracking","$script",".co.uk","$ghide","a8clk.","cookie","google","script",".com^",".xyz^","$doma","a8cv.","click","image","media","track",".com",".fr^",".gif",".jp^",".net","/js/","$doc","$xhr","stat","www.",",1p",",3p",".io",".jp",".js","app","cdn","ent","new","web",".b",".c",".d",".f",".h",".m",".n",".p",".s",".t","@@","/*","/p","||","ab","ac","ad","af","ag","ai","ak","al","am","an","ap","ar","as","at","au","av","aw","ax","ay","az","be","bi","bo","br","ca","ce","ch","ck","cl","ct","cu","de","di","do","e-","e^","ec","ed","el","em","en","ep","er","es","et","ev","ew","ex","fe","ff","fi","fo","fr","g^","ge","gi","go","gr","he","hi","ho","hp","ht","ic","id","ig","il","im","in","ip","ir","is","it","ix","js","ke","le","li","lo","lu","ly","me","mo","mp","ne","no","od","ok","ol","om","on","op","or","ot","ow","pl","po","pr","qu","re","ri","ro","ru","s-","s/","sc","se","sh","si","so","sp","ss","st","su","te","th","ti","to","tr","ts","ty","ub","ud","ul","um","un","up","ur","us","ut","ve","vi","_","-",",","?",".","*","/","^","=","|","~","$","0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"],L=["-webkit-touch-callo",", 1year, , domain, ",", googlesyndication",", SOCS, CAISNQgQEit",":style(overflow: au","##^script:has-text(","9udGVuZHVpc2VydmVyX","GgJmaSADGgYIgOu0sgY","ib3FfaWRlbnRpdHlmcm","position: initial !","set-local-storage-i","set, blurred, false","user-select: text !","zIwMjQwNTE0LjA2X3Aw",'[href^="https://',"rmnt, script, ","ut: default !"," !important)","trusted-set-",", document.",", noopFunc)","##body,html","contextmenu","no-fetch-if","otification",".com##+js(",'="https://',"background","important;"," -webkit-",".*,xhamst","container","AAAAAAAA","nostif, ",",google",":style(","consent","message","nowoif)","privacy","-wrapp",",kayak",".co.uk","[class","##+js(","accept","aopr, ","banner","bottom","cookie","Cookie","google","notice","policy","widget",":has(","##div","block","cript","true)",".co.",".com",".de,",".fr,",".net",".nl,",".pl,",".xyz","#@#.","2%3A","gdpr","html","ight","news","text","to !","wrap","www."," > ",",xh","##.","###","%3D","%7C","ent","lay","web","__","-s",", ",",b",",c",",f",",g",",h",",m",",p",",s",",t",": ",".*",".b",".c",".m",".p",".s",'"]',"##","%2","%5",'="',"00","a-","ab","ac","ad","Ad","af","ag","ak","al","am","an","ap","ar","as","at","au","av","ay","az","bo","ch","ck","cl","ct","de","di","do","e-","ed","el","em","en","er","es","et","ex","fi","fo","he","ic","id","if","ig","il","im","in","is","it","iv","le","lo","mo","ol","om","on","op","or","ot","ov","pl","po","re","ro","s_","s-","se","sh","si","sp","st","t-","th","ti","tr","tv","ub","ul","um","un","up","ur","us","ut","vi"," ","_","-",",",":",".","(",")","[","*","/","^","0","1","2","3","4","5","6","7","8","9","a","b","B","c","C","d","D","e","E","f","F","g","h","i","j","k","l","L","m","M","n","o","p","P","q","r","s","S","t","T","u","v","w","x","y","z"],B=class{constructor(){this.cosmeticSelector=new I(F),this.networkCSP=new I(T),this.networkRedirect=new I(R),this.networkHostname=new I(P),this.networkFilter=new I(O),this.networkRaw=new I(z),this.cosmeticRaw=new I(L)}},U=(()=>{let e=0;const t=new Int32Array(256);for(let o=0;256!==o;o+=1)e=o,e=1&e?-306674912^e>>>1:e>>>1,e=1&e?-306674912^e>>>1:e>>>1,e=1&e?-306674912^e>>>1:e>>>1,e=1&e?-306674912^e>>>1:e>>>1,e=1&e?-306674912^e>>>1:e>>>1,e=1&e?-306674912^e>>>1:e>>>1,e=1&e?-306674912^e>>>1:e>>>1,e=1&e?-306674912^e>>>1:e>>>1,t[o]=e;return t})();var N=2147483647,V=36,j=1,M=26,D=38,H=700,W=72,q=128,G="-",$=/[^\0-\x7E]/,K=/[\x2E\u3002\uFF0E\uFF61]/g,Q={"invalid-input":"Invalid input","not-basic":"Illegal input >= 0x80 (not a basic code point)",overflow:"Overflow: input needs wider integers to process"},Y=V-j;function X(e){throw new RangeError(Q[e])}function Z(e,t){return e+22+75*(e<26?1:0)-((0!==t?1:0)<<5)}function J(e,t,o){let i=0;for(e=o?Math.floor(e/H):e>>1,e+=Math.floor(e/t);e>Y*M>>1;i+=V)e=Math.floor(e/Y);return Math.floor(i+(Y+1)*e/(e+D))}function ee(e){const t=[],o=e.length;let i=0,n=q,s=W,c=e.lastIndexOf(G);c<0&&(c=0);for(let o=0;o=128&&X("not-basic"),t.push(e.charCodeAt(o));for(let a=c>0?c+1:0;a=o&&X("invalid-input");const c=(r=e.charCodeAt(a++))-48<10?r-22:r-65<26?r-65:r-97<26?r-97:V;(c>=V||c>Math.floor((N-i)/t))&&X("overflow"),i+=c*t;const l=n<=s?j:n>=s+M?M:n-s;if(cMath.floor(N/p)&&X("overflow"),t*=p}const l=t.length+1;s=J(i-c,l,0===c),Math.floor(i/l)>N-n&&X("overflow"),n+=Math.floor(i/l),i%=l,t.splice(i++,0,n)}var r;return String.fromCodePoint.apply(null,t)}function te(e){const t=[],o=function(e){const t=[];let o=0;const i=e.length;for(;o=55296&&n<=56319&&o=n&&iMath.floor((N-s)/i)&&X("overflow"),s+=(e-n)*i,n=e;for(let e=0;eN&&X("overflow"),l===n){let e=s;for(let o=V;;o+=V){const i=o<=c?j:o>=c+M?M:o-c;if(e{const e=new B;return ce=()=>e,e};function re(e){return e<=127?1:5}function ae(e,t){return le(e.length,t)}function le(e,t){return(t?3:0)+e+re(e)}function pe(e){return e.length+re(e.length)}function de(e){const t=te(e).length;return t+re(t)}function ue(e){return e.byteLength+re(e.length)}var he,me=class e{static empty(t){return e.fromUint8Array(ie,t)}static fromUint8Array(t,o){return new e(t,o)}static allocate(t,o){return new e(new Uint8Array(t),o)}constructor(e,{enableCompression:t}){if(!1===se)throw new Error("Adblocker currently does not support Big-endian systems");!0===t&&this.enableCompression(),this.buffer=e,this.pos=0}enableCompression(){this.compression=ce()}checksum(){return function(e,t,o){let i=-1;const n=o-7;let s=t;for(;s>>8^U[255&(i^e[s++])],i=i>>>8^U[255&(i^e[s++])],i=i>>>8^U[255&(i^e[s++])],i=i>>>8^U[255&(i^e[s++])],i=i>>>8^U[255&(i^e[s++])],i=i>>>8^U[255&(i^e[s++])],i=i>>>8^U[255&(i^e[s++])],i=i>>>8^U[255&(i^e[s++])];for(;s>>8^U[255&(i^e[s++])];return(-1^i)>>>0}(this.buffer,0,this.pos)}dataAvailable(){return this.pos>>8,this.buffer[this.pos++]=e}getUint16(){return(this.buffer[this.pos++]<<8|this.buffer[this.pos++])>>>0}pushUint32(e){this.buffer[this.pos++]=e>>>24,this.buffer[this.pos++]=e>>>16,this.buffer[this.pos++]=e>>>8,this.buffer[this.pos++]=e}getUint32(){return(this.buffer[this.pos++]<<24>>>0)+(this.buffer[this.pos++]<<16|this.buffer[this.pos++]<<8|this.buffer[this.pos++])>>>0}pushUint32Array(e){this.pushLength(e.length);for(const t of e)this.pushUint32(t)}getUint32Array(){const e=this.getLength(),t=new Uint32Array(e);for(let o=0;othis.buffer.byteLength)throw new Error(`StaticDataView too small: ${this.buffer.byteLength}, but required ${this.pos} bytes`)}pushLength(e){e<=127?this.pushUint8(e):(this.pushUint8(128),this.pushUint32(e))}getLength(){const e=this.getUint8();return 128===e?this.getUint32():e}},Ae=class e{static deserialize(t){return new e({debug:t.getBool(),enableCompression:t.getBool(),enableHtmlFiltering:t.getBool(),enableInMemoryCache:t.getBool(),enableMutationObserver:t.getBool(),enableOptimizations:t.getBool(),enablePushInjectionsOnNavigationEvents:t.getBool(),guessRequestTypeFromUrl:t.getBool(),integrityCheck:t.getBool(),loadCSPFilters:t.getBool(),loadCosmeticFilters:t.getBool(),loadExceptionFilters:t.getBool(),loadExtendedSelectors:t.getBool(),loadGenericCosmeticsFilters:t.getBool(),loadNetworkFilters:t.getBool(),loadPreprocessors:t.getBool()})}constructor({debug:e=!1,enableCompression:t=!1,enableHtmlFiltering:o=!1,enableInMemoryCache:i=!0,enableMutationObserver:n=!0,enableOptimizations:s=!0,enablePushInjectionsOnNavigationEvents:c=!0,guessRequestTypeFromUrl:r=!1,integrityCheck:a=!0,loadCSPFilters:l=!0,loadCosmeticFilters:p=!0,loadExceptionFilters:d=!0,loadExtendedSelectors:u=!1,loadGenericCosmeticsFilters:h=!0,loadNetworkFilters:m=!0,loadPreprocessors:A=!1}={}){this.debug=e,this.enableCompression=t,this.enableHtmlFiltering=o,this.enableInMemoryCache=i,this.enableMutationObserver=n,this.enableOptimizations=s,this.enablePushInjectionsOnNavigationEvents=c,this.guessRequestTypeFromUrl=r,this.integrityCheck=a,this.loadCSPFilters=l,this.loadCosmeticFilters=p,this.loadExceptionFilters=d,this.loadExtendedSelectors=u,this.loadGenericCosmeticsFilters=h,this.loadNetworkFilters=m,this.loadPreprocessors=A}getSerializedSize(){return 16}serialize(e){e.pushBool(this.debug),e.pushBool(this.enableCompression),e.pushBool(this.enableHtmlFiltering),e.pushBool(this.enableInMemoryCache),e.pushBool(this.enableMutationObserver),e.pushBool(this.enableOptimizations),e.pushBool(this.enablePushInjectionsOnNavigationEvents),e.pushBool(this.guessRequestTypeFromUrl),e.pushBool(this.integrityCheck),e.pushBool(this.loadCSPFilters),e.pushBool(this.loadCosmeticFilters),e.pushBool(this.loadExceptionFilters),e.pushBool(this.loadExtendedSelectors),e.pushBool(this.loadGenericCosmeticsFilters),e.pushBool(this.loadNetworkFilters),e.pushBool(this.loadPreprocessors)}},ge="undefined"!=typeof window&&"function"==typeof window.queueMicrotask?e=>window.queueMicrotask(e):e=>(he||(he=Promise.resolve())).then(e).catch((e=>setTimeout((()=>{throw e}),0)));function fe(e,t,o){let i=o.get(e);void 0===i&&(i=[],o.set(e,i)),i.push(t)}function ke(e,t,o){const i=o.get(e);if(void 0!==i){const e=i.indexOf(t);-1!==e&&i.splice(e,1)}}function be(e,t,o){if(0===o.size)return!1;const i=o.get(e);return void 0!==i&&(ge((()=>{for(const e of i)e(...t)})),!0)}var ye=class{constructor(){this.onceListeners=new Map,this.onListeners=new Map}on(e,t){fe(e,t,this.onListeners)}once(e,t){fe(e,t,this.onceListeners)}unsubscribe(e,t){ke(e,t,this.onListeners),ke(e,t,this.onceListeners)}emit(e,...t){be(e,t,this.onListeners),!0===be(e,t,this.onceListeners)&&this.onceListeners.delete(e)}};function we(e,t){return function(e,t){let o=3;const i=()=>e(t).catch((e=>{if(o>0)return o-=1,new Promise(((e,t)=>{setTimeout((()=>{i().then(e).catch(t)}),500)}));throw e}));return i()}(e,t).then((e=>e.text()))}var ve="https://raw.githubusercontent.com/ghostery/adblocker/master/packages/adblocker/assets",_e=[`${ve}/easylist/easylist.txt`,`${ve}/peter-lowe/serverlist.txt`,`${ve}/ublock-origin/badware.txt`,`${ve}/ublock-origin/filters-2020.txt`,`${ve}/ublock-origin/filters-2021.txt`,`${ve}/ublock-origin/filters-2022.txt`,`${ve}/ublock-origin/filters-2023.txt`,`${ve}/ublock-origin/filters-2024.txt`,`${ve}/ublock-origin/filters.txt`,`${ve}/ublock-origin/quick-fixes.txt`,`${ve}/ublock-origin/resource-abuse.txt`,`${ve}/ublock-origin/unbreak.txt`],Ce=[..._e,`${ve}/easylist/easyprivacy.txt`,`${ve}/ublock-origin/privacy.txt`],xe=[...Ce,`${ve}/easylist/easylist-cookie.txt`,`${ve}/ublock-origin/annoyances-others.txt`,`${ve}/ublock-origin/annoyances-cookies.txt`];var Se=new Set(["any","dir","has","host-context","if","if-not","is","matches","not","where"]),Ee={attribute:/\[\s*(?:(?\*|[-\w]*)\|)?(?[-\w\u{0080}-\u{FFFF}]+)\s*(?:(?\W?=)\s*(?.+?)\s*(?[iIsS])?\s*)?\]/gu,id:/#(?(?:[-\w\u{0080}-\u{FFFF}]|\\.)+)/gu,class:/\.(?(?:[-\w\u{0080}-\u{FFFF}]|\\.)+)/gu,comma:/\s*,\s*/g,combinator:/\s*[\s>+~]\s*/g,"pseudo-element":/::(?[-\w\u{0080}-\u{FFFF}]+)(?:\((?:¶*)\))?/gu,"pseudo-class":/:(?[-\w\u{0080}-\u{FFFF}]+)(?:\((?¶*)\))?/gu,type:/(?:(?\*|[-\w]*)\|)?(?[-\w\u{0080}-\u{FFFF}]+)|\*/gu},Ie=new Set(["pseudo-class","pseudo-element"]),Fe=new Set([...Ie,"attribute"]),Te=new Set(["combinator","comma"]),Oe=Object.assign({},Ee);function Pe(e,t){e.lastIndex=0;const o=e.exec(t);if(null===o)return;const i=o.index-1,n=o[0],s=t.slice(0,i+1),c=t.slice(i+n.length+1);return[s,[n,o.groups||{}],c]}Oe["pseudo-element"]=RegExp(Ee["pseudo-element"].source.replace("(?¶*)","(?.*?)"),"gu"),Oe["pseudo-class"]=RegExp(Ee["pseudo-class"].source.replace("(?¶*)","(?.*)"),"gu");var Re=[e=>{const t=Pe(Ee.attribute,e);if(void 0===t)return;const[o,[i,{name:n,operator:s,value:c,namespace:r,caseSensitive:a}],l]=t;return void 0!==n?[o,{type:"attribute",content:i,length:i.length,namespace:r,caseSensitive:a,pos:[],name:n,operator:s,value:c},l]:void 0},e=>{const t=Pe(Ee.id,e);if(void 0===t)return;const[o,[i,{name:n}],s]=t;return void 0!==n?[o,{type:"id",content:i,length:i.length,pos:[],name:n},s]:void 0},e=>{const t=Pe(Ee.class,e);if(void 0===t)return;const[o,[i,{name:n}],s]=t;return void 0!==n?[o,{type:"class",content:i,length:i.length,pos:[],name:n},s]:void 0},e=>{const t=Pe(Ee.comma,e);if(void 0===t)return;const[o,[i],n]=t;return[o,{type:"comma",content:i,length:i.length,pos:[]},n]},e=>{const t=Pe(Ee.combinator,e);if(void 0===t)return;const[o,[i],n]=t;return[o,{type:"combinator",content:i,length:i.length,pos:[]},n]},e=>{const t=Pe(Ee["pseudo-element"],e);if(void 0===t)return;const[o,[i,{name:n}],s]=t;return void 0!==n?[o,{type:"pseudo-element",content:i,length:i.length,pos:[],name:n},s]:void 0},e=>{const t=Pe(Ee["pseudo-class"],e);if(void 0===t)return;const[o,[i,{name:n,argument:s}],c]=t;return void 0!==n?[o,{type:"pseudo-class",content:i,length:i.length,pos:[],name:n,argument:s,subtree:void 0},c]:void 0},e=>{const t=Pe(Ee.type,e);if(void 0===t)return;const[o,[i,{name:n,namespace:s}],c]=t;return[o,{type:"type",content:i,length:i.length,namespace:s,pos:[],name:n},c]}];function ze(e,t,o,i){for(const n of t)for(const t of e)if(i.has(t.type)&&t.pos[0]=0&&"\\"===e[t];)o+=1,t-=1;return o%2!=0}function Be(e,t,o){let i=o+1;for(;-1!==(i=e.indexOf(t,i))&&!0===Le(e,i);)i+=1;if(-1!==i)return e.slice(o,i+1)}function Ue(e,t){let o=0;for(let i=t;i0))return;o-=1}if(0===o)return e.slice(t,i+1)}}function Ne(e,t,o,i){const n=[];let s=0;for(;-1!==(s=e.indexOf(o,s));){const o=i(e,s);if(void 0===o)break;n.push({str:o,start:s}),e=`${e.slice(0,s+1)}${t.repeat(o.length-2)}${e.slice(s+o.length-1)}`,s+=o.length}return[n,e]}function Ve(e){if("string"!=typeof e)return[];if(0===(e=e.trim()).length)return[];const[t,o]=Ne(e,"§",'"',((e,t)=>Be(e,'"',t))),[i,n]=Ne(o,"§","'",((e,t)=>Be(e,"'",t))),[s,c]=Ne(n,"¶","(",Ue),r=function(e){if(!e)return[];const t=[e];for(const e of Re)for(let o=0;o0!==e.length)))}}let o=0;for(const e of t)"string"!=typeof e&&(e.pos=[o,o+e.length],Te.has(e.type)&&(e.content=e.content.trim()||" ")),o+=e.length;return t.every((e=>"string"!=typeof e))?t:[]}(c);return ze(r,s,/\(¶*\)/,Ie),ze(r,t,/"§*"/,Fe),ze(r,i,/'§*'/,Fe),r}function je(e,{list:t=!0}={}){if(!0===t&&e.some((e=>"comma"===e.type))){const t=[],o=[];for(let i=0;i=0;t--){const o=e[t];if("combinator"===o.type){const i=je(e.slice(0,t)),n=je(e.slice(t+1));if(void 0===n)return;if(" "!==o.content&&"~"!==o.content&&"+"!==o.content&&">"!==o.content)return;return{type:"complex",combinator:o.content,left:i,right:n}}}if(0!==e.length)return function(e){return e.every((e=>"comma"!==e.type&&"combinator"!==e.type))}(e)?1===e.length?e[0]:{type:"compound",compound:[...e]}:void 0}function Me(e,t,o,i){if(void 0!==e){if("complex"===e.type)Me(e.left,t,o,e),Me(e.right,t,o,e);else if("compound"===e.type)for(const i of e.compound)Me(i,t,o,e);else"pseudo-class"===e.type&&void 0!==e.subtree&&void 0!==o&&"pseudo-class"===o.type&&void 0!==o.subtree&&Me(e.subtree,t,o,e);t(e,i)}}function De(e,{recursive:t=!0,list:o=!0}={}){const i=Ve(e);if(0===i.length)return;const n=je(i,{list:o});return!0===t&&Me(n,(e=>{"pseudo-class"===e.type&&e.argument&&void 0!==e.name&&Se.has(e.name)&&(e.subtree=De(e.argument,{recursive:!0,list:!0}))})),n}var He,We,qe=new Set(["has","has-text","if"]),Ge=new Set(["active","any","any-link","blank","checked","default","defined","dir","disabled","empty","enabled","first","first-child","first-of-type","focus","focus-visible","focus-within","fullscreen","host","host-context","hover","in-range","indeterminate","invalid","is","lang","last-child","last-of-type","left","link","matches","not","nth-child","nth-last-child","nth-last-of-type","nth-of-type","only-child","only-of-type","optional","out-of-range","placeholder-shown","read-only","read-write","required","right","root","scope","target","valid","visited","where"]),$e=new Set(["after","before","first-letter","first-line"]);function Ke(e){if(-1===e.indexOf(":"))return He.Normal;const t=Ve(e);let o=!1;for(const e of t)if("pseudo-class"===e.type){const{name:t}=e;if(!0===qe.has(t))o=!0;else if(!1===Ge.has(t)&&!1===$e.has(t))return He.Invalid;if(!1===o&&void 0!==e.argument&&!0===Se.has(t)){const t=Ke(e.argument);if(t===He.Invalid)return t;t===He.Extended&&(o=!0)}}return!0===o?He.Extended:He.Normal}(We=He||(He={}))[We.Normal=0]="Normal",We[We.Extended=1]="Extended",We[We.Invalid=2]="Invalid";var Qe=new Set(["htm","html","xhtml"]),Ye=new Set(["eot","otf","sfnt","ttf","woff","woff2"]),Xe=new Set(["apng","bmp","cur","dib","eps","gif","heic","heif","ico","j2k","jfi","jfif","jif","jp2","jpe","jpeg","jpf","jpg","jpm","jpx","mj2","pjp","pjpeg","png","svg","svgz","tif","tiff","webp"]),Ze=new Set(["avi","flv","mp3","mp4","ogg","wav","weba","webm","wmv"]),Je=new Set(["js","ts","jsx","esm"]),et=new Set(["css","scss"]);function tt(e,t){let o=0,i=e.length,n=!1;if(!t){if(e.startsWith("data:"))return null;for(;oo+1&&e.charCodeAt(i-1)<=32;)i-=1;if(47===e.charCodeAt(o)&&47===e.charCodeAt(o+1))o+=2;else{const t=e.indexOf(":/",o);if(-1!==t){const i=t-o,n=e.charCodeAt(o),s=e.charCodeAt(o+1),c=e.charCodeAt(o+2),r=e.charCodeAt(o+3),a=e.charCodeAt(o+4);if(5===i&&104===n&&116===s&&116===c&&112===r&&115===a);else if(4===i&&104===n&&116===s&&116===c&&112===r);else if(3===i&&119===n&&115===s&&115===c);else if(2===i&&119===n&&115===s);else for(let i=o;i=97&&t<=122||t>=48&&t<=57||46===t||45===t||43===t))return null}for(o=t+2;47===e.charCodeAt(o);)o+=1}}let t=-1,s=-1,c=-1;for(let r=o;r=65&&o<=90&&(n=!0)}if(-1!==t&&t>o&&to&&co+1&&46===e.charCodeAt(i-1);)i-=1;const s=0!==o||i!==e.length?e.slice(o,i):e;return n?s.toLowerCase():s}function ot(e){return e>=97&&e<=122||e>=48&&e<=57||e>127}function it(e){if(e.length>255)return!1;if(0===e.length)return!1;if(!ot(e.charCodeAt(0))&&46!==e.charCodeAt(0)&&95!==e.charCodeAt(0))return!1;let t=-1,o=-1;const i=e.length;for(let n=0;n64||46===o||45===o||95===o)return!1;t=n}else if(!ot(i)&&45!==i&&95!==i)return!1;o=i}return i-t-1<=63&&45!==o}var nt=function({allowIcannDomains:e=!0,allowPrivateDomains:t=!1,detectIp:o=!0,extractHostname:i=!0,mixedInputs:n=!0,validHosts:s=null,validateHostname:c=!0}){return{allowIcannDomains:e,allowPrivateDomains:t,detectIp:o,extractHostname:i,mixedInputs:n,validHosts:s,validateHostname:c}}({});function st(e,t,o,i,n){const s=function(e){return void 0===e?nt:function({allowIcannDomains:e=!0,allowPrivateDomains:t=!1,detectIp:o=!0,extractHostname:i=!0,mixedInputs:n=!0,validHosts:s=null,validateHostname:c=!0}){return{allowIcannDomains:e,allowPrivateDomains:t,detectIp:o,extractHostname:i,mixedInputs:n,validHosts:s,validateHostname:c}}(e)}(i);return"string"!=typeof e?n:(s.extractHostname?s.mixedInputs?n.hostname=tt(e,it(e)):n.hostname=tt(e,!1):n.hostname=e,0===t||null===n.hostname||s.detectIp&&(n.isIp=function(e){if(e.length<3)return!1;let t=e.startsWith("[")?1:0,o=e.length;if("]"===e[o-1]&&(o-=1),o-t>39)return!1;let i=!1;for(;t=48&&o<=57||o>=97&&o<=102||o>=65&&o<=90))return!1}return i}(c=n.hostname)||function(e){if(e.length<7)return!1;if(e.length>15)return!1;let t=0;for(let o=0;o57)return!1}return 3===t&&46!==e.charCodeAt(0)&&46!==e.charCodeAt(e.length-1)}(c),n.isIp)?n:s.validateHostname&&s.extractHostname&&!it(n.hostname)?(n.hostname=null,n):(o(n.hostname,s,n),2===t||null===n.publicSuffix?n:(n.domain=function(e,t,o){if(null!==o.validHosts){const e=o.validHosts;for(const o of e)if(function(e,t){return!!e.endsWith(t)&&(e.length===t.length||"."===e[e.length-t.length-1])}(t,o))return o}let i=0;if(t.startsWith("."))for(;i=i)return!1;let n=o,s=i-1;for(;n<=s;){const o=n+s>>>1,i=e[o];if(it))return!0;s=o-1}}return!1}var at=new Uint32Array(20);function lt(e,t,o){if(function(e,t,o){if(!t.allowPrivateDomains&&e.length>3){const t=e.length-1,i=e.charCodeAt(t),n=e.charCodeAt(t-1),s=e.charCodeAt(t-2),c=e.charCodeAt(t-3);if(109===i&&111===n&&99===s&&46===c)return o.isIcann=!0,o.isPrivate=!1,o.publicSuffix="com",!0;if(103===i&&114===n&&111===s&&46===c)return o.isIcann=!0,o.isPrivate=!1,o.publicSuffix="org",!0;if(117===i&&100===n&&101===s&&46===c)return o.isIcann=!0,o.isPrivate=!1,o.publicSuffix="edu",!0;if(118===i&&111===n&&103===s&&46===c)return o.isIcann=!0,o.isPrivate=!1,o.publicSuffix="gov",!0;if(116===i&&101===n&&110===s&&46===c)return o.isIcann=!0,o.isPrivate=!1,o.publicSuffix="net",!0;if(101===i&&100===n&&46===s)return o.isIcann=!0,o.isPrivate=!1,o.publicSuffix="de",!0}return!1}(e,t,o))return;const{allowIcannDomains:i,allowPrivateDomains:n}=t;let s=-1,c=0,r=0,a=1;const l=function(e,t){let o=5381,i=0;for(let n=e.length-1;n>=0;n-=1){const s=e.charCodeAt(n);if(46===s&&(at[i<<1]=o>>>0,at[1+(i<<1)]=n+1,i+=1,i===t))return i;o=33*o^s}return at[i<<1]=o>>>0,at[1+(i<<1)]=0,i+=1,i}(e,ct[0]);for(let e=0;er;)t.shift();o.publicSuffix=t.join(".")}else o.publicSuffix=e.slice(at[1+(r-2<<1)]);else o.publicSuffix=1===l?e:e.slice(at[1])}function pt(e,t={}){return st(e,5,lt,t,{domain:null,domainWithoutSuffix:null,hostname:null,isIcann:null,isIp:null,isPrivate:null,publicSuffix:null,subdomain:null})}var dt=new class{constructor(e){this.pos=0,this.buffer=new Uint32Array(e)}reset(){this.pos=0}slice(){return this.buffer.slice(0,this.pos)}push(e){this.buffer[this.pos++]=e}empty(){return 0===this.pos}full(){return this.pos===this.buffer.length}remaining(){return this.buffer.length-this.pos}}(1024),ut=37,ht=5011;function mt(e){return 16843009*((e=(858993459&(e-=e>>1&1431655765))+(e>>2&858993459))+(e>>4)&252645135)>>24}function At(e,t){return!!(e&t)}function gt(e,t){return e|t}function ft(e,t){return e&~t}function kt(e,t,o){let i=ht;for(let n=t;n>>0}function bt(e){return"string"!=typeof e||0===e.length?ht:kt(e,0,e.length)}function yt(e){const t=new Uint32Array(e.length);let o=0;for(const i of e)t[o++]=bt(i);return t}function wt(e,t){if(e.length=48&&e<=57}function Ct(e){return e>=97&&e<=122||e>=65&&e<=90}function xt(e){return Ct(e)||_t(e)||37===e||function(e){return e>=192&&e<=450}(e)||function(e){return e>=1024&&e<=1279}(e)}function St(e,t,o,i){const n=Math.min(e.length,2*i.remaining());let s=!1,c=0,r=ht;for(let o=0;o1&&(!1===t||0!==c)&&i.push(r>>>0))}!0===s&&!1===o&&e.length-c>1&&!1===i.full()&&i.push(r>>>0)}function Et(e,t){const o=Math.min(e.length,2*t.remaining());let i=!1,n=0,s=ht;for(let c=0;c1&&t.push(s>>>0))}!0===i&&e.length-n>1&&!1===t.full()&&t.push(s>>>0)}function It(e,t){return-1!==function(e,t){if(0===e.length)return-1;let o=0,i=e.length-1;for(;o<=i;){const n=o+i>>>1,s=e[n];if(st))return n;i=n-1}}return-1}(e,t)}var Ft=/[^\u0000-\u00ff]/;function Tt(e){return Ft.test(e)}var Ot={extractHostname:!0,mixedInputs:!1,validateHostname:!1},Pt={beacon:bt("type:beacon"),cspReport:bt("type:csp"),csp_report:bt("type:csp"),cspviolationreport:bt("type:cspviolationreport"),document:bt("type:document"),eventsource:bt("type:other"),fetch:bt("type:xhr"),font:bt("type:font"),image:bt("type:image"),imageset:bt("type:image"),mainFrame:bt("type:document"),main_frame:bt("type:document"),manifest:bt("type:other"),media:bt("type:media"),object:bt("type:object"),object_subrequest:bt("type:object"),other:bt("type:other"),ping:bt("type:ping"),prefetch:bt("type:other"),preflight:bt("type:preflight"),script:bt("type:script"),signedexchange:bt("type:signedexchange"),speculative:bt("type:other"),stylesheet:bt("type:stylesheet"),subFrame:bt("type:subdocument"),sub_frame:bt("type:subdocument"),texttrack:bt("type:other"),webSocket:bt("type:websocket"),web_manifest:bt("type:other"),websocket:bt("type:websocket"),xhr:bt("type:xhr"),xml_dtd:bt("type:other"),xmlhttprequest:bt("type:xhr"),xslt:bt("type:other")};function Rt(e){let t=ht;for(let o=e.length-1;o>=0;o-=1)t=t*ut^e.charCodeAt(o);return t>>>0}function zt(e,t,o){dt.reset();let i=ht;for(let n=t-1;n>=0;n-=1){const t=e.charCodeAt(n);46===t&&n>>0),i=i*ut^t}return dt.push(i>>>0),dt.slice()}function Lt(e,t){const o=function(e,t){let o=null;const i=t.indexOf(".");if(-1!==i){const n=t.slice(i+1);o=e.slice(0,-n.length-1)}return o}(e,t);return null!==o?zt(o,o.length,o.length):ne}function Bt(e,t){return zt(e,e.length,e.length-t.length)}var Ut=class e{static fromRawDetails({requestId:t="0",tabId:o=0,url:i="",hostname:n,domain:s,sourceUrl:c="",sourceHostname:r,sourceDomain:a,type:l="main_frame",_originalRequestDetails:p}){if(i=i.toLowerCase(),void 0===n||void 0===s){const e=pt(i,Ot);n=n||e.hostname||"",s=s||e.domain||""}if(void 0===r||void 0===a){const e=pt(r||a||c,Ot);r=r||e.hostname||"",a=a||e.domain||r||""}return new e({requestId:t,tabId:o,domain:s,hostname:n,url:i,sourceDomain:a,sourceHostname:r,sourceUrl:c,type:l,_originalRequestDetails:p})}constructor({requestId:e,tabId:t,type:o,domain:i,hostname:n,url:s,sourceDomain:c,sourceHostname:r,_originalRequestDetails:a}){if(this.tokens=void 0,this.hostnameHashes=void 0,this.entityHashes=void 0,this._originalRequestDetails=a,this.id=e,this.tabId=t,this.type=o,this.url=s,this.hostname=n,this.domain=i,this.sourceHostnameHashes=0===r.length?ne:Bt(r,c),this.sourceEntityHashes=0===r.length?ne:Lt(r,c),this.isThirdParty=function(e,t,o,i,n){return"main_frame"!==n&&"mainFrame"!==n&&(0!==t.length&&0!==i.length?t!==i:0!==t.length&&0!==o.length?t!==o:0!==i.length&&0!==e.length&&e!==i)}(n,i,r,c,o),this.isFirstParty=!this.isThirdParty,this.isSupported=!0,"websocket"===this.type||this.url.startsWith("ws:")||this.url.startsWith("wss:"))this.isHttp=!1,this.isHttps=!1,this.type="websocket",this.isSupported=!0;else if(this.url.startsWith("http:"))this.isHttp=!0,this.isHttps=!1;else if(this.url.startsWith("https:"))this.isHttps=!0,this.isHttp=!1;else if(this.url.startsWith("data:")){this.isHttp=!1,this.isHttps=!1;const e=this.url.indexOf(",");-1!==e&&(this.url=this.url.slice(0,e))}else this.isHttp=!1,this.isHttps=!1,this.isSupported=!1}getHostnameHashes(){return void 0===this.hostnameHashes&&(this.hostnameHashes=0===this.hostname.length?ne:Bt(this.hostname,this.domain)),this.hostnameHashes}getEntityHashes(){return void 0===this.entityHashes&&(this.entityHashes=0===this.hostname.length?ne:Lt(this.hostname,this.domain)),this.entityHashes}getTokens(){if(void 0===this.tokens){dt.reset();for(const e of this.sourceHostnameHashes)dt.push(e);dt.push(Pt[this.type]),Et(this.url,dt),this.tokens=dt.slice()}return this.tokens}isMainFrame(){return"main_frame"===this.type||"mainFrame"===this.type}isSubFrame(){return"sub_frame"===this.type||"subFrame"===this.type}guessTypeOfRequest(){const e=this.type;return this.type=function(e){const t=function(e){let t=e.length;const o=e.indexOf("#");-1!==o&&(t=o);const i=e.indexOf("?");-1!==i&&i=0&&(s=e.charCodeAt(n),0!=(s>=65&&s<=90||s>=97&&s<=122||s>=48&&s<=57));n-=1);return 46!==s||n<0||t-n>=10?"":e.slice(n+1,t)}(e);return Xe.has(t)||e.startsWith("data:image/")||e.startsWith("https://frog.wix.com/bt")?"image":Ze.has(t)||e.startsWith("data:audio/")||e.startsWith("data:video/")?"media":et.has(t)||e.startsWith("data:text/css")?"stylesheet":Je.has(t)||e.startsWith("data:")&&(e.startsWith("data:application/ecmascript")||e.startsWith("data:application/javascript")||e.startsWith("data:application/x-ecmascript")||e.startsWith("data:application/x-javascript")||e.startsWith("data:text/ecmascript")||e.startsWith("data:text/javascript")||e.startsWith("data:text/javascript1.0")||e.startsWith("data:text/javascript1.1")||e.startsWith("data:text/javascript1.2")||e.startsWith("data:text/javascript1.3")||e.startsWith("data:text/javascript1.4")||e.startsWith("data:text/javascript1.5")||e.startsWith("data:text/jscript")||e.startsWith("data:text/livescript")||e.startsWith("data:text/x-ecmascript")||e.startsWith("data:text/x-javascript"))||e.startsWith("https://maps.googleapis.com/maps/api/js")||e.startsWith("https://www.googletagmanager.com/gtag/js")?"script":Qe.has(t)||e.startsWith("data:text/html")||e.startsWith("data:application/xhtml")||e.startsWith("https://www.youtube.com/embed/")||e.startsWith("https://www.google.com/gen_204")?"document":Ye.has(t)||e.startsWith("data:font/")?"font":"other"}(this.url),e!==this.type&&(this.tokens=void 0),this.type}},Nt=class e{static parse(t,o=!1){if(0===t.length)return;const i=[],n=[],s=[],c=[];for(let e of t){Tt(e)&&(e=oe(e));const t=126===e.charCodeAt(0),o=42===e.charCodeAt(e.length-1)&&46===e.charCodeAt(e.length-2),r=t?1:0,a=o?e.length-2:e.length,l=Rt(!0===t||!0===o?e.slice(r,a):e);t?o?n.push(l):c.push(l):o?i.push(l):s.push(l)}return new e({entities:0!==i.length?new Uint32Array(i).sort():void 0,hostnames:0!==s.length?new Uint32Array(s).sort():void 0,notEntities:0!==n.length?new Uint32Array(n).sort():void 0,notHostnames:0!==c.length?new Uint32Array(c).sort():void 0,parts:!0===o?t.join(","):void 0})}static deserialize(t){const o=t.getUint8();return new e({entities:1==(1&o)?t.getUint32Array():void 0,hostnames:2==(2&o)?t.getUint32Array():void 0,notEntities:4==(4&o)?t.getUint32Array():void 0,notHostnames:8==(8&o)?t.getUint32Array():void 0,parts:16==(16&o)?t.getUTF8():void 0})}constructor({entities:e,hostnames:t,notEntities:o,notHostnames:i,parts:n}){this.entities=e,this.hostnames=t,this.notEntities=o,this.notHostnames=i,this.parts=n}updateId(e){const{hostnames:t,entities:o,notHostnames:i,notEntities:n}=this;if(void 0!==t)for(const o of t)e=e*ut^o;if(void 0!==o)for(const t of o)e=e*ut^t;if(void 0!==i)for(const t of i)e=e*ut^t;if(void 0!==n)for(const t of n)e=e*ut^t;return e}serialize(e){const t=e.getPos();e.pushUint8(0);let o=0;void 0!==this.entities&&(o|=1,e.pushUint32Array(this.entities)),void 0!==this.hostnames&&(o|=2,e.pushUint32Array(this.hostnames)),void 0!==this.notEntities&&(o|=4,e.pushUint32Array(this.notEntities)),void 0!==this.notHostnames&&(o|=8,e.pushUint32Array(this.notHostnames)),void 0!==this.parts&&(o|=16,e.pushUTF8(this.parts)),e.setByte(t,o)}getSerializedSize(){let e=1;return void 0!==this.entities&&(e+=ue(this.entities)),void 0!==this.hostnames&&(e+=ue(this.hostnames)),void 0!==this.notHostnames&&(e+=ue(this.notHostnames)),void 0!==this.notEntities&&(e+=ue(this.notEntities)),void 0!==this.parts&&(e+=de(this.parts)),e}match(e,t){if(void 0!==this.notHostnames)for(const t of e)if(It(this.notHostnames,t))return!1;if(void 0!==this.notEntities)for(const e of t)if(It(this.notEntities,e))return!1;if(void 0!==this.hostnames||void 0!==this.entities){if(void 0!==this.hostnames)for(const t of e)if(It(this.hostnames,t))return!0;if(void 0!==this.entities)for(const e of t)if(It(this.entities,e))return!0;return!1}return!0}};function Vt(e){if(!1===e.startsWith("^script"))return;const t=":has-text(",o=[];let i=7;for(;e.startsWith(t,i);){i+=10;let t=1;const n=i;let s=-1;for(;i=48&&o<=57||o>=65&&o<=90||o>=97&&o<=122)){if(t{}},t=/^[#.]?[\w-.]+$/;return function(o){if(t.test(o))return!0;try{(t=>{e.matches(t)})(o)}catch(e){return!1}return!0}})();function Yt(e,t){const o=e.getSelector();if(!1===e.isScriptInject())return o;const i=e.parseScript();if(void 0===i)return o;const n=t(i.name);return void 0===n?o:o.replace(i.name,n)}(Kt=$t||($t={}))[Kt.unhide=1]="unhide",Kt[Kt.scriptInject=2]="scriptInject",Kt[Kt.isUnicode=4]="isUnicode",Kt[Kt.isClassSelector=8]="isClassSelector",Kt[Kt.isIdSelector=16]="isIdSelector",Kt[Kt.isHrefSelector=32]="isHrefSelector",Kt[Kt.remove=64]="remove",Kt[Kt.extended=128]="extended";var Xt=class e{static parse(t,o=!1){const i=t;let n,s,c,r=0;const a=t.indexOf("#"),l=a+1;let p=l+1;if(t.length>l&&("@"===t[l]?(r=gt(r,$t.unhide),p+=1):"?"===t[l]&&(p+=1)),p>=t.length)return null;if(a>0&&(s=Nt.parse(t.slice(0,a).split(","),o)),t.endsWith(":remove()"))r=gt(r,$t.remove),r=gt(r,$t.extended),t=t.slice(0,-9);else if(t.length-p>=8&&t.endsWith(")")&&-1!==t.indexOf(":style(",p)){const e=t.indexOf(":style(",p);c=t.slice(e+7,-1),t=t.slice(0,e)}if(94===t.charCodeAt(p)){if(!1===vt(t,"script:has-text(",p+1)||41!==t.charCodeAt(t.length-1))return null;if(n=t.slice(p,t.length),void 0===Vt(n))return null}else if(t.length-p>4&&43===t.charCodeAt(p)&&vt(t,"+js(",p)){if((void 0===s||void 0===s.hostnames&&void 0===s.entities)&&!1===At(r,$t.unhide))return null;if(r=gt(r,$t.scriptInject),n=t.slice(p+4,t.length-1),!1===At(r,$t.unhide)&&0===n.length)return null}else{n=t.slice(p);const e=Ke(n);if(e===He.Extended)r=gt(r,$t.extended);else if(e===He.Invalid||!Qt(n))return null}if(void 0===s&&!0===At(r,$t.extended))return null;if(void 0!==n&&(Tt(n)&&(r=gt(r,$t.isUnicode)),!1===At(r,$t.scriptInject)&&!1===At(r,$t.remove)&&!1===At(r,$t.extended)&&!1===n.startsWith("^"))){const e=n.charCodeAt(0),t=n.charCodeAt(1),o=n.charCodeAt(2);!1===At(r,$t.scriptInject)&&(46===e&&qt(n)?r=gt(r,$t.isClassSelector):35===e&&qt(n)?r=gt(r,$t.isIdSelector):(97===e&&91===t&&104===o&&Gt(n,2)||91===e&&104===t&&Gt(n,1))&&(r=gt(r,$t.isHrefSelector)))}return new e({mask:r,rawLine:!0===o?i:void 0,selector:n,style:c,domains:s})}static deserialize(t){const o=t.getUint8(),i=At(o,$t.isUnicode),n=t.getUint8(),s=i?t.getUTF8():t.getCosmeticSelector();return new e({mask:o,selector:s,domains:1==(1&n)?Nt.deserialize(t):void 0,rawLine:2==(2&n)?t.getRawCosmetic():void 0,style:4==(4&n)?t.getASCII():void 0})}constructor({mask:e,selector:t,domains:o,rawLine:i,style:n}){this.mask=e,this.selector=t,this.domains=o,this.style=n,this.id=void 0,this.rawLine=i,this.scriptletDetails=void 0}isCosmeticFilter(){return!0}isNetworkFilter(){return!1}serialize(e){e.pushUint8(this.mask);const t=e.getPos();e.pushUint8(0),this.isUnicode()?e.pushUTF8(this.selector):e.pushCosmeticSelector(this.selector);let o=0;void 0!==this.domains&&(o|=1,this.domains.serialize(e)),void 0!==this.rawLine&&(o|=2,e.pushRawCosmetic(this.rawLine)),void 0!==this.style&&(o|=4,e.pushASCII(this.style)),e.setByte(t,o)}getSerializedSize(e){let t=2;return this.isUnicode()?t+=de(this.selector):t+=function(e,t){return!0===t?le(ce().cosmeticSelector.getCompressedSize(e),!1):pe(e)}(this.selector,e),void 0!==this.domains&&(t+=this.domains.getSerializedSize()),void 0!==this.rawLine&&(t+=function(e,t){return!0===t?le(ce().cosmeticRaw.getCompressedSize(te(e)),!1):de(e)}(this.rawLine,e)),void 0!==this.style&&(t+=pe(this.style)),t}toString(){if(void 0!==this.rawLine)return this.rawLine;let e="";return void 0!==this.domains&&(void 0!==this.domains.parts?e+=this.domains.parts:e+=""),this.isUnhide()?e+="#@#":e+="##",this.isScriptInject()?(e+="+js(",e+=this.selector,e+=")"):e+=this.selector,e}match(e,t){return!1===this.hasHostnameConstraint()||!(!e&&this.hasHostnameConstraint())&&(void 0===this.domains||this.domains.match(0===e.length?ne:Bt(e,t),0===e.length?ne:Lt(e,t)))}getTokens(){const e=[];if(void 0!==this.domains){const{hostnames:t,entities:o}=this.domains;if(void 0!==t)for(const o of t)e.push(new Uint32Array([o]));if(void 0!==o)for(const t of o)e.push(new Uint32Array([t]))}if(0===e.length&&!1===this.isUnhide())if(this.isIdSelector()||this.isClassSelector()){let t=1;const o=this.selector;for(;t0?n=!0:"'"===p&&e.indexOf("'",o+1)>0?s=!0:"{"===p&&e.indexOf("}",o+1)>0?r+=1:"/"===p&&e.indexOf("/",o+1)>0?c=!0:l=!0)),","===p&&(t.push(e.slice(i+1,o).trim()),i=o,l=!1))),a="\\"===p}if(t.push(e.slice(i+1).trim()),0===t.length)return;const p=t.slice(1).map((e=>e.startsWith("'")&&e.endsWith("'")||e.startsWith('"')&&e.endsWith('"')?e.substring(1,e.length-1):e)).map((e=>e.replace(Dt,",").replace(Ht,"\\").replace(Wt,",")));return this.scriptletDetails={name:t[0],args:p},this.scriptletDetails}getScript(e){const t=this.parseScript();if(void 0===t)return;const{name:o,args:i}=t;let n=e(o);if(void 0!==n){for(let e=0;e>>0}(this.mask,this.selector,this.domains,this.style)),this.id}hasCustomStyle(){return void 0!==this.style}getStyle(e=Mt){return this.style||e}getStyleAttributeHash(){return`s${bt(this.getStyle())}`}getSelector(){return this.selector}getSelectorAST(){return De(this.getSelector())}getExtendedSelector(){return Vt(this.selector)}isExtended(){return At(this.mask,$t.extended)}isRemove(){return At(this.mask,$t.remove)}isUnhide(){return At(this.mask,$t.unhide)}isScriptInject(){return At(this.mask,$t.scriptInject)}isCSS(){return!1===this.isScriptInject()}isIdSelector(){return At(this.mask,$t.isIdSelector)}isClassSelector(){return At(this.mask,$t.isClassSelector)}isHrefSelector(){return At(this.mask,$t.isHrefSelector)}isUnicode(){return At(this.mask,$t.isUnicode)}isHtmlFiltering(){return this.getSelector().startsWith("^")}isGenericHide(){var e,t;return void 0===(null===(e=null==this?void 0:this.domains)||void 0===e?void 0:e.hostnames)&&void 0===(null===(t=null==this?void 0:this.domains)||void 0===t?void 0:t.entities)}},Zt=class{constructor(){this.options=new Set,this.prefix=void 0,this.infix=void 0,this.suffix=void 0,this.redirect=void 0}blockRequestsWithType(e){if(this.options.has(e))throw new Error(`Already blocking type ${e}`);return this.options.add(e),this}images(){return this.blockRequestsWithType("image")}scripts(){return this.blockRequestsWithType("script")}frames(){return this.blockRequestsWithType("frame")}fonts(){return this.blockRequestsWithType("font")}medias(){return this.blockRequestsWithType("media")}styles(){return this.blockRequestsWithType("css")}redirectTo(e){if(void 0!==this.redirect)throw new Error(`Already redirecting: ${this.redirect}`);return this.redirect=`redirect=${e}`,this}urlContains(e){if(void 0!==this.infix)throw new Error(`Already matching pattern: ${this.infix}`);return this.infix=e,this}urlStartsWith(e){if(void 0!==this.prefix)throw new Error(`Already matching prefix: ${this.prefix}`);return this.prefix=`|${e}`,this}urlEndsWith(e){if(void 0!==this.suffix)throw new Error(`Already matching suffix: ${this.suffix}`);return this.suffix=`${e}|`,this}withHostname(e){if(void 0!==this.prefix)throw new Error(`Cannot match hostname if filter already has prefix: ${this.prefix}`);return this.prefix=`||${e}^`,this}toString(){const e=[];void 0!==this.prefix&&e.push(this.prefix),void 0!==this.infix&&e.push(this.infix),void 0!==this.suffix&&e.push(this.suffix);const t=["important"];if(0!==this.options.size)for(const e of this.options)t.push(e);return void 0!==this.redirect&&t.push(this.redirect),`${0===e.length?"*":e.join("*")}$${t.join(",")}`}};function Jt(){return new Zt}var eo,to,oo=bt("http"),io=bt("https");(to=eo||(eo={}))[to.fromDocument=1]="fromDocument",to[to.fromFont=2]="fromFont",to[to.fromHttp=4]="fromHttp",to[to.fromHttps=8]="fromHttps",to[to.fromImage=16]="fromImage",to[to.fromMedia=32]="fromMedia",to[to.fromObject=64]="fromObject",to[to.fromOther=128]="fromOther",to[to.fromPing=256]="fromPing",to[to.fromScript=512]="fromScript",to[to.fromStylesheet=1024]="fromStylesheet",to[to.fromSubdocument=2048]="fromSubdocument",to[to.fromWebsocket=4096]="fromWebsocket",to[to.fromXmlHttpRequest=8192]="fromXmlHttpRequest",to[to.firstParty=16384]="firstParty",to[to.thirdParty=32768]="thirdParty",to[to.isReplace=65536]="isReplace",to[to.isBadFilter=131072]="isBadFilter",to[to.isCSP=262144]="isCSP",to[to.isGenericHide=524288]="isGenericHide",to[to.isImportant=1048576]="isImportant",to[to.isSpecificHide=2097152]="isSpecificHide",to[to.isFullRegex=4194304]="isFullRegex",to[to.isRegex=8388608]="isRegex",to[to.isUnicode=16777216]="isUnicode",to[to.isLeftAnchor=33554432]="isLeftAnchor",to[to.isRightAnchor=67108864]="isRightAnchor",to[to.isException=134217728]="isException",to[to.isHostnameAnchor=268435456]="isHostnameAnchor",to[to.isRedirectRule=536870912]="isRedirectRule",to[to.isRedirect=1073741824]="isRedirect";var no=eo.fromDocument|eo.fromFont|eo.fromImage|eo.fromMedia|eo.fromObject|eo.fromOther|eo.fromPing|eo.fromScript|eo.fromStylesheet|eo.fromSubdocument|eo.fromWebsocket|eo.fromXmlHttpRequest,so={beacon:eo.fromPing,document:eo.fromDocument,cspviolationreport:eo.fromOther,fetch:eo.fromXmlHttpRequest,font:eo.fromFont,image:eo.fromImage,imageset:eo.fromImage,mainFrame:eo.fromDocument,main_frame:eo.fromDocument,media:eo.fromMedia,object:eo.fromObject,object_subrequest:eo.fromObject,ping:eo.fromPing,script:eo.fromScript,stylesheet:eo.fromStylesheet,subFrame:eo.fromSubdocument,sub_frame:eo.fromSubdocument,webSocket:eo.fromWebsocket,websocket:eo.fromWebsocket,xhr:eo.fromXmlHttpRequest,xmlhttprequest:eo.fromXmlHttpRequest,cspReport:eo.fromOther,csp_report:eo.fromOther,eventsource:eo.fromOther,manifest:eo.fromOther,other:eo.fromOther,prefetch:eo.fromOther,preflight:eo.fromOther,signedexchange:eo.fromOther,speculative:eo.fromOther,texttrack:eo.fromOther,web_manifest:eo.fromOther,xml_dtd:eo.fromOther,xslt:eo.fromOther};function co(e){const t=[];return e.fromDocument()&&t.push("document"),e.fromImage()&&t.push("image"),e.fromMedia()&&t.push("media"),e.fromObject()&&t.push("object"),e.fromOther()&&t.push("other"),e.fromPing()&&t.push("ping"),e.fromScript()&&t.push("script"),e.fromStylesheet()&&t.push("stylesheet"),e.fromSubdocument()&&t.push("sub_frame"),e.fromWebsocket()&&t.push("websocket"),e.fromXmlHttpRequest()&&t.push("xhr"),e.fromFont()&&t.push("font"),t}function ro(e,t,o,i,n,s){let c=ht*ut^e;if(void 0!==i&&(c=i.updateId(c)),void 0!==n&&(c=n.updateId(c)),void 0!==t)for(let e=0;e>>0}function ao(e,t,o,i){return!0===i?new RegExp(e.slice(1,e.length-1),"i"):(e=(e=(e=e.replace(/([|.$+?{}()[\]\\])/g,"\\$1")).replace(/\*/g,".*")).replace(/\^/g,"(?:[^\\w\\d_.%-]|$)"),o&&(e=`${e}$`),t&&(e=`^${e}`),new RegExp(e))}function lo(e,t,o){const i=t;for(;t=48&&e<=57||e<=65&&e<=70||e>=97&&e<=102}function mo(e,t,o){const i=e.charCodeAt(t+1);return 44===i||47===i?[t+1,!1]:function(e,t,o){const i=e.charCodeAt(t+1);if(44===i||uo.has(i))return[t+1,!0];if(99===i){const o=e.charCodeAt(t+2);if(o>=65&&o<=90||o>=97&&o<=122)return[t+2,!0]}if(120===i&&ho(e.charCodeAt(t+2))&&ho(e.charCodeAt(t+3)))return[t+3,!0];if(117===i)if(123===e.charCodeAt(t+2)){const o=e.indexOf("}",t+3),i=o-t+3;if(i>=1&&i<=6)return[o,!0]}else if(ho(e.charCodeAt(t+2))&&ho(e.charCodeAt(t+3))&&ho(e.charCodeAt(t+4))&&ho(e.charCodeAt(t+5)))return[t+5,!0];return[t+1,!1]}(e,t)}function Ao(e,t,o){if(47!==e.charCodeAt(t++))return[o,void 0];const i=["","",""];let n=t,s=0;for(;t0&&92===e.charCodeAt(o-1);)o=e.lastIndexOf(t,o-1);return o}(t,"$");if(-1!==u&&47!==t.charCodeAt(u+1)){d=u;for(const e of function(e,t,o){const i=[];let n,s;for(;t0&&(c=p);break;case"ehide":case"elemhide":if(t)return null;r=gt(r,eo.isGenericHide),r=gt(r,eo.isSpecificHide);break;case"shide":case"specifichide":if(t)return null;r=gt(r,eo.isSpecificHide);break;case"ghide":case"generichide":if(t)return null;r=gt(r,eo.isGenericHide);break;case"inline-script":if(t)return null;r=gt(r,eo.isCSP),c="script-src 'self' 'unsafe-eval' http: https: data: blob: mediastream: filesystem:";break;case"inline-font":if(t)return null;r=gt(r,eo.isCSP),c="font-src 'self' 'unsafe-eval' http: https: data: blob: mediastream: filesystem:";break;case"replace":case"content":if(t||(0===p.length?!1===At(r,eo.isException):null===go(p)))return null;r=gt(r,eo.isReplace),c=p;break;default:{let e=0;switch(i){case"all":if(t)return null;break;case"image":e=eo.fromImage;break;case"media":e=eo.fromMedia;break;case"object":case"object-subrequest":e=eo.fromObject;break;case"other":e=eo.fromOther;break;case"ping":case"beacon":e=eo.fromPing;break;case"script":e=eo.fromScript;break;case"css":case"stylesheet":e=eo.fromStylesheet;break;case"frame":case"subdocument":e=eo.fromSubdocument;break;case"xhr":case"xmlhttprequest":e=eo.fromXmlHttpRequest;break;case"websocket":e=eo.fromWebsocket;break;case"font":e=eo.fromFont;break;case"doc":case"document":e=eo.fromDocument;break;default:return null}t?l=ft(l,e):a=gt(a,e);break}}}}let h;if(r|=0===a?l:l===no?a:a&l,d-p>=2&&47===t.charCodeAt(p)&&47===t.charCodeAt(d-1)){h=t.slice(p,d);try{ao(h,!1,!1,!0)}catch(e){return null}r=gt(r,eo.isFullRegex)}else{if(d>0&&124===t.charCodeAt(d-1)&&(r=gt(r,eo.isRightAnchor),d-=1),p0&&42===t.charCodeAt(d-1)&&(d-=1),!1===At(r,eo.isHostnameAnchor)&&d-p>0&&42===t.charCodeAt(p)&&(r=ft(r,eo.isLeftAnchor),p+=1),At(r,eo.isLeftAnchor)&&(d-p==5&&vt(t,"ws://",p)?(r=gt(r,eo.fromWebsocket),r=ft(r,eo.isLeftAnchor),r=ft(r,eo.fromHttp),r=ft(r,eo.fromHttps),p=d):d-p==7&&vt(t,"http://",p)?(r=gt(r,eo.fromHttp),r=ft(r,eo.fromHttps),r=ft(r,eo.isLeftAnchor),p=d):d-p==8&&vt(t,"https://",p)?(r=gt(r,eo.fromHttps),r=ft(r,eo.fromHttp),r=ft(r,eo.isLeftAnchor),p=d):d-p==8&&vt(t,"http*://",p)&&(r=gt(r,eo.fromHttps),r=gt(r,eo.fromHttp),r=ft(r,eo.isLeftAnchor),p=d)),d-p>0&&(h=t.slice(p,d).toLowerCase(),r=bo(r,eo.isUnicode,Tt(h)),!1===At(r,eo.isRegex)&&(r=bo(r,eo.isRegex,function(e,t,o){const i=e.indexOf("^",t);if(-1!==i&&it.length)return!1;if(e.length===t.length)return e===t;const i=t.indexOf(e);if(-1===i)return!1;if(0===i)return!0===o||46===t.charCodeAt(e.length)||46===e.charCodeAt(e.length-1);if(t.length===i+e.length)return 46===t.charCodeAt(i-1)||46===e.charCodeAt(0);return!(!0!==o&&46!==t.charCodeAt(e.length)&&46!==e.charCodeAt(e.length-1)||46!==t.charCodeAt(i-1)&&46!==e.charCodeAt(0))}(i,t.hostname,void 0!==e.filter&&42===e.filter.charCodeAt(0)))return!1;if(e.isRegex())return e.getRegex().test(t.url.slice(t.url.indexOf(i)+i.length));if(e.isRightAnchor()&&e.isLeftAnchor()){return o===t.url.slice(t.url.indexOf(i)+i.length)}if(e.isRightAnchor()){const n=t.hostname;return!1===e.hasFilter()?i.length===n.length||n.endsWith(i):t.url.endsWith(o)}return e.isLeftAnchor()?vt(t.url,o,t.url.indexOf(i)+i.length):!1===e.hasFilter()||-1!==t.url.indexOf(o,t.url.indexOf(i)+i.length)}if(e.isRegex())return e.getRegex().test(t.url);if(e.isLeftAnchor()&&e.isRightAnchor())return t.url===o;if(e.isLeftAnchor())return wt(t.url,o);if(e.isRightAnchor())return t.url.endsWith(o);if(!1===e.hasFilter())return!0;return-1!==t.url.indexOf(o)}(this,e)}serialize(e){e.pushUint32(this.mask);const t=e.getPos();e.pushUint8(0);let o=0;void 0!==this.filter&&(o|=1,this.isUnicode()?e.pushUTF8(this.filter):e.pushNetworkFilter(this.filter)),void 0!==this.hostname&&(o|=2,e.pushNetworkHostname(this.hostname)),void 0!==this.domains&&(o|=4,this.domains.serialize(e)),void 0!==this.rawLine&&(o|=8,e.pushRawNetwork(this.rawLine)),void 0!==this.denyallow&&(o|=16,this.denyallow.serialize(e)),void 0!==this.optionValue&&(o|=32,this.isCSP()?e.pushNetworkCSP(this.optionValue):this.isRedirect()?e.pushNetworkRedirect(this.optionValue):e.pushUTF8(this.optionValue)),e.setByte(t,o)}getSerializedSize(e){let t=5;return void 0!==this.filter&&(!0===this.isUnicode()?t+=de(this.filter):t+=function(e,t){return!0===t?le(ce().networkFilter.getCompressedSize(e),!1):pe(e)}(this.filter,e)),void 0!==this.hostname&&(t+=function(e,t){return!0===t?le(ce().networkHostname.getCompressedSize(e),!1):pe(e)}(this.hostname,e)),void 0!==this.domains&&(t+=this.domains.getSerializedSize()),void 0!==this.rawLine&&(t+=function(e,t){return!0===t?le(ce().networkRaw.getCompressedSize(te(e)),!1):de(e)}(this.rawLine,e)),void 0!==this.denyallow&&(t+=this.denyallow.getSerializedSize()),void 0!==this.optionValue&&(this.isCSP()?t+=function(e,t){return!0===t?le(ce().networkCSP.getCompressedSize(e),!1):pe(e)}(this.optionValue,e):this.isRedirect()?t+=function(e,t){return!0===t?le(ce().networkRedirect.getCompressedSize(e),!1):pe(e)}(this.optionValue,e):t+=de(this.optionValue)),t}toString(e){if(void 0!==this.rawLine)return this.rawLine;let t="";this.isException()&&(t+="@@"),this.isHostnameAnchor()?t+="||":this.fromHttp()!==this.fromHttps()?this.fromHttp()?t+="|http://":t+="|https://":this.isLeftAnchor()&&(t+="|"),this.hasHostname()&&(t+=this.getHostname(),t+="^"),this.isFullRegex()?t+=`/${this.getRegex().source}/`:this.isRegex()?t+=this.getRegex().source:t+=this.getFilter(),this.isRightAnchor()&&"^"!==t[t.length-1]&&(t+="|");const o=[];if(!1===this.fromAny()){const e=mt(this.getCptMask());if(mt(no)-e")),void 0!==this.denyallow&&(void 0!==this.denyallow.parts?o.push(`denyallow=${this.denyallow.parts}`):o.push("denyallow=")),this.isBadFilter()&&o.push("badfilter"),o.length>0&&(t+="function"==typeof e?`$${o.map(e).join(",")}`:`$${o.join(",")}`),t}getIdWithoutBadFilter(){return ro(this.mask&~eo.isBadFilter,this.filter,this.hostname,this.domains,this.denyallow,this.optionValue)}getId(){return void 0===this.id&&(this.id=ro(this.mask,this.filter,this.hostname,this.domains,this.denyallow,this.optionValue)),this.id}hasFilter(){return void 0!==this.filter}hasDomains(){return void 0!==this.domains}getMask(){return this.mask}getCptMask(){return this.getMask()&no}isRedirect(){return At(this.getMask(),eo.isRedirect)}isRedirectRule(){return At(this.mask,eo.isRedirectRule)}getRedirect(){var e;return null!==(e=this.optionValue)&&void 0!==e?e:""}isReplace(){return At(this.getMask(),eo.isReplace)}getHtmlModifier(){var e;return 0===(null===(e=this.optionValue)||void 0===e?void 0:e.length)?null:go(this.optionValue)}isHtmlFilteringRule(){return this.isReplace()}getRedirectResource(){const e=this.getRedirect(),t=e.lastIndexOf(":");return-1===t?e:e.slice(0,t)}getRedirectPriority(){const e=this.getRedirect(),t=e.lastIndexOf(":");return-1===t?0:Number(e.slice(t+1))}hasHostname(){return void 0!==this.hostname}getHostname(){return this.hostname||""}getFilter(){return this.filter||""}getRegex(){return void 0===this.regex&&(this.regex=void 0!==this.filter&&this.isRegex()?ao(this.filter,this.isLeftAnchor(),this.isRightAnchor(),this.isFullRegex()):fo),this.regex}getTokens(){if(dt.reset(),void 0!==this.domains&&void 0!==this.domains.hostnames&&void 0===this.domains.entities&&void 0===this.domains.notHostnames&&void 0===this.domains.notEntities&&1===this.domains.hostnames.length&&dt.push(this.domains.hostnames[0]),!1===this.isFullRegex()){if(void 0!==this.filter){const e=!this.isRightAnchor(),t=!this.isLeftAnchor();!function(e,t,o,i){const n=Math.min(e.length,2*i.remaining());let s=!1,c=0,r=0,a=ht;for(let o=0;o1&&42!==n&&42!==c&&(!1===t||0!==r)&&i.push(a>>>0)),c=n)}!1===o&&!0===s&&42!==c&&e.length-r>1&&!1===i.full()&&i.push(a>>>0)}(this.filter,t,e,dt)}void 0!==this.hostname&&St(this.hostname,!1,void 0!==this.filter&&42===this.filter.charCodeAt(0),dt)}else void 0!==this.filter&&function(e,t){let o=e.length-1,i=1,n=0;for(;i=i;o-=1){const t=e.charCodeAt(o);if(124===t)return;if(41===t||42===t||43===t||63===t||93===t||125===t||46===t&&92!==e.charCodeAt(o-1)||92===t&&Ct(n))break;n=t}if(o1&&St(e.slice(1,i),94!==e.charCodeAt(1),!0,t),oObject.prototype.hasOwnProperty.call(Io,e),To=(e,t)=>"true"===e&&!t.has("true")||!("false"===e&&!t.has("false"))&&!!t.get(e),Oo=(e,t)=>{if(0===e.length)return!1;if((e=>Eo.test(e))(e))return"!"===e[0]?!To(e.slice(1),t):To(e,t);const o=(e=>e.match(So))(e);if(!o||0===o.length)return!1;if(e.length!==o.reduce(((e,t)=>e+t.length),0))return!1;const i=[],n=[];for(const e of o)if("("===e)n.push(e);else if(")"===e){for(;0!==n.length&&"("!==n[n.length-1];)i.push(n.pop());if(0===n.length)return!1;n.pop()}else if(Fo(e)){for(;n.length&&Fo(n[n.length-1])&&Io[e]<=Io[n[n.length-1]];)i.push(n.pop());n.push(e)}else i.push(To(e,t));if("("===n[0]||")"===n[0])return!1;for(;0!==n.length;)i.push(n.pop());for(const e of i)if(!0===e||!1===e)n.push(e);else if("!"===e)n.push(!n.pop());else if(Fo(e)){const t=n.pop(),o=n.pop();"&&"===e?n.push(o&&t):n.push(o||t)}return!0===n[0]},Po=class e{static getCondition(e){return e.slice(5).replace(/\s/g,"")}static parse(t,o){return new this({condition:e.getCondition(t),filterIDs:o})}static deserialize(e){const t=e.getUTF8(),o=new Set;for(let t=0,i=e.getUint32();t2)for(;e4&&32===t.charCodeAt(0)&&32===t.charCodeAt(1)&&32===t.charCodeAt(2)&&32===t.charCodeAt(3)&&32!==t.charCodeAt(4)))break;a+=t.slice(4),e+=1}0!==a.length&&a.charCodeAt(a.length-1)<=32&&(a=a.trim());const l=Ro(a,{extendedNonSupportedTypes:!0});if(l===Co.NETWORK&&!0===t.loadNetworkFilters){const i=ko.parse(a,t.debug);null!==i?(o.push(i),r.length>0&&r[r.length-1].filterIDs.add(i.getId())):n.push({lineNumber:e,filter:a,filterType:l})}else if(l===Co.COSMETIC&&!0===t.loadCosmeticFilters){const o=Xt.parse(a,t.debug);null!==o?!0!==t.loadGenericCosmeticsFilters&&!1!==o.isGenericHide()||(i.push(o),r.length>0&&r[r.length-1].filterIDs.add(o.getId())):n.push({lineNumber:e,filter:a,filterType:Co.COSMETIC})}else if(t.loadPreprocessors){const t=_o(a);if(t===yo.BEGIF)r.length>0?r.push(new Po({condition:`(${r[r.length-1].condition})&&(${Po.getCondition(a)})`})):r.push(Po.parse(a));else if((t===yo.ENDIF||t===yo.ELSE)&&r.length>0){const e=r.pop();c.push(e),t===yo.ELSE&&r.push(new Po({condition:`!(${e.condition})`}))}else l===Co.NOT_SUPPORTED_ADGUARD&&n.push({lineNumber:e,filter:a,filterType:l})}else l===Co.NOT_SUPPORTED_ADGUARD&&n.push({lineNumber:e,filter:a,filterType:l})}return{networkFilters:o,cosmeticFilters:i,preprocessors:c.filter((e=>e.filterIDs.size>0)),notSupportedFilters:n}}(xo=Co||(Co={}))[xo.NOT_SUPPORTED=0]="NOT_SUPPORTED",xo[xo.NETWORK=1]="NETWORK",xo[xo.COSMETIC=2]="COSMETIC",xo[xo.NOT_SUPPORTED_EMPTY=100]="NOT_SUPPORTED_EMPTY",xo[xo.NOT_SUPPORTED_COMMENT=101]="NOT_SUPPORTED_COMMENT",xo[xo.NOT_SUPPORTED_ADGUARD=102]="NOT_SUPPORTED_ADGUARD";var Lo="video/flv",Bo={contentType:`${Lo};base64`,aliases:[Lo,".flv","flv"],body:"RkxWAQEAAAAJAAAAABIAALgAAAAAAAAAAgAKb25NZXRhRGF0YQgAAAAIAAhkdXJhdGlvbgAAAAAAAAAAAAAFd2lkdGgAP/AAAAAAAAAABmhlaWdodAA/8AAAAAAAAAANdmlkZW9kYXRhcmF0ZQBAaGoAAAAAAAAJZnJhbWVyYXRlAEBZAAAAAAAAAAx2aWRlb2NvZGVjaWQAQAAAAAAAAAAAB2VuY29kZXICAA1MYXZmNTcuNDEuMTAwAAhmaWxlc2l6ZQBAaoAAAAAAAAAACQAAAMM="},Uo="image/gif",No={contentType:`${Uo};base64`,aliases:[Uo,".gif","gif"],body:"R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"},Vo="text/html",jo={contentType:Vo,aliases:[Vo,".html","html",".htm","htm","noopframe","noop.html"],body:""},Mo="image/vnd.microsoft.icon",Do={contentType:`${Mo};base64`,aliases:[Mo,".ico","ico"],body:"AAABAAEAAQEAAAEAGAAwAAAAFgAAACgAAAABAAAAAgAAAAEAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAAAAA=="},Ho="image/jpeg",Wo={contentType:`${Ho};base64`,aliases:[Ho,".jpg","jpg",".jpeg","jpeg"],body:"/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k="},qo="application/javascript",Go={contentType:qo,aliases:[qo,".js","js","javascript",".jsx","jsx","typescript",".ts","ts","noop.js","noopjs"],body:""},$o="application/json",Ko={contentType:$o,aliases:[$o,".json","json"],body:"0"},Qo="audio/mpeg",Yo={contentType:`${Qo};base64`,aliases:[Qo,".mp3","mp3","noop-0.1s.mp3","noopmp3-0.1s"],body:"/+MYxAAAAANIAAAAAExBTUUzLjk4LjIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"},Xo="video/mp4",Zo={contentType:`${Xo};base64`,aliases:[Xo,".mp4","mp4",".m4a","m4a",".m4p","m4p",".m4b","m4b",".m4r","m4r",".m4v","m4v","noop-1s.mp4","noopmp4-1s"],body:"AAAAHGZ0eXBpc29tAAACAGlzb21pc28ybXA0MQAAAAhmcmVlAAAC721kYXQhEAUgpBv/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3pwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcCEQBSCkG//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADengAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAAsJtb292AAAAbG12aGQAAAAAAAAAAAAAAAAAAAPoAAAALwABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAB7HRyYWsAAABcdGtoZAAAAAMAAAAAAAAAAAAAAAIAAAAAAAAALwAAAAAAAAAAAAAAAQEAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAAC8AAAAAAAEAAAAAAWRtZGlhAAAAIG1kaGQAAAAAAAAAAAAAAAAAAKxEAAAIAFXEAAAAAAAtaGRscgAAAAAAAAAAc291bgAAAAAAAAAAAAAAAFNvdW5kSGFuZGxlcgAAAAEPbWluZgAAABBzbWhkAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAADTc3RibAAAAGdzdHNkAAAAAAAAAAEAAABXbXA0YQAAAAAAAAABAAAAAAAAAAAAAgAQAAAAAKxEAAAAAAAzZXNkcwAAAAADgICAIgACAASAgIAUQBUAAAAAAfQAAAHz+QWAgIACEhAGgICAAQIAAAAYc3R0cwAAAAAAAAABAAAAAgAABAAAAAAcc3RzYwAAAAAAAAABAAAAAQAAAAIAAAABAAAAHHN0c3oAAAAAAAAAAAAAAAIAAAFzAAABdAAAABRzdGNvAAAAAAAAAAEAAAAsAAAAYnVkdGEAAABabWV0YQAAAAAAAAAhaGRscgAAAAAAAAAAbWRpcmFwcGwAAAAAAAAAAAAAAAAtaWxzdAAAACWpdG9vAAAAHWRhdGEAAAABAAAAAExhdmY1Ni40MC4xMDE="},Jo="application/pdf",ei={contentType:`${Jo};base64`,aliases:[Jo,".pdf","pdf"],body:"JVBERi0xLgoxIDAgb2JqPDwvUGFnZXMgMiAwIFI+PmVuZG9iagoyIDAgb2JqPDwvS2lkc1szIDAgUl0vQ291bnQgMT4+ZW5kb2JqCjMgMCBvYmo8PC9QYXJlbnQgMiAwIFI+PmVuZG9iagp0cmFpbGVyIDw8L1Jvb3QgMSAwIFI+Pg=="},ti="image/png",oi={contentType:`${ti};base64`,aliases:[ti,".png","png"],body:"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg=="},ii="image/svg+xml",ni={contentType:ii,aliases:[ii,".svg","svg"],body:"https://raw.githubusercontent.com/mathiasbynens/small/master/svg.svg"},si="text/plain",ci={contentType:si,aliases:[si,".txt","txt","text","nooptext","noop.txt"],body:""},ri="audio/wav",ai={contentType:`${ri};base64`,aliases:[ri,".wav","wav"],body:"UklGRiQAAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQAAAAA="},li="video/webm",pi={contentType:`${li};base64`,aliases:[li,".webm","webm"],body:"GkXfo0AgQoaBAUL3gQFC8oEEQvOBCEKCQAR3ZWJtQoeBAkKFgQIYU4BnQI0VSalmQCgq17FAAw9CQE2AQAZ3aGFtbXlXQUAGd2hhbW15RIlACECPQAAAAAAAFlSua0AxrkAu14EBY8WBAZyBACK1nEADdW5khkAFVl9WUDglhohAA1ZQOIOBAeBABrCBCLqBCB9DtnVAIueBAKNAHIEAAIAwAQCdASoIAAgAAUAmJaQAA3AA/vz0AAA="},di="image/webp",ui={contentType:`${di};base64`,aliases:[di,".webp","webp"],body:"UklGRhIAAABXRUJQVlA4TAYAAAAvQWxvAGs="},hi="video/wmv",mi={contentType:`${hi};base64`,aliases:[hi,".wmv","wmv"],body:"MCaydY5mzxGm2QCqAGLObOUBAAAAAAAABQAAAAECodyrjEepzxGO5ADADCBTZWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABcCAAAAAAAAAIA+1d6xnQEAAAAAAAAAAMAF2QEAAAAAAAAAAAAAAAAcDAAAAAAAAAIAAACADAAAgAwAAEANAwC1A79fLqnPEY7jAMAMIFNlLgAAAAAAAAAR0tOruqnPEY7mAMAMIFNlBgAAAAAAQKTQ0gfj0hGX8ACgyV6oUGQAAAAAAAAAAQAoAFcATQAvAEUAbgBjAG8AZABpAG4AZwBTAGUAdAB0AGkAbgBnAHMAAAAAABwATABhAHYAZgA1ADcALgA0ADEALgAxADAAMAAAAJEH3Le3qc8RjuYAwAwgU2WBAAAAAAAAAMDvGbxNW88RqP0AgF9cRCsAV/sgVVvPEaj9AIBfXEQrAAAAAAAAAAAzAAAAAAAAAAEAAAAAAAEAAAABAAAAAigAKAAAAAEAAAABAAAAAQAYAE1QNDMDAAAAAAAAAAAAAAAAAAAAAAAAAEBS0YYdMdARo6QAoMkDSPZMAAAAAAAAAEFS0YYdMdARo6QAoMkDSPYBAAAAAQAKAG0AcwBtAHAAZQBnADQAdgAzAAAAAAAEAE1QNDM2JrJ1jmbPEabZAKoAYs5sMgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQ=="},Ai=(()=>{const e={};for(const t of[Bo,No,jo,Do,Wo,Go,Ko,Yo,Zo,ei,oi,ni,ci,ai,pi,ui,mi])for(const o of t.aliases)e[o]=t;return e})();function gi(e){return Ai[e]||ci}function fi(e){if(null===e)return!1;if("object"!=typeof e)return!1;const{name:t,aliases:o,body:i,contentType:n}=e;return"string"==typeof t&&(!(!Array.isArray(o)||!o.every((e=>"string"==typeof e)))&&("string"==typeof i&&"string"==typeof n))}function ki(e){if(null===e)return!1;if("object"!=typeof e)return!1;const{name:t,aliases:o,body:i,dependencies:n,executionWorld:s,requiresTrust:c}=e;return"string"==typeof t&&(!(!Array.isArray(o)||!o.every((e=>"string"==typeof e)))&&("string"==typeof i&&(!(!Array.isArray(n)||!n.every((e=>"string"==typeof e)))&&((void 0===s||"MAIN"===s||"ISOLATED"===s)&&(void 0===c||"boolean"==typeof c)))))}var bi=class e{static deserialize(t){const o=t.getASCII(),i=[],n=[];for(let e=0,o=t.getUint16();e["if (typeof scriptletGlobals === 'undefined') { var scriptletGlobals = {}; }",...t,`(${e})(...['{{1}}','{{2}}','{{3}}','{{4}}','{{5}}','{{6}}','{{7}}','{{8}}','{{9}}','{{10}}'].filter((a,i) => a !== '{{'+(i+1)+'}}').map((a) => decodeURIComponent(a)))`].join(";"))(t.body,i),this.scriptletsCache.set(t.name,o),o}getSurrogate(e){const t=this.resourcesByName.get(e.endsWith(".js")?e:`${e}.js`);if(void 0!==t&&"application/javascript"===t.contentType)return t.body}getScriptletCanonicalName(e){var t;return null===(t=this.getRawScriptlet(e))||void 0===t?void 0:t.name}getRawScriptlet(e){if(!e.endsWith(".fn"))return this.scriptletsByName.get(e.endsWith(".js")?e:`${e}.js`)}getScriptletDependencies(e){const t=new Map,o=[...e.dependencies];for(;o.length>0;){const e=o.pop();if(t.has(e))continue;const i=this.scriptletsByName.get(e);t.set(e,i.body),o.push(...i.dependencies)}return Array.from(t.values())}getSerializedSize(){let e=pe(this.checksum);e+=2;for(const{name:t,aliases:o,body:i,contentType:n}of this.resources)e+=pe(t),e+=o.reduce(((e,t)=>e+pe(t)),2),e+=de(i),e+=pe(n);e+=2;for(const{name:t,aliases:o,body:i,dependencies:n}of this.scriptlets)e+=pe(t),e+=o.reduce(((e,t)=>e+pe(t)),2),e+=de(i),e+=1,e+=1,e+=1,e+=1,e+=n.reduce(((e,t)=>e+pe(t)),2);return e}serialize(e){e.pushASCII(this.checksum),e.pushUint16(this.resources.length);for(const{name:t,aliases:o,body:i,contentType:n}of this.resources){e.pushASCII(t),e.pushUint16(o.length);for(const t of o)e.pushASCII(t);e.pushUTF8(i),e.pushASCII(n)}e.pushUint16(this.scriptlets.length);for(const{name:t,aliases:o,body:i,dependencies:n,executionWorld:s,requiresTrust:c}of this.scriptlets){e.pushASCII(t),e.pushUint16(o.length);for(const t of o)e.pushASCII(t);e.pushUTF8(i),e.pushBool(void 0!==s),e.pushBool("ISOLATED"===s),e.pushBool(void 0!==c),e.pushBool(!0===c),e.pushUint16(n.length),n.forEach((t=>e.pushASCII(t)))}}};var yi=new Uint32Array(0);function wi(e){return`(?:${e.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&")})`}function vi(e,t,o){let i=e.get(t);void 0===i&&(i=[],e.set(t,i)),i.push(o)}function _i(e,t){const o=new Map;for(const i of e)vi(o,t(i),i);return Array.from(o.values())}function Ci(e,t){const o=[],i=[];for(const n of e)t(n)?o.push(n):i.push(n);return{negative:i,positive:o}}var xi=[{description:"Remove duplicated filters by ID",fusion:e=>e[0],groupByCriteria:e=>""+e.getId(),select:()=>!0},{description:"Group idential filter with same mask but different domains in single filters",fusion:e=>{const t=[],o=new Set,i=new Set,n=new Set,s=new Set;for(const{domains:c}of e)if(void 0!==c){if(void 0!==c.parts&&t.push(c.parts),void 0!==c.hostnames)for(const e of c.hostnames)o.add(e);if(void 0!==c.entities)for(const e of c.entities)n.add(e);if(void 0!==c.notHostnames)for(const e of c.notHostnames)i.add(e);if(void 0!==c.notEntities)for(const e of c.notEntities)s.add(e)}return new ko(Object.assign({},e[0],{domains:new Nt({hostnames:0!==o.size?new Uint32Array(o).sort():void 0,entities:0!==n.size?new Uint32Array(n).sort():void 0,notHostnames:0!==i.size?new Uint32Array(i).sort():void 0,notEntities:0!==s.size?new Uint32Array(s).sort():void 0,parts:0!==t.length?t.join(","):void 0}),rawLine:void 0!==e[0].rawLine?e.map((({rawLine:e})=>e)).join(" <+> "):void 0}))},groupByCriteria:e=>{var t;return e.getHostname()+e.getFilter()+e.getMask()+(null!==(t=e.optionValue)&&void 0!==t?t:"")},select:e=>!e.isCSP()&&void 0===e.denyallow&&void 0!==e.domains},{description:"Group simple patterns, into a single filter",fusion:e=>{const t=[];for(const o of e)o.isRegex()?t.push(`(?:${o.getRegex().source})`):o.isRightAnchor()?t.push(`${wi(o.getFilter())}$`):o.isLeftAnchor()?t.push(`^${wi(o.getFilter())}`):t.push(wi(o.getFilter()));return new ko(Object.assign({},e[0],{mask:gt(e[0].mask,eo.isRegex),rawLine:void 0!==e[0].rawLine?e.map((({rawLine:e})=>e)).join(" <+> "):void 0,regex:new RegExp(t.join("|"))}))},groupByCriteria:e=>""+(e.getMask()&~eo.isRegex&~eo.isFullRegex),select:e=>void 0===e.domains&&void 0===e.denyallow&&!e.isHostnameAnchor()&&!e.isRedirect()&&!e.isCSP()}];function Si(e){return e}function Ei(e){return e}function Ii(e){const t=[];let o=e;for(const{select:e,fusion:i,groupByCriteria:n}of xi){const{positive:s,negative:c}=Ci(o,e);o=c;const r=_i(s,n);for(const e of r)e.length>1?t.push(i(e)):o.push(e[0])}for(const e of o)t.push(e);return t}function Fi(e){return e--,e|=e>>1,e|=e>>2,e|=e>>4,e|=e>>8,e|=e>>16,++e}var Ti=1;var Oi=Number.MAX_SAFE_INTEGER>>>0,Pi=class e{static deserialize(t,o,i,n){const s=t.getUint32(),c=t.getUint32(),r=t.getUint32(),a=me.fromUint8Array(t.getBytes(!0),n),l=a.getUint32ArrayView(s),p=a.getUint32ArrayView(c),d=a.pos;return a.seekZero(),new e({config:n,deserialize:o,filters:[],optimize:i}).updateInternals({bucketsIndex:p,filtersIndexStart:d,numberOfFilters:r,tokensLookupIndex:l,view:a})}constructor({deserialize:e,filters:t,optimize:o,config:i}){this.bucketsIndex=ne,this.filtersIndexStart=0,this.numberOfFilters=0,this.tokensLookupIndex=ne,this.cache=new Map,this.view=me.empty(i),this.deserializeFilter=e,this.optimize=o,this.config=i,0!==t.length&&this.update(t,void 0)}getFilters(){const e=[];if(0===this.numberOfFilters)return e;this.view.setPos(this.filtersIndexStart);for(let t=0;t!t.has(e.getId())||(r-=e.getSerializedSize(o),!1))));for(const t of e)r+=t.getSerializedSize(o),a.push(t)}else{a=e;for(const t of e)r+=t.getSerializedSize(o)}if(0===a.length)return void this.updateInternals({bucketsIndex:ne,filtersIndexStart:0,numberOfFilters:0,tokensLookupIndex:ne,view:me.empty(this.config)});!0===this.config.debug&&a.sort(((e,t)=>e.getId()-t.getId()));const l=new Uint32Array(Math.max(Fi(2*a.length),256));for(const e of a){const t=e.getTokens();s.push(t),c+=2*t.length,n+=t.length;for(const e of t){i+=e.length;for(const t of e)l[t%l.length]+=1}}r+=4*c;const p=Math.max(2,Fi(n)),d=p-1,u=[];for(let e=0;e1?this.optimize(c):c,lastRequestSeen:-1},!0===this.config.enableInMemoryCache&&this.cache.set(e,i)}if(i.lastRequestSeen!==t){i.lastRequestSeen=t;const e=i.filters;for(let t=0;t0){const o=e[t];e[t]=e[t-1],e[t-1]=o}return!1}}return!0}},Ri=new Uint8Array(4),zi=class e{static deserialize(t,o,i){const n=new e({deserialize:o,config:i,filters:[]});return n.filters=t.getBytes(),n}constructor({config:e,deserialize:t,filters:o}){this.deserialize=t,this.filters=Ri,this.config=e,0!==o.length&&this.update(o,void 0)}update(e,t){let o=this.filters.byteLength,i=[];const n=this.config.enableCompression,s=this.getFilters();if(0!==s.length)if(void 0===t||0===t.size)i=s;else for(const e of s)!1===t.has(e.getId())?i.push(e):o-=e.getSerializedSize(n);const c=i.length!==s.length,r=i.length;for(const t of e)o+=t.getSerializedSize(n),i.push(t);const a=i.length>r;if(0===i.length)this.filters=Ri;else if(!0===a||!0===c){const e=me.allocate(o,this.config);e.pushUint32(i.length),!0===this.config.debug&&i.sort(((e,t)=>e.getId()-t.getId()));for(const t of i)t.serialize(e);this.filters=e.buffer}}getSerializedSize(){return ae(this.filters,!1)}serialize(e){e.pushBytes(this.filters)}getFilters(){if(this.filters.byteLength<=4)return[];const e=[],t=me.fromUint8Array(this.filters,this.config),o=t.getUint32();for(let i=0;i(!0!==c&&!0!==o.isScriptInject()||!o.match(t,e)||(null==p?void 0:p(o))||u.push(o),!0))),!0===s&&!0===a){const o=this.getGenericRules(l);for(const i of o)!0!==i.match(t,e)||(null==p?void 0:p(i))||u.push(i)}!0===s&&!0===r&&0!==o.length&&this.classesIndex.iterMatchingFilters(yt(o),(o=>(o.match(t,e)&&!(null==p?void 0:p(o))&&u.push(o),!0))),!0===s&&!0===r&&0!==n.length&&this.idsIndex.iterMatchingFilters(yt(n),(o=>(o.match(t,e)&&!(null==p?void 0:p(o))&&u.push(o),!0))),!0===s&&!0===r&&0!==i.length&&this.hrefsIndex.iterMatchingFilters(function(e){const t=e.sort();let o=1;for(let e=1;e{return t=e,dt.reset(),Et(t,dt),dt.slice();var t})))),(o=>(o.match(t,e)&&!(null==p?void 0:p(o))&&u.push(o),!0)));const h=[];return 0!==u.length&&this.unhideIndex.iterMatchingFilters(d,(o=>(o.match(t,e)&&!(null==p?void 0:p(o))&&h.push(o),!0))),{filters:u,unhides:h}}getStylesheetsFromFilters({filters:e,extendedFilters:t},{getBaseRules:o,allowGenericHides:i,hidingStyle:n=Mt}){let s=!1===o||!1===i?"":this.getBaseStylesheet(n);0!==e.length&&(0!==s.length&&(s+="\n\n"),s+=Ui(e,n));const c=[];if(0!==t.length){const e=new Map;for(const o of t){const t=o.getSelectorAST();if(void 0!==t){const i=o.isRemove()?void 0:o.getStyleAttributeHash();void 0!==i&&e.set(o.getStyle(n),i),c.push({ast:t,remove:o.isRemove(),attribute:i})}}0!==e.size&&(0!==s.length&&(s+="\n\n"),s+=[...e.entries()].map((([e,t])=>`[${t}] { ${e} }`)).join("\n\n"))}return{stylesheet:s,extended:c}}getGenericRules(e){return null===this.extraGenericRules?this.lazyPopulateGenericRulesCache(e).genericRules:this.extraGenericRules}getBaseStylesheet(e){return null===this.baseStylesheet?this.lazyPopulateGenericRulesCache(e).baseStylesheet:this.baseStylesheet}lazyPopulateGenericRulesCache(e){if(null===this.baseStylesheet||null===this.extraGenericRules){const t=this.unhideIndex.getFilters(),o=new Set;for(const e of t)o.add(e.getSelector());const i=this.genericRules.getFilters(),n=[],s=[];for(const e of i)e.hasCustomStyle()||e.isScriptInject()||e.hasHostnameConstraint()||o.has(e.getSelector())?s.push(e):n.push(e);this.baseStylesheet=Ui(n,e),this.extraGenericRules=s}return{baseStylesheet:this.baseStylesheet,genericRules:this.extraGenericRules}}},ji=class e{static deserialize(t,o){const i=new e({config:o});return i.index=Pi.deserialize(t,ko.deserialize,o.enableOptimizations?Ii:Si,o),i.badFilters=zi.deserialize(t,ko.deserialize,o),i}constructor({filters:e=[],config:t}){this.index=new Pi({config:t,deserialize:ko.deserialize,filters:[],optimize:t.enableOptimizations?Ii:Si}),this.badFiltersIds=null,this.badFilters=new zi({config:t,deserialize:ko.deserialize,filters:[]}),0!==e.length&&this.update(e,void 0)}getFilters(){return[].concat(this.badFilters.getFilters(),this.index.getFilters())}update(e,t){const o=[],i=[];for(const t of e)t.isBadFilter()?o.push(t):i.push(t);this.badFilters.update(o,t),this.index.update(i,t),this.badFiltersIds=null}getSerializedSize(){return this.badFilters.getSerializedSize()+this.index.getSerializedSize()}serialize(e){this.index.serialize(e),this.badFilters.serialize(e)}matchAll(e,t){const o=[];return this.index.iterMatchingFilters(e.getTokens(),(i=>(i.match(e)&&!1===this.isFilterDisabled(i)&&!(null==t?void 0:t(i))&&o.push(i),!0))),o}match(e,t){let o;return this.index.iterMatchingFilters(e.getTokens(),(i=>!(i.match(e)&&!1===this.isFilterDisabled(i)&&!(null==t?void 0:t(i)))||(o=i,!1))),o}isFilterDisabled(e){if(null===this.badFiltersIds){const e=this.badFilters.getFilters();if(0===e.length)return!1;const t=new Set;for(const o of e)t.add(o.getIdWithoutBadFilter());this.badFiltersIds=t}return this.badFiltersIds.has(e.getId())}},Mi=class e{static deserialize(t,o){const i=new e({config:o});return i.networkIndex=Pi.deserialize(t,ko.deserialize,o.enableOptimizations?Ii:Si,o),i.exceptionsIndex=Pi.deserialize(t,ko.deserialize,o.enableOptimizations?Ii:Si,o),i.cosmeticIndex=Pi.deserialize(t,Xt.deserialize,Ei,o),i.unhideIndex=Pi.deserialize(t,Xt.deserialize,Ei,o),i}constructor({filters:e=[],config:t}){this.config=t,this.networkIndex=new Pi({config:t,deserialize:ko.deserialize,filters:[],optimize:t.enableOptimizations?Ii:Si}),this.exceptionsIndex=new Pi({config:t,deserialize:ko.deserialize,filters:[],optimize:t.enableOptimizations?Ii:Si}),this.cosmeticIndex=new Pi({config:t,deserialize:Xt.deserialize,filters:[],optimize:Ei}),this.unhideIndex=new Pi({config:t,deserialize:Xt.deserialize,filters:[],optimize:Ei}),0!==e.length&&this.update(e,void 0)}update(e,t){const o=[],i=[],n=[],s=[];for(const t of e)t.isNetworkFilter()?t.isException()?i.push(t):o.push(t):t.isCosmeticFilter()&&(t.isUnhide()?s.push(t):n.push(t));this.networkIndex.update(o,t),this.exceptionsIndex.update(i,t),this.cosmeticIndex.update(n,t),this.unhideIndex.update(s,t)}serialize(e){this.networkIndex.serialize(e),this.exceptionsIndex.serialize(e),this.cosmeticIndex.serialize(e),this.unhideIndex.serialize(e)}getSerializedSize(){return this.networkIndex.getSerializedSize()+this.exceptionsIndex.getSerializedSize()+this.cosmeticIndex.getSerializedSize()+this.unhideIndex.getSerializedSize()}getHTMLFilters(e,t){const o=[],i=[],n=[],s=[];if(!0===this.config.loadNetworkFilters&&this.networkIndex.iterMatchingFilters(e.getTokens(),(i=>(i.match(e)&&!(null==t?void 0:t(i))&&o.push(i),!0))),0!==o.length&&this.exceptionsIndex.iterMatchingFilters(e.getTokens(),(o=>(o.match(e)&&!(null==t?void 0:t(o))&&n.push(o),!0))),!0===this.config.loadCosmeticFilters&&e.isMainFrame()){const{hostname:o,domain:n=""}=e,c=Ni(o,n);this.cosmeticIndex.iterMatchingFilters(c,(e=>(e.match(o,n)&&!(null==t?void 0:t(e))&&i.push(e),!0))),0!==i.length&&this.unhideIndex.iterMatchingFilters(c,(e=>(e.match(o,n)&&!(null==t?void 0:t(e))&&s.push(e),!0)))}return{networkFilters:o,cosmeticFilters:i,unhides:s,exceptions:n}}getFilters(){return[].concat(this.networkIndex.getFilters(),this.exceptionsIndex.getFilters(),this.cosmeticIndex.getFilters(),this.unhideIndex.getFilters())}},Di=Number.MAX_SAFE_INTEGER>>>0,Hi=class e{static deserialize(t,o){const i=t.getUint32(),n=t.getUint32(),s=t.getUint32(),c=me.fromUint8Array(t.getBytes(!0),{enableCompression:!1}),r=c.getUint32ArrayView(i),a=c.getUint32ArrayView(n),l=c.pos;return c.seekZero(),new e({deserialize:o,values:[],getKeys:()=>[],getSerializedSize:()=>0,serialize:()=>{}}).updateInternals({bucketsIndex:a,valuesIndexStart:l,numberOfValues:s,tokensLookupIndex:r,view:c})}constructor({serialize:e,deserialize:t,getKeys:o,getSerializedSize:i,values:n}){if(this.cache=new Map,this.bucketsIndex=ne,this.tokensLookupIndex=ne,this.valuesIndexStart=0,this.numberOfValues=0,this.view=me.empty({enableCompression:!1}),this.deserializeValue=t,0!==n.length){const t=[];let s=0,c=0;for(const e of n)c+=i(e);if(0===n.length)return void this.updateInternals({bucketsIndex:ne,valuesIndexStart:0,numberOfValues:0,tokensLookupIndex:ne,view:me.empty({enableCompression:!1})});for(const e of n){const i=o(e);t.push(i),s+=2*i.length}c+=4*s;const r=Math.max(2,Fi(n.length)),a=r-1,l=[];for(let e=0;e[qi(e)],serialize:$i,deserialize:Ki,values:e})}function Yi(e){if(null===e)return!1;if("object"!=typeof e)return!1;const{key:t,name:o,description:i,country:n,website_url:s,privacy_policy_url:c,privacy_contact:r,ghostery_id:a}=e;return"string"==typeof t&&("string"==typeof o&&((null===i||"string"==typeof i)&&((null===n||"string"==typeof n)&&((null===s||"string"==typeof s)&&((null===c||"string"==typeof c)&&((null===r||"string"==typeof r)&&(null===a||"string"==typeof a)))))))}function Xi(e){return bt(e.key)}function Zi(e){return de(e.key)+de(e.name)+de(e.description||"")+de(e.website_url||"")+de(e.country||"")+de(e.privacy_policy_url||"")+de(e.privacy_contact||"")+de(e.ghostery_id||"")}function Ji(e,t){t.pushUTF8(e.key),t.pushUTF8(e.name),t.pushUTF8(e.description||""),t.pushUTF8(e.website_url||""),t.pushUTF8(e.country||""),t.pushUTF8(e.privacy_policy_url||""),t.pushUTF8(e.privacy_contact||""),t.pushUTF8(e.ghostery_id||"")}function en(e){return{key:e.getUTF8(),name:e.getUTF8(),description:e.getUTF8()||null,website_url:e.getUTF8()||null,country:e.getUTF8()||null,privacy_policy_url:e.getUTF8()||null,privacy_contact:e.getUTF8()||null,ghostery_id:e.getUTF8()||null}}function tn(e){return new Hi({getSerializedSize:Zi,getKeys:e=>[Xi(e)],serialize:Ji,deserialize:en,values:e})}function on(e){if(null===e)return!1;if("object"!=typeof e)return!1;const{key:t,name:o,category:i,organization:n,alias:s,website_url:c,domains:r,filters:a}=e;return"string"==typeof t&&("string"==typeof o&&("string"==typeof i&&((null===n||"string"==typeof n)&&(("string"==typeof s||null===s)&&((null===c||"string"==typeof c)&&(!(!Array.isArray(r)||!r.every((e=>"string"==typeof e)))&&!(!Array.isArray(a)||!a.every((e=>"string"==typeof e)))))))))}function nn(e){const t=[];for(const o of e.filters){const e=ko.parse(o);null!==e&&t.push(e.getId())}for(const o of e.domains){const e=ko.parse(`||${o}^`);null!==e&&t.push(e.getId())}return[...new Set(t)]}function sn(e){let t=re(e.domains.length);for(const o of e.domains)t+=de(o);let o=re(e.filters.length);for(const t of e.filters)o+=de(t);return de(e.key)+de(e.name)+de(e.category)+de(e.organization||"")+de(e.alias||"")+de(e.website_url||"")+de(e.ghostery_id||"")+t+o}function cn(e,t){t.pushUTF8(e.key),t.pushUTF8(e.name),t.pushUTF8(e.category),t.pushUTF8(e.organization||""),t.pushUTF8(e.alias||""),t.pushUTF8(e.website_url||""),t.pushUTF8(e.ghostery_id||""),t.pushLength(e.domains.length);for(const o of e.domains)t.pushUTF8(o);t.pushLength(e.filters.length);for(const o of e.filters)t.pushUTF8(o)}function rn(e){const t=e.getUTF8(),o=e.getUTF8(),i=e.getUTF8(),n=e.getUTF8()||null,s=e.getUTF8()||null,c=e.getUTF8()||null,r=e.getUTF8()||null,a=e.getLength(),l=[];for(let t=0;t=2;t.shift()){const e=t.join("."),o=ko.parse(`||${e}^`);if(null===o)continue;const i=this.fromId(o.getId());if(i.length>0)return i}return[]}fromId(e){var t,o;const i=[];for(const n of this.patterns.get(e))i.push({pattern:n,category:null===(t=this.categories.get(qi({key:n.category})))||void 0===t?void 0:t[0],organization:null!==n.organization?null===(o=this.organizations.get(Xi({key:n.organization})))||void 0===o?void 0:o[0]:null});return i}},pn=class{static deserialize(e){const t=new Set;for(let o=0,i=e.getUint32();ot.condition===e.condition));if(t)for(const o of e.filterIDs)t.filterIDs.delete(o)}if(e)for(const t of e){const e=this.preprocessors.find((e=>e.condition===t.condition));if(e)for(const o of t.filterIDs)e.filterIDs.add(o);else this.preprocessors.push(t)}(t&&0!==t.length||e&&0!==e.length)&&this.updateEnv(o)}serialize(e){e.pushUint32(this.excluded.size);for(const t of this.excluded)e.pushUint32(t);e.pushUint32(this.preprocessors.length);for(const t of this.preprocessors)t.serialize(e)}getSerializedSize(){let e=4*(1+this.excluded.size);e+=4;for(const t of this.preprocessors)e+=t.getSerializedSize();return e}};function dn(e){if(0===e.length)return!1;let t,o=0;for(const i of e){const e=(i.isImportant()?4:0)|(i.isException()?1:2);e>=o&&(o=e,t=i)}return void 0!==t&&t.isException()}var un=class extends ye{static fromCached(e,t){if(void 0===t)return e();const{path:o,read:i,write:n}=t;return i(o).then((e=>this.deserialize(e))).catch((()=>e().then((e=>n(o,e.serialize()).then((()=>e))))))}static empty(e={}){return new this({config:e})}static fromLists(e,t,o={},i){return this.fromCached((()=>{const i=function(e,t){return Promise.all(t.map((t=>we(e,t))))}(e,t),n=function(e){return we(e,`${ve}/ublock-origin/resources.json`)}(e);return Promise.all([i,n]).then((([e,t])=>{const i=this.parse(e.join("\n"),o);return void 0!==t&&i.updateResources(t,""+t.length),i}))}),i)}static fromPrebuiltAdsOnly(e=fetch,t){return this.fromLists(e,_e,{},t)}static fromPrebuiltAdsAndTracking(e=fetch,t){return this.fromLists(e,Ce,{},t)}static fromPrebuiltFull(e=fetch,t){return this.fromLists(e,xe,{},t)}static fromTrackerDB(e,t={}){const o=new Ae(t),i=new ln(e),n=[];for(const e of i.getPatterns())n.push(...e.filters);const s=this.parse(n.join("\n"),o);return s.metadata=i,s}static merge(e,{skipResources:t=!1}={}){if(!e||e.length<2)throw new Error("merging engines requires at least two engines");const o=e[0].config,i=new Map,n=new Map,s=new Map,c=[],r={organizations:{},categories:{},patterns:{}},a=[],l=Object.keys(o).filter((function(e){return"boolean"==typeof o[e]&&!a.includes(e)}));for(const t of e){for(const e of l)if(o[e]!==t.config[e])throw new Error(`config "${e}" of all merged engines must be the same`);const e=t.getFilters();for(const t of e.networkFilters)n.set(t.getId(),t);for(const t of e.cosmeticFilters)s.set(t.getId(),t);for(const e of t.preprocessors.preprocessors)c.push(e);for(const[e,o]of t.lists)i.has(e)||i.set(e,o);if(void 0!==t.metadata){for(const e of t.metadata.organizations.getValues())void 0===r.organizations[e.key]&&(r.organizations[e.key]=e);for(const e of t.metadata.categories.getValues())void 0===r.categories[e.key]&&(r.categories[e.key]=e);for(const e of t.metadata.patterns.getValues())void 0===r.patterns[e.key]&&(r.patterns[e.key]=e)}}const p=new this({networkFilters:Array.from(n.values()),cosmeticFilters:Array.from(s.values()),preprocessors:c,lists:i,config:o});if(Object.keys(r.categories).length+Object.keys(r.organizations).length+Object.keys(r.patterns).length!==0&&(p.metadata=new ln(r)),!0!==t){for(const t of e.slice(1))if(t.resources.checksum!==e[0].resources.checksum)throw new Error(`resource checksum of all merged engines must match with the first one: "${e[0].resources.checksum}" but got: "${t.resources.checksum}"`);p.resources=bi.copy(e[0].resources)}return p}static parse(e,t={}){const o=new Ae(t);return new this({...zo(e,o),config:o})}static deserialize(e){const t=me.fromUint8Array(e,{enableCompression:!1}),o=t.getUint16();if(699!==o)throw new Error(`serialized engine version mismatch, expected 699 but got ${o}`);const i=Ae.deserialize(t);if(i.enableCompression&&t.enableCompression(),i.integrityCheck){const o=t.pos;t.pos=e.length-4;const i=t.checksum(),n=t.getUint32();if(i!==n)throw new Error(`serialized engine checksum mismatch, expected ${n} but got ${i}`);t.pos=o}const n=new this({config:i});n.resources=bi.deserialize(t);const s=new Map,c=t.getUint16();for(let e=0;ee.getId()))).concat(o.map((e=>e.getId()))));l.push(new Po({condition:e,filterIDs:n}))}if(void 0!==t.added&&0!==t.added.length){const{networkFilters:o,cosmeticFilters:i}=zo(t.added.join("\n"),this.config),n=new Set([].concat(i.map((e=>e.getId()))).concat(o.map((e=>e.getId()))));c.push(new Po({condition:e,filterIDs:n}))}}return this.update({newCosmeticFilters:n,newNetworkFilters:s,newPreprocessors:c,removedCosmeticFilters:r.map((e=>e.getId())),removedNetworkFilters:a.map((e=>e.getId())),removedPreprocessors:l},i)}getHtmlFilters(e){const t=[];if(!1===this.config.enableHtmlFiltering)return t;const{networkFilters:o,exceptions:i,cosmeticFilters:n,unhides:s}=this.htmlFilters.getHTMLFilters(e,this.isFilterExcluded.bind(this));if(0!==n.length){const o=new Map(s.map((e=>[e.getSelector(),e])));for(const i of n){const n=i.getExtendedSelector();if(void 0===n)continue;const s=o.get(i.getSelector());void 0===s&&t.push(n),this.emit("filter-matched",{filter:i,exception:s},{request:e,filterType:Co.COSMETIC})}}if(0!==o.length){const n=new Map;let s;for(const e of i){const t=e.optionValue;if(""===t){s=e;break}n.set(t,e)}for(const i of o){const o=i.getHtmlModifier();if(null===o)continue;const c=s||n.get(i.optionValue);this.emit("filter-matched",{filter:i,exception:c},{request:e,filterType:Co.NETWORK}),void 0===c&&t.push(["replace",o])}}return 0!==t.length&&this.emit("html-filtered",t,e.url),t}getCosmeticsFilters({url:e,hostname:t,domain:o,classes:i,hrefs:n,ids:s,getBaseRules:c=!0,getInjectionRules:r=!0,getExtendedRules:a=!0,getRulesFromDOM:l=!0,getRulesFromHostname:p=!0,hidingStyle:d,callerContext:u}){if(!1===this.config.loadCosmeticFilters)return{active:!1,extended:[],scripts:[],styles:""};o||(o="");let h=!0,m=!0;const A=this.hideExceptions.matchAll(Ut.fromRawDetails({domain:o,hostname:t,url:e,sourceDomain:"",sourceHostname:"",sourceUrl:""}),this.isFilterExcluded.bind(this)),g=[],f=[];for(const e of A){if(e.isElemHide()){h=!1,m=!1;break}e.isSpecificHide()?f.push(e):e.isGenericHide()&&g.push(e)}!0===h&&(h=!1===dn(g)),!0===m&&(m=!1===dn(f));const{filters:k,unhides:b}=this.cosmetics.getCosmeticsFilters({domain:o,hostname:t,classes:i,hrefs:n,ids:s,allowGenericHides:h,allowSpecificHides:m,getRulesFromDOM:l,getRulesFromHostname:p,hidingStyle:d,isFilterExcluded:this.isFilterExcluded.bind(this)});let y=!1;const w=new Map;for(const e of b)!0===e.isScriptInject()&&!0===e.isUnhide()&&0===e.getSelector().length&&(y=!0),w.set(Yt(e,this.resources.getScriptletCanonicalName.bind(this.resources)),e);const v=[],_=[],C=[];if(0!==k.length)for(const t of k){const o=w.get(Yt(t,this.resources.getScriptletCanonicalName.bind(this.resources)));if(void 0!==o)continue;let i=!1;!0===t.isScriptInject()?!0===r&&!1===y&&(v.push(t),i=!0):t.isExtended()?!0===a&&(C.push(t),i=!0):(_.push(t),i=!0),i&&this.emit("filter-matched",{filter:t,exception:o},{url:e,callerContext:u,filterType:Co.COSMETIC})}const x=[];for(const t of v){const o=t.getScript(this.resources.getScriptlet.bind(this.resources));void 0!==o&&(this.emit("script-injected",o,e),x.push(o))}const{stylesheet:S,extended:E}=this.cosmetics.getStylesheetsFromFilters({filters:_,extendedFilters:C},{getBaseRules:c,allowGenericHides:h,hidingStyle:d});return 0!==S.length&&this.emit("style-injected",S,e),{active:!0,extended:E,scripts:x,styles:S}}matchAll(e){const t=[];return e.isSupported&&(Array.prototype.push.apply(t,this.importants.matchAll(e,this.isFilterExcluded.bind(this))),Array.prototype.push.apply(t,this.filters.matchAll(e,this.isFilterExcluded.bind(this))),Array.prototype.push.apply(t,this.exceptions.matchAll(e,this.isFilterExcluded.bind(this))),Array.prototype.push.apply(t,this.csp.matchAll(e,this.isFilterExcluded.bind(this))),Array.prototype.push.apply(t,this.hideExceptions.matchAll(e,this.isFilterExcluded.bind(this))),Array.prototype.push.apply(t,this.redirects.matchAll(e,this.isFilterExcluded.bind(this)))),new Set(t)}getCSPDirectives(e){if(!this.config.loadNetworkFilters)return;if(!0!==e.isSupported||!1===e.isMainFrame())return;const t=this.csp.matchAll(e,this.isFilterExcluded.bind(this));if(0===t.length)return;const o=new Map,i=[];for(const n of t)if(n.isException()){if(void 0===n.csp)return void this.emit("filter-matched",{exception:n},{request:e,filterType:Co.NETWORK});o.set(n.csp,n)}else i.push(n);if(0===i.length)return;const n=new Set;for(const t of i.values()){const i=o.get(t.csp);void 0===i&&n.add(t.csp),this.emit("filter-matched",{filter:t,exception:i},{request:e,filterType:Co.NETWORK})}const s=Array.from(n).join("; ");return s.length>0&&this.emit("csp-injected",e,s),s}match(e,t=!1){const o={exception:void 0,filter:void 0,match:!1,redirect:void 0,metadata:void 0};if(!this.config.loadNetworkFilters)return o;if(e.isSupported){let t,i;if(o.filter=this.importants.match(e,this.isFilterExcluded.bind(this)),void 0===o.filter){const n=this.redirects.matchAll(e,this.isFilterExcluded.bind(this)).sort(((e,t)=>t.getRedirectPriority()-e.getRedirectPriority()));if(0!==n.length)for(const e of n)"none"===e.getRedirectResource()?t=e:e.isRedirectRule()?void 0===i&&(i=e):void 0===o.filter&&(o.filter=e);void 0===o.filter&&(o.filter=this.filters.match(e,this.isFilterExcluded.bind(this)),void 0!==i&&void 0!==o.filter&&(o.filter=i)),void 0!==o.filter&&(o.exception=this.exceptions.match(e,this.isFilterExcluded.bind(this)))}void 0!==o.filter&&void 0===o.exception&&o.filter.isRedirect()&&(void 0!==t?o.exception=t:o.redirect=this.resources.getResource(o.filter.getRedirectResource()))}return o.match=void 0===o.exception&&void 0!==o.filter,o.filter&&this.emit("filter-matched",{filter:o.filter,exception:o.exception},{request:e,filterType:Co.NETWORK}),void 0!==o.exception?this.emit("request-whitelisted",e,o):void 0!==o.redirect?this.emit("request-redirected",e,o):void 0!==o.filter?this.emit("request-blocked",e,o):this.emit("request-allowed",e,o),!0===t&&void 0!==o.filter&&this.metadata&&(o.metadata=this.metadata.fromFilter(o.filter)),o}getPatternMetadata(e,{getDomainMetadata:t=!1}={}){if(void 0===this.metadata)return[];const o=new Set,i=[];for(const t of this.matchAll(e))for(const e of this.metadata.fromFilter(t))o.has(e.pattern.key)||(o.add(e.pattern.key),i.push(e));if(t)for(const t of this.metadata.fromDomain(e.hostname))o.has(t.pattern.key)||(o.add(t.pattern.key),i.push(t));return i}blockScripts(){return this.updateFromDiff({added:[Jt().scripts().redirectTo("javascript").toString()]}),this}blockImages(){return this.updateFromDiff({added:[Jt().images().redirectTo("png").toString()]}),this}blockMedias(){return this.updateFromDiff({added:[Jt().medias().redirectTo("mp4").toString()]}),this}blockFrames(){return this.updateFromDiff({added:[Jt().frames().redirectTo("html").toString()]}),this}blockFonts(){return this.updateFromDiff({added:[Jt().fonts().toString()]}),this}blockStyles(){return this.updateFromDiff({added:[Jt().styles().toString()]}),this}};function hn(e){const t=new Set(["br","head","link","meta","script","style","s"]),o=new Set,i=new Set,n=new Set,s=new Set;for(const c of e)for(const e of[c,...c.querySelectorAll("[id]:not(html):not(body),[class]:not(html):not(body),[href]:not(html):not(body)")]){if(s.has(e))continue;if(s.add(e),t.has(e.nodeName.toLowerCase()))continue;const c=e.getAttribute("id");"string"==typeof c&&n.add(c);const r=e.classList;for(const e of r)o.add(e);const a=e.getAttribute("href");"string"==typeof a&&i.add(a)}return{classes:Array.from(o),hrefs:Array.from(i),ids:Array.from(n)}}function mn(e){try{const t=pt(location.href),o=t.hostname||"",i=t.domain||"";return e.getCosmeticsFilters({url:location.href,hostname:o,domain:i,...hn([document.documentElement]),getBaseRules:!0,getInjectionRules:!1,getExtendedRules:!0,getRulesFromDOM:!0,getRulesFromHostname:!0,hidingStyle:A("opacity")}).styles}catch(e){return console.error("Error getting cosmetic rules",e),""}}function An(e){if(e){return e.replace(/\s*{[^\\}]*}\s*/g,",").replace(/,$/,"")}return""}var gn=new Uint8Array([]);var fn=[{name:"192.com",detectCmp:[{exists:".ont-cookies"}],detectPopup:[{visible:".ont-cookies"}],optIn:[{click:".ont-btn-main.ont-cookies-btn.js-ont-btn-ok2"}],optOut:[{click:".ont-cookes-btn-manage"},{click:".ont-btn-main.ont-cookies-btn.js-ont-btn-choose"}],test:[{eval:"EVAL_ONENINETWO_0"}]},{name:"1password-com",cosmetic:!0,prehideSelectors:['footer #footer-root [aria-label="Cookie Consent"]'],detectCmp:[{exists:'footer #footer-root [aria-label="Cookie Consent"]'}],detectPopup:[{visible:'footer #footer-root [aria-label="Cookie Consent"]'}],optIn:[{click:'footer #footer-root [aria-label="Cookie Consent"] button'}],optOut:[{hide:'footer #footer-root [aria-label="Cookie Consent"]'}]},{name:"aa",vendorUrl:"https://aa.com",prehideSelectors:[],cosmetic:!0,detectCmp:[{exists:"#aa_optoutmulti-Modal,#cookieBannerMessage"}],detectPopup:[{visible:"#aa_optoutmulti-Modal,#cookieBannerMessage"}],optIn:[{hide:"#aa_optoutmulti-Modal,#cookieBannerMessage"},{waitForThenClick:"#aa_optoutmulti_checkBox"},{waitForThenClick:"#aa_optoutmulti-Modal button.optoutmulti_button"}],optOut:[{hide:"#aa_optoutmulti-Modal,#cookieBannerMessage"}]},{name:"abc",vendorUrl:"https://abc.net.au",runContext:{urlPattern:"^https://([a-z0-9-]+\\.)?abc\\.net\\.au/"},prehideSelectors:[],detectCmp:[{exists:"[data-component=CookieBanner]"}],detectPopup:[{visible:"[data-component=CookieBanner] [data-component=CookieBanner_AcceptAll]"}],optIn:[{waitForThenClick:"[data-component=CookieBanner] [data-component=CookieBanner_AcceptAll]"}],optOut:[{waitForThenClick:"[data-component=CookieBanner] [data-component=CookieBanner_AcceptABCRequired]"}],test:[{eval:"EVAL_ABC_TEST"}]},{name:"abconcerts.be",vendorUrl:"https://unknown",intermediate:!1,prehideSelectors:["dialog.cookie-consent"],detectCmp:[{exists:"dialog.cookie-consent form.cookie-consent__form"}],detectPopup:[{visible:"dialog.cookie-consent form.cookie-consent__form"}],optIn:[{waitForThenClick:"dialog.cookie-consent form.cookie-consent__form button[value=yes]"}],optOut:[{if:{exists:"dialog.cookie-consent form.cookie-consent__form button[value=no]"},then:[{click:"dialog.cookie-consent form.cookie-consent__form button[value=no]"}],else:[{click:"dialog.cookie-consent form.cookie-consent__form button.cookie-consent__options-toggle"},{waitForThenClick:'dialog.cookie-consent form.cookie-consent__form button[value="save_options"]'}]}]},{name:"acris",prehideSelectors:["div.acris-cookie-consent"],detectCmp:[{exists:"[data-acris-cookie-consent]"}],detectPopup:[{visible:".acris-cookie-consent.is--modal"}],optIn:[{waitForVisible:"#ccConsentAcceptAllButton",check:"any"},{wait:500},{waitForThenClick:"#ccConsentAcceptAllButton"}],optOut:[{waitForVisible:"#ccAcceptOnlyFunctional",check:"any"},{wait:500},{waitForThenClick:"#ccAcceptOnlyFunctional"}]},{name:"activobank.pt",runContext:{urlPattern:"^https://(www\\.)?activobank\\.pt"},prehideSelectors:["aside#cookies,.overlay-cookies"],detectCmp:[{exists:"#cookies .cookies-btn"}],detectPopup:[{visible:"#cookies #submitCookies"}],optIn:[{waitForThenClick:"#cookies #submitCookies"}],optOut:[{waitForThenClick:"#cookies #rejectCookies"}]},{name:"Adroll",prehideSelectors:["#adroll_consent_container"],detectCmp:[{exists:"#adroll_consent_container"}],detectPopup:[{visible:"#adroll_consent_container"}],optIn:[{waitForThenClick:"#adroll_consent_accept"}],optOut:[{waitForThenClick:"#adroll_consent_reject"}],test:[{eval:"EVAL_ADROLL_0"}]},{name:"affinity.serif.com",detectCmp:[{exists:".c-cookie-banner button[data-qa='allow-all-cookies']"}],detectPopup:[{visible:".c-cookie-banner"}],optIn:[{click:'button[data-qa="allow-all-cookies"]'}],optOut:[{click:'button[data-qa="manage-cookies"]'},{waitFor:'.c-cookie-banner ~ [role="dialog"]'},{waitForThenClick:'.c-cookie-banner ~ [role="dialog"] input[type="checkbox"][value="true"]',all:!0},{click:'.c-cookie-banner ~ [role="dialog"] .c-modal__action button'}],test:[{wait:500},{eval:"EVAL_AFFINITY_SERIF_COM_0"}]},{name:"agolde.com",cosmetic:!0,prehideSelectors:["#modal-1 div[data-micromodal-close]"],detectCmp:[{exists:"#modal-1 div[aria-labelledby=modal-1-title]"}],detectPopup:[{exists:"#modal-1 div[data-micromodal-close]"}],optIn:[{click:'button[aria-label="Close modal"]'}],optOut:[{hide:"#modal-1 div[data-micromodal-close]"}]},{name:"aliexpress",vendorUrl:"https://aliexpress.com/",runContext:{urlPattern:"^https://.*\\.aliexpress\\.com/"},prehideSelectors:["#gdpr-new-container"],detectCmp:[{exists:"#gdpr-new-container,#voyager-gdpr > div"}],detectPopup:[{visible:"#gdpr-new-container,#voyager-gdpr > div"}],optIn:[{waitForThenClick:"#gdpr-new-container .btn-accept,#voyager-gdpr > div > div > button:nth-child(1)"}],optOut:[{if:{exists:"#voyager-gdpr > div"},then:[{waitForThenClick:"#voyager-gdpr > div > div > button:nth-child(2)"}],else:[{waitForThenClick:"#gdpr-new-container .btn-more"},{waitFor:"#gdpr-new-container .gdpr-dialog-switcher"},{click:"#gdpr-new-container .switcher-on",all:!0,optional:!0},{click:"#gdpr-new-container .btn-save"}]}]},{name:"almacmp",prehideSelectors:["#alma-cmpv2-container"],detectCmp:[{exists:"#alma-cmpv2-container"}],detectPopup:[{visible:"#alma-cmpv2-container #almacmp-modal-layer1"}],optIn:[{waitForThenClick:"#alma-cmpv2-container #almacmp-modal-layer1 #almacmp-modalConfirmBtn"}],optOut:[{waitForThenClick:"#alma-cmpv2-container #almacmp-modal-layer1 #almacmp-modalSettingBtn"},{waitFor:"#alma-cmpv2-container #almacmp-modal-layer2"},{waitForThenClick:"#alma-cmpv2-container #almacmp-modal-layer2 #almacmp-reject-all-layer2"}],test:[{eval:"EVAL_ALMACMP_0"}]},{name:"altium.com",cosmetic:!0,prehideSelectors:[".altium-privacy-bar"],detectCmp:[{exists:".altium-privacy-bar"}],detectPopup:[{exists:".altium-privacy-bar"}],optIn:[{click:"a.altium-privacy-bar__btn"}],optOut:[{hide:".altium-privacy-bar"}]},{name:"amazon.com",prehideSelectors:['span[data-action="sp-cc"][data-sp-cc*="rejectAllAction"]'],detectCmp:[{exists:'span[data-action="sp-cc"][data-sp-cc*="rejectAllAction"]'}],detectPopup:[{visible:'span[data-action="sp-cc"][data-sp-cc*="rejectAllAction"]'}],optIn:[{waitForVisible:"#sp-cc-accept"},{wait:500},{click:"#sp-cc-accept"}],optOut:[{waitForVisible:"#sp-cc-rejectall-link"},{wait:500},{click:"#sp-cc-rejectall-link"}]},{name:"amex",vendorUrl:"https://www.americanexpress.com/",cosmetic:!1,prehideSelectors:["#user-consent-management-granular-banner-overlay"],detectCmp:[{exists:"#user-consent-management-granular-banner-overlay"}],detectPopup:[{visible:"#user-consent-management-granular-banner-overlay"}],optIn:[{waitForThenClick:"[data-testid=granular-banner-button-accept-all]"}],optOut:[{waitForThenClick:"[data-testid=granular-banner-button-decline-all]"}]},{name:"aquasana.com",prehideSelectors:["#consent-tracking"],detectCmp:[{exists:"#consent-tracking"}],detectPopup:[{exists:"#consent-tracking"}],optIn:[{waitForThenClick:"#consent-tracking .affirm.btn"}],optOut:[{if:{exists:"#consent-tracking .decline.btn"},then:[{click:"#consent-tracking .decline.btn"}],else:[{hide:"#consent-tracking"}]}]},{name:"arbeitsagentur",vendorUrl:"https://www.arbeitsagentur.de/",prehideSelectors:[".modal-open bahf-cookie-disclaimer-dpl3"],detectCmp:[{exists:"bahf-cookie-disclaimer-dpl3"}],detectPopup:[{visible:"bahf-cookie-disclaimer-dpl3"}],optIn:[{waitForThenClick:["bahf-cookie-disclaimer-dpl3","#bahf-cookie-disclaimer-modal .ba-btn-primary"]}],optOut:[{waitForThenClick:["bahf-cookie-disclaimer-dpl3","#bahf-cookie-disclaimer-modal .ba-btn-contrast"]}],test:[{eval:"EVAL_ARBEITSAGENTUR_TEST"}]},{name:"asus",vendorUrl:"https://www.asus.com/",runContext:{urlPattern:"^https://www\\.asus\\.com/"},prehideSelectors:["#cookie-policy-info,#cookie-policy-info-bg"],detectCmp:[{exists:"#cookie-policy-info"}],detectPopup:[{visible:"#cookie-policy-info"}],optIn:[{waitForThenClick:'#cookie-policy-info [data-agree="Accept Cookies"]'}],optOut:[{if:{exists:"#cookie-policy-info .btn-reject"},then:[{waitForThenClick:"#cookie-policy-info .btn-reject"}],else:[{waitForThenClick:"#cookie-policy-info .btn-setting"},{waitForThenClick:'#cookie-policy-lightbox-wrapper [data-agree="Save Settings"]'}]}]},{name:"athlinks-com",runContext:{urlPattern:"^https://(www\\.)?athlinks\\.com/"},cosmetic:!0,prehideSelectors:["#footer-container ~ div"],detectCmp:[{exists:"#footer-container ~ div"}],detectPopup:[{visible:"#footer-container > div"}],optIn:[{click:"#footer-container ~ div button"}],optOut:[{hide:"#footer-container ~ div"}]},{name:"ausopen.com",cosmetic:!0,detectCmp:[{exists:".gdpr-popup__message"}],detectPopup:[{visible:".gdpr-popup__message"}],optOut:[{hide:".gdpr-popup__message"}],optIn:[{click:".gdpr-popup__message button"}]},{name:"automattic-cmp-optout",prehideSelectors:['form[class*="cookie-banner"][method="post"]'],detectCmp:[{exists:'form[class*="cookie-banner"][method="post"]'}],detectPopup:[{visible:'form[class*="cookie-banner"][method="post"]'}],optIn:[{click:'a[class*="accept-all-button"]'}],optOut:[{click:'form[class*="cookie-banner"] div[class*="simple-options"] a[class*="customize-button"]'},{waitForThenClick:"input[type=checkbox][checked]:not([disabled])",all:!0},{click:'a[class*="accept-selection-button"]'}]},{name:"aws.amazon.com",prehideSelectors:["#awsccc-cb-content","#awsccc-cs-container","#awsccc-cs-modalOverlay","#awsccc-cs-container-inner"],detectCmp:[{exists:"#awsccc-cb-content"}],detectPopup:[{visible:"#awsccc-cb-content"}],optIn:[{click:"button[data-id=awsccc-cb-btn-accept"}],optOut:[{click:"button[data-id=awsccc-cb-btn-customize]"},{waitFor:"input[aria-checked]"},{click:"input[aria-checked=true]",all:!0,optional:!0},{click:"button[data-id=awsccc-cs-btn-save]"}]},{name:"axeptio",prehideSelectors:[".axeptio_widget"],detectCmp:[{exists:".axeptio_widget"}],detectPopup:[{visible:".axeptio_widget"}],optIn:[{waitFor:".axeptio-widget--open"},{click:"button#axeptio_btn_acceptAll"}],optOut:[{waitFor:".axeptio-widget--open"},{click:"button#axeptio_btn_dismiss"}],test:[{eval:"EVAL_AXEPTIO_0"}]},{name:"baden-wuerttemberg.de",prehideSelectors:[".cookie-alert.t-dark"],cosmetic:!0,detectCmp:[{exists:".cookie-alert.t-dark"}],detectPopup:[{visible:".cookie-alert.t-dark"}],optIn:[{click:".cookie-alert__form input:not([disabled]):not([checked])"},{click:".cookie-alert__button button"}],optOut:[{hide:".cookie-alert.t-dark"}]},{name:"bahn-de",vendorUrl:"https://www.bahn.de/",cosmetic:!1,runContext:{main:!0,frame:!1,urlPattern:"^https://(www\\.)?bahn\\.de/"},intermediate:!1,prehideSelectors:[],detectCmp:[{exists:["body > div:first-child","#consent-layer"]}],detectPopup:[{visible:["body > div:first-child","#consent-layer"]}],optIn:[{waitForThenClick:["body > div:first-child","#consent-layer .js-accept-all-cookies"]}],optOut:[{waitForThenClick:["body > div:first-child","#consent-layer .js-accept-essential-cookies"]}],test:[{eval:"EVAL_BAHN_TEST"}]},{name:"bbb.org",runContext:{urlPattern:"^https://www\\.bbb\\.org/"},cosmetic:!0,prehideSelectors:['div[aria-label="use of cookies on bbb.org"]'],detectCmp:[{exists:'div[aria-label="use of cookies on bbb.org"]'}],detectPopup:[{visible:'div[aria-label="use of cookies on bbb.org"]'}],optIn:[{click:'div[aria-label="use of cookies on bbb.org"] button.bds-button-unstyled span.visually-hidden'}],optOut:[{hide:'div[aria-label="use of cookies on bbb.org"]'}]},{name:"bing.com",prehideSelectors:["#bnp_container"],detectCmp:[{exists:"#bnp_cookie_banner"}],detectPopup:[{visible:"#bnp_cookie_banner"},{visible:"#bnp_btn_accept,#bnp_btn_reject"}],optIn:[{waitForThenClick:"#bnp_btn_accept"}],optOut:[{wait:500},{waitForThenClick:"#bnp_btn_reject"}],test:[{eval:"EVAL_BING_0"}]},{name:"blocksy",vendorUrl:"https://creativethemes.com/blocksy/docs/extensions/cookies-consent/",cosmetic:!1,runContext:{main:!0,frame:!1},intermediate:!1,prehideSelectors:[".cookie-notification"],detectCmp:[{exists:"#blocksy-ext-cookies-consent-styles-css"}],detectPopup:[{visible:".cookie-notification"}],optIn:[{click:".cookie-notification .ct-cookies-decline-button"}],optOut:[{waitForThenClick:".cookie-notification .ct-cookies-decline-button"}],test:[{eval:"EVAL_BLOCKSY_0"}]},{name:"borlabs",detectCmp:[{exists:"._brlbs-block-content"}],detectPopup:[{visible:"._brlbs-bar-wrap,._brlbs-box-wrap"}],optIn:[{click:"a[data-cookie-accept-all]"}],optOut:[{click:"a[data-cookie-individual]"},{waitForVisible:".cookie-preference"},{click:"input[data-borlabs-cookie-checkbox]:checked",all:!0,optional:!0},{click:"#CookiePrefSave"},{wait:500}],prehideSelectors:["#BorlabsCookieBox"],test:[{eval:"EVAL_BORLABS_0"}]},{name:"bundesregierung.de",prehideSelectors:[".bpa-cookie-banner"],detectCmp:[{exists:".bpa-cookie-banner"}],detectPopup:[{visible:".bpa-cookie-banner .bpa-module-full-hero"}],optIn:[{click:".bpa-accept-all-button"}],optOut:[{wait:500,comment:"click is not immediately recognized"},{waitForThenClick:".bpa-close-button"}],test:[{eval:"EVAL_BUNDESREGIERUNG_DE_0"}]},{name:"burpee.com",cosmetic:!0,prehideSelectors:["#notice-cookie-block"],detectCmp:[{exists:"#notice-cookie-block"}],detectPopup:[{exists:"#html-body #notice-cookie-block"}],optIn:[{click:"#btn-cookie-allow"}],optOut:[{hide:"#html-body #notice-cookie-block, #notice-cookie"}]},{name:"canva.com",prehideSelectors:['div[role="dialog"] a[data-anchor-id="cookie-policy"]'],detectCmp:[{exists:'div[role="dialog"] a[data-anchor-id="cookie-policy"]'}],detectPopup:[{exists:'div[role="dialog"] a[data-anchor-id="cookie-policy"]'}],optIn:[{click:'div[role="dialog"] button:nth-child(1)'}],optOut:[{if:{exists:'div[role="dialog"] button:nth-child(3)'},then:[{click:'div[role="dialog"] button:nth-child(2)'}],else:[{click:'div[role="dialog"] button:nth-child(2)'},{waitFor:'div[role="dialog"] a[data-anchor-id="cookie-policy"]'},{waitFor:'div[role="dialog"] button[role=switch]'},{click:'div[role="dialog"] button:nth-child(2):not([role])'},{click:'div[role="dialog"] div:last-child button:only-child'}]}],test:[{eval:"EVAL_CANVA_0"}]},{name:"canyon.com",runContext:{urlPattern:"^https://www\\.canyon\\.com/"},prehideSelectors:["div.modal.cookiesModal.is-open"],detectCmp:[{exists:"div.modal.cookiesModal.is-open"}],detectPopup:[{visible:"div.modal.cookiesModal.is-open"}],optIn:[{click:'div.cookiesModal__buttonWrapper > button[data-closecause="close-by-submit"]'}],optOut:[{click:'div.cookiesModal__buttonWrapper > button[data-closecause="close-by-manage-cookies"]'},{waitForThenClick:"button#js-manage-data-privacy-save-button"}]},{name:"cassie",vendorUrl:"https://trustcassie.com",cosmetic:!1,runContext:{main:!0,frame:!1},prehideSelectors:[".cassie-cookie-module"],detectCmp:[{exists:".cassie-pre-banner"}],detectPopup:[{visible:"#cassie_pre_banner_text"}],optIn:[{waitForThenClick:".cassie-accept-all"}],optOut:[{waitForThenClick:".cassie-reject-all"}]},{name:"cc-banner-springer",prehideSelectors:[".cc-banner[data-cc-banner]"],detectCmp:[{exists:".cc-banner[data-cc-banner]"}],detectPopup:[{visible:".cc-banner[data-cc-banner]"}],optIn:[{waitForThenClick:".cc-banner[data-cc-banner] button[data-cc-action=accept]"}],optOut:[{if:{exists:".cc-banner[data-cc-banner] button[data-cc-action=reject]"},then:[{click:".cc-banner[data-cc-banner] button[data-cc-action=reject]"}],else:[{waitForThenClick:".cc-banner[data-cc-banner] button[data-cc-action=preferences]"},{waitFor:".cc-preferences[data-cc-preferences]"},{click:".cc-preferences[data-cc-preferences] input[type=radio][data-cc-action=toggle-category][value=off]",all:!0,optional:!0},{if:{exists:".cc-preferences[data-cc-preferences] button[data-cc-action=reject]"},then:[{click:".cc-preferences[data-cc-preferences] button[data-cc-action=reject]"}],else:[{click:".cc-preferences[data-cc-preferences] button[data-cc-action=save]"}]}]}],test:[{eval:"EVAL_CC_BANNER2_0"}]},{name:"cc_banner",cosmetic:!0,prehideSelectors:[".cc_banner-wrapper"],detectCmp:[{exists:".cc_banner-wrapper"}],detectPopup:[{visible:".cc_banner"}],optIn:[{click:".cc_btn_accept_all"}],optOut:[{hide:".cc_banner-wrapper"}]},{name:"check24-partnerprogramm-de",prehideSelectors:["[data-modal-content]:has([data-toggle-target^='cookie'])"],detectCmp:[{exists:"[data-toggle-target^='cookie']"}],detectPopup:[{visible:"[data-toggle-target^='cookie']",check:"any"}],optIn:[{waitForThenClick:"[data-cookie-accept-all]"}],optOut:[{waitForThenClick:"[data-cookie-dismiss-all]"}]},{name:"ciaopeople.it",prehideSelectors:["#cp-gdpr-choices"],detectCmp:[{exists:"#cp-gdpr-choices"}],detectPopup:[{visible:"#cp-gdpr-choices"}],optIn:[{waitForThenClick:".gdpr-btm__right > button:nth-child(2)"}],optOut:[{waitForThenClick:".gdpr-top-content > button"},{waitFor:".gdpr-top-back"},{waitForThenClick:".gdpr-btm__right > button:nth-child(1)"}],test:[{visible:"#cp-gdpr-choices",check:"none"}]},{vendorUrl:"https://www.civicuk.com/cookie-control/",name:"civic-cookie-control",prehideSelectors:["#ccc-module,#ccc-overlay,#ccc"],detectCmp:[{exists:"#ccc-module,#ccc-notify"}],detectPopup:[{visible:"#ccc"},{visible:"#ccc-module,#ccc-notify"}],optOut:[{if:{exists:"#ccc-notify"},then:[{waitForThenClick:["#ccc #ccc-notify .ccc-notify-buttons","xpath///button[contains(., 'Settings') or contains(., 'Cookie Preferences')]"]},{waitForVisible:"#ccc-module"}]},{if:{exists:"#ccc-reject-settings"},then:[{waitForThenClick:"#ccc-reject-settings"}],else:[{waitForThenClick:"#ccc-dismiss-button"}]}],optIn:[{waitForThenClick:"#ccc-recommended-settings,#ccc-notify-accept"}]},{name:"click.io",prehideSelectors:["#cl-consent"],detectCmp:[{exists:"#cl-consent"}],detectPopup:[{visible:"#cl-consent"}],optIn:[{waitForThenClick:'#cl-consent [data-role="b_agree"]'}],optOut:[{waitFor:'#cl-consent [data-role="b_options"]'},{wait:500},{click:'#cl-consent [data-role="b_options"]'},{waitFor:'.cl-consent-popup.cl-consent-visible [data-role="alloff"]'},{click:'.cl-consent-popup.cl-consent-visible [data-role="alloff"]',all:!0},{click:'[data-role="b_save"]'}],test:[{eval:"EVAL_CLICKIO_0",comment:"TODO: this only checks if we interacted at all"}]},{name:"clinch",intermediate:!1,runContext:{frame:!1,main:!0},prehideSelectors:[".consent-modal[role=dialog]"],detectCmp:[{exists:".consent-modal[role=dialog]"}],detectPopup:[{visible:".consent-modal[role=dialog]"}],optIn:[{click:"#consent_agree"}],optOut:[{if:{exists:"#consent_reject"},then:[{click:"#consent_reject"}],else:[{click:"#manage_cookie_preferences"},{click:"#cookie_consent_preferences input:checked",all:!0,optional:!0},{click:"#consent_save"}]}],test:[{eval:"EVAL_CLINCH_0"}]},{name:"clustrmaps.com",runContext:{urlPattern:"^https://(www\\.)?clustrmaps\\.com/"},cosmetic:!0,prehideSelectors:["#gdpr-cookie-message"],detectCmp:[{exists:"#gdpr-cookie-message"}],detectPopup:[{visible:"#gdpr-cookie-message"}],optIn:[{click:"button#gdpr-cookie-accept"}],optOut:[{hide:"#gdpr-cookie-message"}]},{name:"coinbase",intermediate:!1,runContext:{frame:!0,main:!0,urlPattern:"^https://(www|help)\\.coinbase\\.com"},prehideSelectors:[],detectCmp:[{exists:"div[class^=CookieBannerContent__Container]"}],detectPopup:[{visible:"div[class^=CookieBannerContent__Container]"}],optIn:[{click:"div[class^=CookieBannerContent__CTA] :nth-last-child(1)"}],optOut:[{click:"button[class^=CookieBannerContent__Settings]"},{click:"div[class^=CookiePreferencesModal__CategoryContainer] input:checked",all:!0,optional:!0},{click:"div[class^=CookiePreferencesModal__ButtonContainer] > button"}],test:[{eval:"EVAL_COINBASE_0"}]},{name:"Complianz banner",prehideSelectors:["#cmplz-cookiebanner-container"],detectCmp:[{exists:"#cmplz-cookiebanner-container .cmplz-cookiebanner"}],detectPopup:[{visible:"#cmplz-cookiebanner-container .cmplz-cookiebanner",check:"any"}],optIn:[{waitForThenClick:".cmplz-cookiebanner .cmplz-accept"}],optOut:[{waitForThenClick:".cmplz-cookiebanner .cmplz-deny"}],test:[{eval:"EVAL_COMPLIANZ_BANNER_0"}]},{name:"Complianz categories",prehideSelectors:['.cc-type-categories[aria-describedby="cookieconsent:desc"]'],detectCmp:[{exists:'.cc-type-categories[aria-describedby="cookieconsent:desc"]'}],detectPopup:[{visible:'.cc-type-categories[aria-describedby="cookieconsent:desc"]'}],optIn:[{any:[{click:".cc-accept-all"},{click:".cc-allow-all"},{click:".cc-allow"},{click:".cc-dismiss"}]}],optOut:[{if:{exists:'.cc-type-categories[aria-describedby="cookieconsent:desc"] .cc-dismiss'},then:[{click:".cc-dismiss"}],else:[{click:".cc-type-categories input[type=checkbox]:not([disabled]):checked",all:!0,optional:!0},{click:".cc-save"}]}]},{name:"Complianz notice",prehideSelectors:['.cc-type-info[aria-describedby="cookieconsent:desc"]'],cosmetic:!0,detectCmp:[{exists:'.cc-type-info[aria-describedby="cookieconsent:desc"] .cc-compliance .cc-btn'}],detectPopup:[{visible:'.cc-type-info[aria-describedby="cookieconsent:desc"] .cc-compliance .cc-btn'}],optIn:[{click:".cc-accept-all",optional:!0},{click:".cc-allow",optional:!0},{click:".cc-dismiss",optional:!0}],optOut:[{if:{exists:".cc-deny"},then:[{click:".cc-deny"}],else:[{hide:'[aria-describedby="cookieconsent:desc"]'}]}]},{name:"Complianz opt-both",prehideSelectors:['[aria-describedby="cookieconsent:desc"] .cc-type-opt-both'],detectCmp:[{exists:'[aria-describedby="cookieconsent:desc"] .cc-type-opt-both'}],detectPopup:[{visible:'[aria-describedby="cookieconsent:desc"] .cc-type-opt-both'}],optIn:[{click:".cc-accept-all",optional:!0},{click:".cc-allow",optional:!0},{click:".cc-dismiss",optional:!0}],optOut:[{waitForThenClick:".cc-deny"}]},{name:"Complianz opt-out",prehideSelectors:['[aria-describedby="cookieconsent:desc"].cc-type-opt-out'],detectCmp:[{exists:'[aria-describedby="cookieconsent:desc"].cc-type-opt-out'}],detectPopup:[{visible:'[aria-describedby="cookieconsent:desc"].cc-type-opt-out'}],optIn:[{click:".cc-accept-all",optional:!0},{click:".cc-allow",optional:!0},{click:".cc-dismiss",optional:!0}],optOut:[{if:{exists:".cc-deny"},then:[{click:".cc-deny"}],else:[{if:{exists:".cmp-pref-link"},then:[{click:".cmp-pref-link"},{waitForThenClick:".cmp-body [id*=rejectAll]"},{waitForThenClick:".cmp-body .cmp-save-btn"}]}]}]},{name:"Complianz optin",prehideSelectors:['.cc-type-opt-in[aria-describedby="cookieconsent:desc"]'],detectCmp:[{exists:'.cc-type-opt-in[aria-describedby="cookieconsent:desc"]'}],detectPopup:[{visible:'.cc-type-opt-in[aria-describedby="cookieconsent:desc"]'}],optIn:[{any:[{click:".cc-accept-all"},{click:".cc-allow"},{click:".cc-dismiss"}]}],optOut:[{if:{visible:".cc-deny"},then:[{click:".cc-deny"}],else:[{if:{visible:".cc-settings"},then:[{waitForThenClick:".cc-settings"},{waitForVisible:".cc-settings-view"},{click:".cc-settings-view input[type=checkbox]:not([disabled]):checked",all:!0,optional:!0},{click:".cc-settings-view .cc-btn-accept-selected"}],else:[{click:".cc-dismiss"}]}]}]},{name:"cookie-consent-spice",cosmetic:!1,runContext:{main:!0,frame:!1},prehideSelectors:[".spicy-consent-wrapper",".spicy-consent-bar"],detectCmp:[{exists:".spicy-consent-bar"}],detectPopup:[{visible:".spicy-consent-bar"}],optIn:[{waitForThenClick:".spicy-consent-bar__action-accept"}],optOut:[{waitForThenClick:".js-decline-all-cookies"}]},{name:"cookie-law-info",prehideSelectors:["#cookie-law-info-bar"],detectCmp:[{exists:"#cookie-law-info-bar"},{eval:"EVAL_COOKIE_LAW_INFO_DETECT"}],detectPopup:[{visible:"#cookie-law-info-bar"}],optIn:[{click:'[data-cli_action="accept_all"]'}],optOut:[{hide:"#cookie-law-info-bar"},{eval:"EVAL_COOKIE_LAW_INFO_0"}],test:[{eval:"EVAL_COOKIE_LAW_INFO_1"}]},{name:"cookie-manager-popup",cosmetic:!1,runContext:{main:!0,frame:!1},intermediate:!1,detectCmp:[{exists:"#notice-cookie-block #allow-functional-cookies, #notice-cookie-block #btn-cookie-settings"}],detectPopup:[{visible:"#notice-cookie-block"}],optIn:[{click:"#btn-cookie-allow"}],optOut:[{if:{exists:"#allow-functional-cookies"},then:[{click:"#allow-functional-cookies"}],else:[{waitForThenClick:"#btn-cookie-settings"},{waitForVisible:".modal-body"},{click:'.modal-body input:checked, .switch[data-switch="on"]',all:!0,optional:!0},{click:'[role="dialog"] .modal-footer button'}]}],prehideSelectors:["#btn-cookie-settings"],test:[{eval:"EVAL_COOKIE_MANAGER_POPUP_0"}]},{name:"cookie-notice",prehideSelectors:["#cookie-notice"],cosmetic:!0,detectCmp:[{visible:"#cookie-notice .cookie-notice-container"}],detectPopup:[{visible:"#cookie-notice"}],optIn:[{click:"#cn-accept-cookie"}],optOut:[{hide:"#cookie-notice"}]},{name:"cookie-script",vendorUrl:"https://cookie-script.com/",prehideSelectors:["#cookiescript_injected"],detectCmp:[{exists:"#cookiescript_injected"}],detectPopup:[{visible:"#cookiescript_injected"}],optOut:[{if:{exists:"#cookiescript_reject"},then:[{wait:100},{click:"#cookiescript_reject"}],else:[{click:"#cookiescript_manage"},{waitForVisible:".cookiescript_fsd_main"},{waitForThenClick:"#cookiescript_reject"}]}],optIn:[{click:"#cookiescript_accept"}]},{name:"cookieacceptbar",vendorUrl:"https://unknown",cosmetic:!0,prehideSelectors:["#cookieAcceptBar.cookieAcceptBar"],detectCmp:[{exists:"#cookieAcceptBar.cookieAcceptBar"}],detectPopup:[{visible:"#cookieAcceptBar.cookieAcceptBar"}],optIn:[{waitForThenClick:"#cookieAcceptBarConfirm"}],optOut:[{hide:"#cookieAcceptBar.cookieAcceptBar"}]},{name:"cookiealert",intermediate:!1,prehideSelectors:[],runContext:{frame:!0,main:!0},detectCmp:[{exists:".cookie-alert-extended"}],detectPopup:[{visible:".cookie-alert-extended-modal"}],optIn:[{click:"button[data-controller='cookie-alert/extended/button/accept']"},{eval:"EVAL_COOKIEALERT_0"}],optOut:[{click:"a[data-controller='cookie-alert/extended/detail-link']"},{click:".cookie-alert-configuration-input:checked",all:!0,optional:!0},{click:"button[data-controller='cookie-alert/extended/button/configuration']"},{eval:"EVAL_COOKIEALERT_0"}],test:[{eval:"EVAL_COOKIEALERT_2"}]},{name:"cookieconsent2",vendorUrl:"https://www.github.com/orestbida/cookieconsent",comment:"supports v2.x.x of the library",prehideSelectors:["#cc--main"],detectCmp:[{exists:"#cc--main"}],detectPopup:[{visible:"#cm"},{exists:"#s-all-bn"}],optIn:[{waitForThenClick:"#s-all-bn"}],optOut:[{waitForThenClick:"#s-rall-bn"}],test:[{eval:"EVAL_COOKIECONSENT2_TEST"}]},{name:"cookieconsent3",vendorUrl:"https://www.github.com/orestbida/cookieconsent",comment:"supports v3.x.x of the library",prehideSelectors:["#cc-main"],detectCmp:[{exists:"#cc-main"}],detectPopup:[{visible:"#cc-main .cm-wrapper"}],optIn:[{waitForThenClick:".cm__btn[data-role=all]"}],optOut:[{waitForThenClick:".cm__btn[data-role=necessary]"}],test:[{eval:"EVAL_COOKIECONSENT3_TEST"}]},{name:"cookiecuttr",vendorUrl:"https://github.com/cdwharton/cookieCuttr",cosmetic:!1,runContext:{main:!0,frame:!1,urlPattern:""},prehideSelectors:[".cc-cookies"],detectCmp:[{exists:".cc-cookies .cc-cookie-accept"}],detectPopup:[{visible:".cc-cookies .cc-cookie-accept"}],optIn:[{waitForThenClick:".cc-cookies .cc-cookie-accept"}],optOut:[{if:{exists:".cc-cookies .cc-cookie-decline"},then:[{click:".cc-cookies .cc-cookie-decline"}],else:[{hide:".cc-cookies"}]}]},{name:"cookiefirst.com",prehideSelectors:["#cookiefirst-root,.cookiefirst-root,[aria-labelledby=cookie-preference-panel-title]"],detectCmp:[{exists:"#cookiefirst-root,.cookiefirst-root"}],detectPopup:[{visible:"#cookiefirst-root,.cookiefirst-root"}],optIn:[{click:"button[data-cookiefirst-action=accept]"}],optOut:[{if:{exists:"button[data-cookiefirst-action=adjust]"},then:[{click:"button[data-cookiefirst-action=adjust]"},{waitForVisible:"[data-cookiefirst-widget=modal]",timeout:1e3},{eval:"EVAL_COOKIEFIRST_1"},{wait:1e3},{click:"button[data-cookiefirst-action=save]"}],else:[{click:"button[data-cookiefirst-action=reject]"}]}],test:[{eval:"EVAL_COOKIEFIRST_0"}]},{name:"Cookie Information Banner",prehideSelectors:["#cookie-information-template-wrapper"],detectCmp:[{exists:"#cookie-information-template-wrapper"}],detectPopup:[{visible:"#cookie-information-template-wrapper"}],optIn:[{eval:"EVAL_COOKIEINFORMATION_1"}],optOut:[{hide:"#cookie-information-template-wrapper",comment:"some templates don't hide the banner automatically"},{eval:"EVAL_COOKIEINFORMATION_0"}],test:[{eval:"EVAL_COOKIEINFORMATION_2"}]},{name:"cookieyes",prehideSelectors:[".cky-overlay,.cky-consent-container"],detectCmp:[{exists:".cky-consent-container"}],detectPopup:[{visible:".cky-consent-container"}],optIn:[{waitForThenClick:".cky-consent-container [data-cky-tag=accept-button]"}],optOut:[{if:{exists:".cky-consent-container [data-cky-tag=reject-button]"},then:[{waitForThenClick:".cky-consent-container [data-cky-tag=reject-button]"}],else:[{if:{exists:".cky-consent-container [data-cky-tag=settings-button]"},then:[{click:".cky-consent-container [data-cky-tag=settings-button]"},{waitFor:".cky-modal-open input[type=checkbox]"},{click:".cky-modal-open input[type=checkbox]:checked",all:!0,optional:!0},{waitForThenClick:".cky-modal [data-cky-tag=detail-save-button]"}],else:[{hide:".cky-consent-container,.cky-overlay"}]}]}],test:[{eval:"EVAL_COOKIEYES_0"}]},{name:"corona-in-zahlen.de",prehideSelectors:[".cookiealert"],detectCmp:[{exists:".cookiealert"}],detectPopup:[{visible:".cookiealert"}],optOut:[{click:".configurecookies"},{click:".confirmcookies"}],optIn:[{click:".acceptcookies"}]},{name:"crossfit-com",cosmetic:!0,prehideSelectors:['body #modal > div > div[class^="_wrapper_"]'],detectCmp:[{exists:'body #modal > div > div[class^="_wrapper_"]'}],detectPopup:[{visible:'body #modal > div > div[class^="_wrapper_"]'}],optIn:[{click:'button[aria-label="accept cookie policy"]'}],optOut:[{hide:'body #modal > div > div[class^="_wrapper_"]'}]},{name:"csu-landtag-de",runContext:{urlPattern:"^https://(www\\.|)?csu-landtag\\.de"},prehideSelectors:["#cookie-disclaimer"],detectCmp:[{exists:"#cookie-disclaimer"}],detectPopup:[{visible:"#cookie-disclaimer"}],optIn:[{click:"#cookieall"}],optOut:[{click:"#cookiesel"}]},{name:"dailymotion-us",cosmetic:!0,prehideSelectors:['div[class*="CookiePopup__desktopContainer"]:has(div[class*="CookiePopup"])'],detectCmp:[{exists:'div[class*="CookiePopup__desktopContainer"]'}],detectPopup:[{visible:'div[class*="CookiePopup__desktopContainer"]'}],optIn:[{click:'div[class*="CookiePopup__desktopContainer"] > button > span'}],optOut:[{hide:'div[class*="CookiePopup__desktopContainer"]'}]},{name:"dailymotion.com",runContext:{urlPattern:"^https://(www\\.)?dailymotion\\.com/"},prehideSelectors:['div[class*="Overlay__container"]:has(div[class*="TCF2Popup"])'],detectCmp:[{exists:'div[class*="TCF2Popup"]'}],detectPopup:[{visible:'[class*="TCF2Popup"] a[href^="https://www.dailymotion.com/legal/cookiemanagement"]'}],optIn:[{waitForThenClick:'button[class*="TCF2Popup__button"]:not([class*="TCF2Popup__personalize"])'}],optOut:[{waitForThenClick:'button[class*="TCF2ContinueWithoutAcceptingButton"]'}],test:[{eval:"EVAL_DAILYMOTION_0"}]},{name:"dan-com",vendorUrl:"https://unknown",runContext:{main:!0,frame:!1},prehideSelectors:[],detectCmp:[{exists:".cookie-banner.show .cookie-banner__content-all-btn"}],detectPopup:[{visible:".cookie-banner.show .cookie-banner__content-all-btn"}],optIn:[{waitForThenClick:".cookie-banner__content-all-btn"}],optOut:[{waitForThenClick:".cookie-banner__content-essential-btn"}]},{name:"deepl.com",prehideSelectors:[".dl_cookieBanner_container"],detectCmp:[{exists:".dl_cookieBanner_container"}],detectPopup:[{visible:".dl_cookieBanner_container"}],optOut:[{click:".dl_cookieBanner--buttonSelected"}],optIn:[{click:".dl_cookieBanner--buttonAll"}]},{name:"delta.com",runContext:{urlPattern:"^https://www\\.delta\\.com/"},cosmetic:!0,prehideSelectors:["ngc-cookie-banner"],detectCmp:[{exists:"div.cookie-footer-container"}],detectPopup:[{visible:"div.cookie-footer-container"}],optIn:[{click:" button.cookie-close-icon"}],optOut:[{hide:"div.cookie-footer-container"}]},{name:"dmgmedia-us",prehideSelectors:["#mol-ads-cmp-iframe, div.mol-ads-cmp > form > div"],detectCmp:[{exists:"div.mol-ads-cmp > form > div"}],detectPopup:[{waitForVisible:"div.mol-ads-cmp > form > div"}],optIn:[{waitForThenClick:"button.mol-ads-cmp--btn-primary"}],optOut:[{waitForThenClick:"div.mol-ads-ccpa--message > u > a"},{waitForVisible:".mol-ads-cmp--modal-dialog"},{waitForThenClick:"a.mol-ads-cmp-footer-privacy"},{waitForThenClick:"button.mol-ads-cmp--btn-secondary"}]},{name:"dmgmedia",prehideSelectors:['[data-project="mol-fe-cmp"]'],detectCmp:[{exists:'[data-project="mol-fe-cmp"] [class*=footer]'}],detectPopup:[{visible:'[data-project="mol-fe-cmp"] [class*=footer]'}],optIn:[{waitForThenClick:'[data-project="mol-fe-cmp"] button[class*=primary]'}],optOut:[{waitForThenClick:'[data-project="mol-fe-cmp"] button[class*=basic]'},{waitForVisible:'[data-project="mol-fe-cmp"] div[class*="tabContent"]'},{waitForThenClick:'[data-project="mol-fe-cmp"] div[class*="toggle"][class*="enabled"]',all:!0},{waitForThenClick:['[data-project="mol-fe-cmp"] [class*=footer]',"xpath///button[contains(., 'Save & Exit')]"]}]},{name:"dndbeyond",vendorUrl:"https://www.dndbeyond.com/",runContext:{urlPattern:"^https://(www\\.)?dndbeyond\\.com/"},prehideSelectors:["[id^=cookie-consent-banner]"],detectCmp:[{exists:"[id^=cookie-consent-banner]"}],detectPopup:[{visible:"[id^=cookie-consent-banner]"}],optIn:[{waitForThenClick:"#cookie-consent-granted"}],optOut:[{waitForThenClick:"#cookie-consent-denied"}],test:[{eval:"EVAL_DNDBEYOND_TEST"}]},{name:"dpgmedia-nl",prehideSelectors:["#pg-shadow-root-host"],detectCmp:[{exists:"#pg-shadow-root-host"}],detectPopup:[{visible:["#pg-shadow-root-host","#pg-modal"]}],optIn:[{waitForThenClick:["#pg-shadow-root-host","#pg-accept-btn"]}],optOut:[{waitForThenClick:["#pg-shadow-root-host","#pg-configure-btn"]},{waitForThenClick:["#pg-shadow-root-host","#pg-reject-btn"]}]},{name:"Drupal",detectCmp:[{exists:"#drupalorg-crosssite-gdpr"}],detectPopup:[{visible:"#drupalorg-crosssite-gdpr"}],optOut:[{click:".no"}],optIn:[{click:".yes"}]},{name:"WP DSGVO Tools",link:"https://wordpress.org/plugins/shapepress-dsgvo/",prehideSelectors:[".sp-dsgvo"],cosmetic:!0,detectCmp:[{exists:".sp-dsgvo.sp-dsgvo-popup-overlay"}],detectPopup:[{visible:".sp-dsgvo.sp-dsgvo-popup-overlay",check:"any"}],optIn:[{click:".sp-dsgvo-privacy-btn-accept-all",all:!0}],optOut:[{hide:".sp-dsgvo.sp-dsgvo-popup-overlay"}],test:[{eval:"EVAL_DSGVO_0"}]},{name:"dunelm.com",prehideSelectors:["div[data-testid=cookie-consent-modal-backdrop]"],detectCmp:[{exists:"div[data-testid=cookie-consent-message-contents]"}],detectPopup:[{visible:"div[data-testid=cookie-consent-message-contents]"}],optIn:[{click:'[data-testid="cookie-consent-allow-all"]'}],optOut:[{click:"button[data-testid=cookie-consent-adjust-settings]"},{click:"button[data-testid=cookie-consent-preferences-save]"}],test:[{eval:"EVAL_DUNELM_0"}]},{name:"ebay",vendorUrl:"https://ebay.com",cosmetic:!1,runContext:{main:!0,frame:!1,urlPattern:"^https://(www\\.)?ebay\\.([.a-z]+)/"},prehideSelectors:["#gdpr-banner"],detectCmp:[{exists:"#gdpr-banner"}],detectPopup:[{visible:"#gdpr-banner"}],optIn:[{waitForThenClick:"#gdpr-banner-accept"}],optOut:[{waitForThenClick:"#gdpr-banner-decline"}]},{name:"ecosia",vendorUrl:"https://www.ecosia.org/",runContext:{urlPattern:"^https://www\\.ecosia\\.org/"},prehideSelectors:[".cookie-wrapper"],detectCmp:[{exists:".cookie-wrapper > .cookie-notice"}],detectPopup:[{visible:".cookie-wrapper > .cookie-notice"}],optIn:[{waitForThenClick:"[data-test-id=cookie-notice-accept]"}],optOut:[{waitForThenClick:"[data-test-id=cookie-notice-reject]"}]},{name:"Ensighten ensModal",prehideSelectors:[".ensModal"],detectCmp:[{exists:".ensModal"},{visible:"#ensModalWrapper[style*=block]"}],detectPopup:[{visible:"#ensModalWrapper[style*=block]"}],optIn:[{waitForThenClick:"#modalAcceptButton"}],optOut:[{wait:500},{visible:"#ensModalWrapper[style*=block]"},{waitForThenClick:".ensCheckbox:checked",all:!0},{waitForThenClick:"#ensSave"}]},{name:"Ensighten ensNotifyBanner",prehideSelectors:["#ensNotifyBanner"],detectCmp:[{exists:"#ensNotifyBanner"}],detectPopup:[{visible:"#ensNotifyBanner[style*=block]"}],optIn:[{waitForThenClick:"#ensCloseBanner"}],optOut:[{wait:500},{visible:"#ensNotifyBanner[style*=block]"},{waitForThenClick:"#ensRejectAll,#rejectAll,#ensRejectBanner,.rejectAll,#ensCloseBanner",timeout:2e3}]},{name:"espace-personnel.agirc-arrco.fr",runContext:{urlPattern:"^https://espace-personnel\\.agirc-arrco\\.fr/"},prehideSelectors:[".cdk-overlay-container"],detectCmp:[{exists:".cdk-overlay-container app-esaa-cookie-component"}],detectPopup:[{visible:".cdk-overlay-container app-esaa-cookie-component"}],optIn:[{waitForThenClick:".btn-cookie-accepter"}],optOut:[{waitForThenClick:".btn-cookie-refuser"}]},{name:"etsy",prehideSelectors:["#gdpr-single-choice-overlay","#gdpr-privacy-settings"],detectCmp:[{exists:"#gdpr-single-choice-overlay"}],detectPopup:[{visible:"#gdpr-single-choice-overlay"}],optOut:[{click:"button[data-gdpr-open-full-settings]"},{waitForVisible:".gdpr-overlay-body input",timeout:3e3},{wait:1e3},{eval:"EVAL_ETSY_0"},{eval:"EVAL_ETSY_1"}],optIn:[{click:"button[data-gdpr-single-choice-accept]"}]},{name:"eu-cookie-compliance-banner",detectCmp:[{exists:"body.eu-cookie-compliance-popup-open"}],detectPopup:[{exists:"body.eu-cookie-compliance-popup-open"}],optIn:[{click:".agree-button"}],optOut:[{if:{visible:".decline-button,.eu-cookie-compliance-save-preferences-button"},then:[{click:".decline-button,.eu-cookie-compliance-save-preferences-button"}]},{hide:".eu-cookie-compliance-banner-info, #sliding-popup"}],test:[{eval:"EVAL_EU_COOKIE_COMPLIANCE_0"}]},{name:"EU Cookie Law",prehideSelectors:[".pea_cook_wrapper,.pea_cook_more_info_popover"],cosmetic:!0,detectCmp:[{exists:".pea_cook_wrapper"}],detectPopup:[{wait:500},{visible:".pea_cook_wrapper"}],optIn:[{click:"#pea_cook_btn"}],optOut:[{hide:".pea_cook_wrapper"}],test:[{eval:"EVAL_EU_COOKIE_LAW_0"}]},{name:"europa-eu",vendorUrl:"https://ec.europa.eu/",runContext:{urlPattern:"^https://[^/]*europa\\.eu/"},prehideSelectors:["#cookie-consent-banner"],detectCmp:[{exists:".cck-container"}],detectPopup:[{visible:".cck-container"}],optIn:[{waitForThenClick:'.cck-actions-button[href="#accept"]'}],optOut:[{waitForThenClick:'.cck-actions-button[href="#refuse"]',hide:".cck-container"}]},{name:"EZoic",prehideSelectors:["#ez-cookie-dialog-wrapper"],detectCmp:[{exists:"#ez-cookie-dialog-wrapper"}],detectPopup:[{visible:"#ez-cookie-dialog-wrapper"}],optIn:[{click:"#ez-accept-all",optional:!0},{eval:"EVAL_EZOIC_0",optional:!0}],optOut:[{wait:500},{click:"#ez-manage-settings"},{waitFor:"#ez-cookie-dialog input[type=checkbox]"},{click:"#ez-cookie-dialog input[type=checkbox]:checked",all:!0},{click:"#ez-save-settings"}],test:[{eval:"EVAL_EZOIC_1"}]},{name:"facebook",runContext:{urlPattern:"^https://([a-z0-9-]+\\.)?facebook\\.com/"},prehideSelectors:['div[data-testid="cookie-policy-manage-dialog"]'],detectCmp:[{exists:'div[data-testid="cookie-policy-manage-dialog"]'}],detectPopup:[{visible:'div[data-testid="cookie-policy-manage-dialog"]'}],optIn:[{waitForThenClick:'button[data-cookiebanner="accept_button"]'},{waitForVisible:'div[data-testid="cookie-policy-manage-dialog"]',check:"none"}],optOut:[{waitForThenClick:'button[data-cookiebanner="accept_only_essential_button"]'},{waitForVisible:'div[data-testid="cookie-policy-manage-dialog"]',check:"none"}]},{name:"fides",vendorUrl:"https://github.com/ethyca/fides",prehideSelectors:["#fides-overlay"],detectCmp:[{exists:"#fides-overlay #fides-banner"}],detectPopup:[{visible:"#fides-overlay #fides-banner"},{eval:"EVAL_FIDES_DETECT_POPUP"}],optIn:[{waitForThenClick:"#fides-banner .fides-accept-all-button"}],optOut:[{waitForThenClick:"#fides-banner .fides-reject-all-button"}]},{name:"funding-choices",prehideSelectors:[".fc-consent-root,.fc-dialog-container,.fc-dialog-overlay,.fc-dialog-content"],detectCmp:[{exists:".fc-consent-root"}],detectPopup:[{exists:".fc-dialog-container"}],optOut:[{click:".fc-cta-do-not-consent,.fc-cta-manage-options"},{click:".fc-preference-consent:checked,.fc-preference-legitimate-interest:checked",all:!0,optional:!0},{click:".fc-confirm-choices",optional:!0}],optIn:[{click:".fc-cta-consent"}]},{name:"geeks-for-geeks",runContext:{urlPattern:"^https://www\\.geeksforgeeks\\.org/"},cosmetic:!0,prehideSelectors:[".cookie-consent"],detectCmp:[{exists:".cookie-consent"}],detectPopup:[{visible:".cookie-consent"}],optIn:[{click:".cookie-consent button.consent-btn"}],optOut:[{hide:".cookie-consent"}]},{name:"google-consent-standalone",prehideSelectors:[],detectCmp:[{exists:'a[href^="https://policies.google.com/technologies/cookies"'},{exists:'form[action^="https://consent.google."][action$="/save"]'}],detectPopup:[{visible:'a[href^="https://policies.google.com/technologies/cookies"'}],optIn:[{waitForThenClick:'form[action^="https://consent.google."][action$="/save"]:has(input[name=set_eom][value=false]) button'}],optOut:[{waitForThenClick:'form[action^="https://consent.google."][action$="/save"]:has(input[name=set_eom][value=true]) button'}]},{name:"google-cookiebar",vendorUrl:"https://www.android.com/better-together/quick-share-app/",cosmetic:!1,prehideSelectors:[".glue-cookie-notification-bar"],detectCmp:[{exists:".glue-cookie-notification-bar"}],detectPopup:[{visible:".glue-cookie-notification-bar"}],optIn:[{waitForThenClick:".glue-cookie-notification-bar__accept"}],optOut:[{if:{exists:".glue-cookie-notification-bar__reject"},then:[{click:".glue-cookie-notification-bar__reject"}],else:[{hide:".glue-cookie-notification-bar"}]}],test:[]},{name:"google.com",prehideSelectors:[".HTjtHe#xe7COe"],detectCmp:[{exists:".HTjtHe#xe7COe"},{exists:'.HTjtHe#xe7COe a[href^="https://policies.google.com/technologies/cookies"]'}],detectPopup:[{visible:".HTjtHe#xe7COe button#W0wltc"}],optIn:[{waitForThenClick:".HTjtHe#xe7COe button#L2AGLb"}],optOut:[{waitForThenClick:".HTjtHe#xe7COe button#W0wltc"}],test:[{eval:"EVAL_GOOGLE_0"}]},{name:"gov.uk",detectCmp:[{exists:"#global-cookie-message"}],detectPopup:[{exists:"#global-cookie-message"}],optIn:[{click:"button[data-accept-cookies=true]"}],optOut:[{click:"button[data-reject-cookies=true],#reject-cookies"},{click:"button[data-hide-cookie-banner=true],#hide-cookie-decision"}]},{name:"hashicorp",vendorUrl:"https://hashicorp.com/",runContext:{urlPattern:"^https://[^.]*\\.hashicorp\\.com/"},prehideSelectors:["[data-testid=consent-banner]"],detectCmp:[{exists:"[data-testid=consent-banner]"}],detectPopup:[{visible:"[data-testid=consent-banner]"}],optIn:[{waitForThenClick:"[data-testid=accept]"}],optOut:[{waitForThenClick:"[data-testid=manage-preferences]"},{waitForThenClick:"[data-testid=consent-mgr-dialog] [data-ga-button=save-preferences]"}]},{name:"healthline-media",prehideSelectors:["#modal-host > div.no-hash > div.window-wrapper"],detectCmp:[{exists:"#modal-host > div.no-hash > div.window-wrapper, div[data-testid=qualtrics-container]"}],detectPopup:[{exists:"#modal-host > div.no-hash > div.window-wrapper, div[data-testid=qualtrics-container]"}],optIn:[{click:"#modal-host > div.no-hash > div.window-wrapper > div:last-child button"}],optOut:[{if:{exists:'#modal-host > div.no-hash > div.window-wrapper > div:last-child a[href="/privacy-settings"]'},then:[{click:'#modal-host > div.no-hash > div.window-wrapper > div:last-child a[href="/privacy-settings"]'}],else:[{waitForVisible:"div#__next"},{click:"#__next div:nth-child(1) > button:first-child"}]}]},{name:"hema",prehideSelectors:[".cookie-modal"],detectCmp:[{visible:".cookie-modal .cookie-accept-btn"}],detectPopup:[{visible:".cookie-modal .cookie-accept-btn"}],optIn:[{waitForThenClick:".cookie-modal .cookie-accept-btn"}],optOut:[{waitForThenClick:".cookie-modal .js-cookie-reject-btn"}],test:[{eval:"EVAL_HEMA_TEST_0"}]},{name:"hetzner.com",runContext:{urlPattern:"^https://www\\.hetzner\\.com/"},prehideSelectors:["#CookieConsent"],detectCmp:[{exists:"#CookieConsent"}],detectPopup:[{visible:"#CookieConsent"}],optIn:[{click:"#CookieConsentGiven"}],optOut:[{click:"#CookieConsentDeclined"}]},{name:"hl.co.uk",prehideSelectors:[".cookieModalContent","#cookie-banner-overlay"],detectCmp:[{exists:"#cookie-banner-overlay"}],detectPopup:[{exists:"#cookie-banner-overlay"}],optIn:[{click:"#acceptCookieButton"}],optOut:[{click:"#manageCookie"},{hide:".cookieSettingsModal"},{waitFor:"#AOCookieToggle"},{click:"#AOCookieToggle[aria-pressed=true]",optional:!0},{waitFor:"#TPCookieToggle"},{click:"#TPCookieToggle[aria-pressed=true]",optional:!0},{click:"#updateCookieButton"}]},{name:"holidaymedia",vendorUrl:"https://holidaymedia.nl/",prehideSelectors:["dialog[data-cookie-consent]"],detectCmp:[{exists:"dialog[data-cookie-consent]"}],detectPopup:[{visible:"dialog[data-cookie-consent]"}],optIn:[{waitForThenClick:"button.cookie-consent__button--accept-all"}],optOut:[{waitForThenClick:'a[data-cookie-accept="functional"]',timeout:2e3}]},{name:"hu-manity",vendorUrl:"https://hu-manity.co/",prehideSelectors:["#hu.hu-wrapper"],detectCmp:[{exists:"#hu.hu-visible"}],detectPopup:[{visible:"#hu.hu-visible"}],optIn:[{waitForThenClick:"[data-hu-action=cookies-notice-consent-choices-3]"},{waitForThenClick:"#hu-cookies-save"}],optOut:[{waitForThenClick:"#hu-cookies-save"}]},{name:"hubspot",detectCmp:[{exists:"#hs-eu-cookie-confirmation"}],detectPopup:[{visible:"#hs-eu-cookie-confirmation"}],optIn:[{click:"#hs-eu-confirmation-button"}],optOut:[{click:"#hs-eu-decline-button"}]},{name:"indeed.com",cosmetic:!0,prehideSelectors:["#CookiePrivacyNotice"],detectCmp:[{exists:"#CookiePrivacyNotice"}],detectPopup:[{visible:"#CookiePrivacyNotice"}],optIn:[{click:"#CookiePrivacyNotice button[data-gnav-element-name=CookiePrivacyNoticeOk]"}],optOut:[{hide:"#CookiePrivacyNotice"}]},{name:"ing.de",runContext:{urlPattern:"^https://www\\.ing\\.de/"},cosmetic:!0,prehideSelectors:['div[slot="backdrop"]'],detectCmp:[{exists:'[data-tag-name="ing-cc-dialog-frame"]'}],detectPopup:[{visible:'[data-tag-name="ing-cc-dialog-frame"]'}],optIn:[{click:['[data-tag-name="ing-cc-dialog-level0"]','[data-tag-name="ing-cc-button"][class*="accept"]']}],optOut:[{click:['[data-tag-name="ing-cc-dialog-level0"]','[data-tag-name="ing-cc-button"][class*="more"]']}]},{name:"instagram",vendorUrl:"https://instagram.com",runContext:{urlPattern:"^https://www\\.instagram\\.com/"},prehideSelectors:[],detectCmp:[{exists:'xpath///span[contains(., "Vill du tillåta användningen av cookies från Instagram i den här webbläsaren?") or contains(., "Allow the use of cookies from Instagram on this browser?") or contains(., "Povolit v prohlížeči použití souborů cookie z Instagramu?") or contains(., "Dopustiti upotrebu kolačića s Instagrama na ovom pregledniku?") or contains(., "Разрешить использование файлов cookie от Instagram в этом браузере?") or contains(., "Vuoi consentire l\'uso dei cookie di Instagram su questo browser?") or contains(., "Povoliť používanie cookies zo služby Instagram v tomto prehliadači?") or contains(., "Die Verwendung von Cookies durch Instagram in diesem Browser erlauben?") or contains(., "Sallitaanko Instagramin evästeiden käyttö tällä selaimella?") or contains(., "Engedélyezed az Instagram cookie-jainak használatát ebben a böngészőben?") or contains(., "Het gebruik van cookies van Instagram toestaan in deze browser?") or contains(., "Bu tarayıcıda Instagram\'dan çerez kullanımına izin verilsin mi?") or contains(., "Permitir o uso de cookies do Instagram neste navegador?") or contains(., "Permiţi folosirea modulelor cookie de la Instagram în acest browser?") or contains(., "Autoriser l’utilisation des cookies d’Instagram sur ce navigateur ?") or contains(., "¿Permitir el uso de cookies de Instagram en este navegador?") or contains(., "Zezwolić na użycie plików cookie z Instagramu w tej przeglądarce?") or contains(., "Να επιτρέπεται η χρήση cookies από τo Instagram σε αυτό το πρόγραμμα περιήγησης;") or contains(., "Разрешавате ли използването на бисквитки от Instagram на този браузър?") or contains(., "Vil du tillade brugen af cookies fra Instagram i denne browser?") or contains(., "Vil du tillate bruk av informasjonskapsler fra Instagram i denne nettleseren?")]'}],detectPopup:[{visible:'xpath///span[contains(., "Vill du tillåta användningen av cookies från Instagram i den här webbläsaren?") or contains(., "Allow the use of cookies from Instagram on this browser?") or contains(., "Povolit v prohlížeči použití souborů cookie z Instagramu?") or contains(., "Dopustiti upotrebu kolačića s Instagrama na ovom pregledniku?") or contains(., "Разрешить использование файлов cookie от Instagram в этом браузере?") or contains(., "Vuoi consentire l\'uso dei cookie di Instagram su questo browser?") or contains(., "Povoliť používanie cookies zo služby Instagram v tomto prehliadači?") or contains(., "Die Verwendung von Cookies durch Instagram in diesem Browser erlauben?") or contains(., "Sallitaanko Instagramin evästeiden käyttö tällä selaimella?") or contains(., "Engedélyezed az Instagram cookie-jainak használatát ebben a böngészőben?") or contains(., "Het gebruik van cookies van Instagram toestaan in deze browser?") or contains(., "Bu tarayıcıda Instagram\'dan çerez kullanımına izin verilsin mi?") or contains(., "Permitir o uso de cookies do Instagram neste navegador?") or contains(., "Permiţi folosirea modulelor cookie de la Instagram în acest browser?") or contains(., "Autoriser l’utilisation des cookies d’Instagram sur ce navigateur ?") or contains(., "¿Permitir el uso de cookies de Instagram en este navegador?") or contains(., "Zezwolić na użycie plików cookie z Instagramu w tej przeglądarce?") or contains(., "Να επιτρέπεται η χρήση cookies από τo Instagram σε αυτό το πρόγραμμα περιήγησης;") or contains(., "Разрешавате ли използването на бисквитки от Instagram на този браузър?") or contains(., "Vil du tillade brugen af cookies fra Instagram i denne browser?") or contains(., "Vil du tillate bruk av informasjonskapsler fra Instagram i denne nettleseren?")]'}],optIn:[{waitForThenClick:"xpath///button[contains(., 'Tillad alle cookies') or contains(., 'Alle Cookies erlauben') or contains(., 'Allow all cookies') or contains(., 'Разрешаване на всички бисквитки') or contains(., 'Tillåt alla cookies') or contains(., 'Povolit všechny soubory cookie') or contains(., 'Tüm çerezlere izin ver') or contains(., 'Permite toate modulele cookie') or contains(., 'Να επιτρέπονται όλα τα cookies') or contains(., 'Tillat alle informasjonskapsler') or contains(., 'Povoliť všetky cookies') or contains(., 'Permitir todas las cookies') or contains(., 'Permitir todos os cookies') or contains(., 'Alle cookies toestaan') or contains(., 'Salli kaikki evästeet') or contains(., 'Consenti tutti i cookie') or contains(., 'Az összes cookie engedélyezése') or contains(., 'Autoriser tous les cookies') or contains(., 'Zezwól na wszystkie pliki cookie') or contains(., 'Разрешить все cookie') or contains(., 'Dopusti sve kolačiće')]"}],optOut:[{waitForThenClick:"xpath///button[contains(., 'Отклонить необязательные файлы cookie') or contains(., 'Decline optional cookies') or contains(., 'Refuser les cookies optionnels') or contains(., 'Hylkää valinnaiset evästeet') or contains(., 'Afvis valgfrie cookies') or contains(., 'Odmietnuť nepovinné cookies') or contains(., 'Απόρριψη προαιρετικών cookies') or contains(., 'Neka valfria cookies') or contains(., 'Optionale Cookies ablehnen') or contains(., 'Rifiuta cookie facoltativi') or contains(., 'Odbij neobavezne kolačiće') or contains(., 'Avvis valgfrie informasjonskapsler') or contains(., 'İsteğe bağlı çerezleri reddet') or contains(., 'Recusar cookies opcionais') or contains(., 'Optionele cookies afwijzen') or contains(., 'Rechazar cookies opcionales') or contains(., 'Odrzuć opcjonalne pliki cookie') or contains(., 'Отхвърляне на бисквитките по избор') or contains(., 'Odmítnout volitelné soubory cookie') or contains(., 'Refuză modulele cookie opţionale') or contains(., 'A nem kötelező cookie-k elutasítása')]"},{wait:2e3}]},{name:"ionos.de",prehideSelectors:[".privacy-consent--backdrop",".privacy-consent--modal"],detectCmp:[{exists:".privacy-consent--modal"}],detectPopup:[{visible:".privacy-consent--modal"}],optIn:[{click:"#selectAll"}],optOut:[{click:".footer-config-link"},{click:"#confirmSelection"}]},{name:"itopvpn.com",cosmetic:!0,prehideSelectors:[".pop-cookie"],detectCmp:[{exists:".pop-cookie"}],detectPopup:[{exists:".pop-cookie"}],optIn:[{click:"#_pcookie"}],optOut:[{hide:".pop-cookie"}]},{name:"iubenda",prehideSelectors:["#iubenda-cs-banner"],detectCmp:[{exists:"#iubenda-cs-banner"}],detectPopup:[{visible:".iubenda-cs-accept-btn"}],optIn:[{waitForThenClick:".iubenda-cs-accept-btn"}],optOut:[{waitForThenClick:".iubenda-cs-customize-btn"},{eval:"EVAL_IUBENDA_0"},{waitForThenClick:"#iubFooterBtn"}],test:[{eval:"EVAL_IUBENDA_1"}]},{name:"iWink",prehideSelectors:["body.cookies-request #cookie-bar"],detectCmp:[{exists:"body.cookies-request #cookie-bar"}],detectPopup:[{visible:"body.cookies-request #cookie-bar"}],optIn:[{waitForThenClick:"body.cookies-request #cookie-bar .allow-cookies"}],optOut:[{waitForThenClick:"body.cookies-request #cookie-bar .disallow-cookies"}],test:[{eval:"EVAL_IWINK_TEST"}]},{name:"jdsports",vendorUrl:"https://www.jdsports.co.uk/",runContext:{urlPattern:"^https://(www|m)\\.jdsports\\."},prehideSelectors:[".miniConsent,#PrivacyPolicyBanner"],detectCmp:[{exists:".miniConsent,#PrivacyPolicyBanner"}],detectPopup:[{visible:".miniConsent,#PrivacyPolicyBanner"}],optIn:[{waitForThenClick:".miniConsent .accept-all-cookies"}],optOut:[{if:{exists:"#PrivacyPolicyBanner"},then:[{hide:"#PrivacyPolicyBanner"}],else:[{waitForThenClick:"#cookie-settings"},{waitForThenClick:"#reject-all-cookies"}]}]},{name:"johnlewis.com",prehideSelectors:["div[class^=pecr-cookie-banner-]"],detectCmp:[{exists:"div[class^=pecr-cookie-banner-]"}],detectPopup:[{exists:"div[class^=pecr-cookie-banner-]"}],optOut:[{click:"button[data-test^=manage-cookies]"},{wait:"500"},{click:"label[data-test^=toggle][class*=checked]:not([class*=disabled])",all:!0,optional:!0},{click:"button[data-test=save-preferences]"}],optIn:[{click:"button[data-test=allow-all]"}]},{name:"jquery.cookieBar",vendorUrl:"https://github.com/kovarp/jquery.cookieBar",prehideSelectors:[".cookie-bar"],cosmetic:!0,detectCmp:[{exists:".cookie-bar .cookie-bar__message,.cookie-bar .cookie-bar__buttons"}],detectPopup:[{visible:".cookie-bar .cookie-bar__message,.cookie-bar .cookie-bar__buttons",check:"any"}],optIn:[{click:".cookie-bar .cookie-bar__btn"}],optOut:[{hide:".cookie-bar"}],test:[{visible:".cookie-bar .cookie-bar__message,.cookie-bar .cookie-bar__buttons",check:"none"},{eval:"EVAL_JQUERY_COOKIEBAR_0"}]},{name:"justwatch.com",prehideSelectors:[".consent-banner"],detectCmp:[{exists:".consent-banner .consent-banner__actions"}],detectPopup:[{visible:".consent-banner .consent-banner__actions"}],optIn:[{click:".consent-banner__actions button.basic-button.primary"}],optOut:[{click:".consent-banner__actions button.basic-button.secondary"},{waitForThenClick:".consent-modal__footer button.basic-button.secondary"},{waitForThenClick:".consent-modal ion-content > div > a:nth-child(9)"},{click:"label.consent-switch input[type=checkbox]:checked",all:!0,optional:!0},{waitForVisible:".consent-modal__footer button.basic-button.primary"},{click:".consent-modal__footer button.basic-button.primary"}]},{name:"kconsent",cosmetic:!1,runContext:{main:!0,frame:!1},prehideSelectors:[".kc-overlay"],detectCmp:[{exists:"#kconsent"}],detectPopup:[{visible:".kc-dialog"}],optIn:[{waitForThenClick:"#kc-acceptAndHide"}],optOut:[{waitForThenClick:"#kc-denyAndHide"}]},{name:"ketch",vendorUrl:"https://www.ketch.com",runContext:{frame:!1,main:!0},intermediate:!1,prehideSelectors:["#lanyard_root div[role='dialog']"],detectCmp:[{exists:"#lanyard_root div[role='dialog']"}],detectPopup:[{visible:"#lanyard_root div[role='dialog']"}],optIn:[{if:{exists:"#lanyard_root button[class='confirmButton']"},then:[{waitForThenClick:"#lanyard_root div[class*=buttons] > :nth-child(2)"},{click:"#lanyard_root button[class='confirmButton']"}],else:[{waitForThenClick:"#lanyard_root div[class*=buttons] > :nth-child(2)"}]}],optOut:[{if:{exists:"#lanyard_root [aria-describedby=banner-description]"},then:[{waitForThenClick:"#lanyard_root div[class*=buttons] > button[class*=secondaryButton], #lanyard_root button[class*=buttons-secondary]",comment:"can be either settings or reject button"}]},{waitFor:"#lanyard_root [aria-describedby=preference-description],#lanyard_root [aria-describedby=modal-description], #ketch-preferences",timeout:1e3,optional:!0},{if:{exists:"#lanyard_root [aria-describedby=preference-description],#lanyard_root [aria-describedby=modal-description], #ketch-preferences"},then:[{waitForThenClick:"#lanyard_root button[class*=rejectButton], #lanyard_root button[class*=rejectAllButton]"},{click:"#lanyard_root button[class*=confirmButton],#lanyard_root div[class*=actions_] > button:nth-child(1), #lanyard_root button[class*=actionButton]"}]}],test:[{eval:"EVAL_KETCH_TEST"}]},{name:"kleinanzeigen-de",runContext:{urlPattern:"^https?://(www\\.)?kleinanzeigen\\.de"},prehideSelectors:["#gdpr-banner-container"],detectCmp:[{any:[{exists:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-cmp-button]"},{exists:"#ConsentManagementPage"}]}],detectPopup:[{any:[{visible:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-cmp-button]"},{visible:"#ConsentManagementPage"}]}],optIn:[{if:{exists:"#gdpr-banner-container #gdpr-banner"},then:[{click:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-accept]"}],else:[{click:"#ConsentManagementPage .Button-primary"}]}],optOut:[{if:{exists:"#gdpr-banner-container #gdpr-banner"},then:[{click:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-cmp-button]"}],else:[{click:"#ConsentManagementPage .Button-secondary"}]}]},{name:"lightbox",prehideSelectors:[".darken-layer.open,.lightbox.lightbox--cookie-consent"],detectCmp:[{exists:"body.cookie-consent-is-active div.lightbox--cookie-consent > div.lightbox__content > div.cookie-consent[data-jsb]"}],detectPopup:[{visible:"body.cookie-consent-is-active div.lightbox--cookie-consent > div.lightbox__content > div.cookie-consent[data-jsb]"}],optOut:[{click:".cookie-consent__footer > button[type='submit']:not([data-button='selectAll'])"}],optIn:[{click:".cookie-consent__footer > button[type='submit'][data-button='selectAll']"}]},{name:"lineagrafica",vendorUrl:"https://addons.prestashop.com/en/legal/8734-eu-cookie-law-gdpr-banner-blocker.html",cosmetic:!0,prehideSelectors:["#lgcookieslaw_banner,#lgcookieslaw_modal,.lgcookieslaw-overlay"],detectCmp:[{exists:"#lgcookieslaw_banner,#lgcookieslaw_modal,.lgcookieslaw-overlay"}],detectPopup:[{exists:"#lgcookieslaw_banner,#lgcookieslaw_modal,.lgcookieslaw-overlay"}],optIn:[{waitForThenClick:"#lgcookieslaw_accept"}],optOut:[{hide:"#lgcookieslaw_banner,#lgcookieslaw_modal,.lgcookieslaw-overlay"}]},{name:"linkedin.com",prehideSelectors:[".artdeco-global-alert[type=COOKIE_CONSENT]"],detectCmp:[{exists:".artdeco-global-alert[type=COOKIE_CONSENT]"}],detectPopup:[{visible:".artdeco-global-alert[type=COOKIE_CONSENT]"}],optIn:[{waitForVisible:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=ACCEPT]"},{wait:500},{waitForThenClick:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=ACCEPT]"}],optOut:[{waitForVisible:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=DENY]"},{wait:500},{waitForThenClick:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=DENY]"}],test:[{waitForVisible:".artdeco-global-alert[type=COOKIE_CONSENT]",check:"none"}]},{name:"livejasmin",vendorUrl:"https://www.livejasmin.com/",runContext:{urlPattern:"^https://(m|www)\\.livejasmin\\.com/"},prehideSelectors:["#consent_modal"],detectCmp:[{exists:"#consent_modal"}],detectPopup:[{visible:"#consent_modal"}],optIn:[{waitForThenClick:"#consent_modal button[data-testid=ButtonStyledButton]:first-of-type"}],optOut:[{waitForThenClick:"#consent_modal button[data-testid=ButtonStyledButton]:nth-of-type(2)"},{waitForVisible:"[data-testid=PrivacyPreferenceCenterWithConsentCookieContent]"},{click:"[data-testid=PrivacyPreferenceCenterWithConsentCookieContent] input[data-testid=PrivacyPreferenceCenterWithConsentCookieSwitch]:checked",optional:!0,all:!0},{waitForThenClick:"[data-testid=PrivacyPreferenceCenterWithConsentCookieContent] button[data-testid=ButtonStyledButton]:last-child"}]},{name:"macpaw.com",cosmetic:!0,prehideSelectors:['div[data-banner="cookies"]'],detectCmp:[{exists:'div[data-banner="cookies"]'}],detectPopup:[{exists:'div[data-banner="cookies"]'}],optIn:[{click:'button[data-banner-close="cookies"]'}],optOut:[{hide:'div[data-banner="cookies"]'}]},{name:"marksandspencer.com",cosmetic:!0,detectCmp:[{exists:".navigation-cookiebbanner"}],detectPopup:[{visible:".navigation-cookiebbanner"}],optOut:[{hide:".navigation-cookiebbanner"}],optIn:[{click:".navigation-cookiebbanner__submit"}]},{name:"mediamarkt.de",prehideSelectors:["div[aria-labelledby=pwa-consent-layer-title]","div[class^=StyledConsentLayerWrapper-]"],detectCmp:[{exists:"div[aria-labelledby^=pwa-consent-layer-title]"}],detectPopup:[{exists:"div[aria-labelledby^=pwa-consent-layer-title]"}],optOut:[{click:"button[data-test^=pwa-consent-layer-deny-all]"}],optIn:[{click:"button[data-test^=pwa-consent-layer-accept-all"}]},{name:"Mediavine",prehideSelectors:['[data-name="mediavine-gdpr-cmp"]'],detectCmp:[{exists:'[data-name="mediavine-gdpr-cmp"]'}],detectPopup:[{wait:500},{visible:'[data-name="mediavine-gdpr-cmp"]'}],optIn:[{waitForThenClick:'[data-name="mediavine-gdpr-cmp"] [format="primary"]'}],optOut:[{waitForThenClick:'[data-name="mediavine-gdpr-cmp"] [data-view="manageSettings"]'},{waitFor:'[data-name="mediavine-gdpr-cmp"] input[type=checkbox]'},{eval:"EVAL_MEDIAVINE_0",optional:!0},{click:'[data-name="mediavine-gdpr-cmp"] [format="secondary"]'}]},{name:"medium",vendorUrl:"https://medium.com",cosmetic:!0,runContext:{main:!0,frame:!1,urlPattern:"^https://([a-z0-9-]+\\.)?medium\\.com/"},prehideSelectors:[],detectCmp:[{exists:'div:has(> div > div > div[role=alert] > a[href^="https://policy.medium.com/medium-privacy-policy-"])'}],detectPopup:[{visible:'div:has(> div > div > div[role=alert] > a[href^="https://policy.medium.com/medium-privacy-policy-"])'}],optIn:[{waitForThenClick:"[data-testid=close-button]"}],optOut:[{hide:'div:has(> div > div > div[role=alert] > a[href^="https://policy.medium.com/medium-privacy-policy-"])'}]},{name:"microsoft.com",prehideSelectors:["#wcpConsentBannerCtrl"],detectCmp:[{exists:"#wcpConsentBannerCtrl"}],detectPopup:[{exists:"#wcpConsentBannerCtrl"}],optOut:[{eval:"EVAL_MICROSOFT_0"}],optIn:[{eval:"EVAL_MICROSOFT_1"}],test:[{eval:"EVAL_MICROSOFT_2"}]},{name:"midway-usa",runContext:{urlPattern:"^https://www\\.midwayusa\\.com/"},cosmetic:!0,prehideSelectors:["#cookie-container"],detectCmp:[{exists:['div[aria-label="Cookie Policy Banner"]']}],detectPopup:[{visible:"#cookie-container"}],optIn:[{click:"button#cookie-btn"}],optOut:[{hide:'div[aria-label="Cookie Policy Banner"]'}]},{name:"moneysavingexpert.com",detectCmp:[{exists:"dialog[data-testid=accept-our-cookies-dialog]"}],detectPopup:[{visible:"dialog[data-testid=accept-our-cookies-dialog]"}],optIn:[{click:"#banner-accept"}],optOut:[{click:"#banner-manage"},{click:"#pc-confirm"}]},{name:"monzo.com",prehideSelectors:[".cookie-alert, cookie-alert__content"],detectCmp:[{exists:'div.cookie-alert[role="dialog"]'},{exists:'a[href*="monzo"]'}],detectPopup:[{visible:".cookie-alert__content"}],optIn:[{click:".js-accept-cookie-policy"}],optOut:[{click:".js-decline-cookie-policy"}]},{name:"Moove",prehideSelectors:["#moove_gdpr_cookie_info_bar"],detectCmp:[{exists:"#moove_gdpr_cookie_info_bar"}],detectPopup:[{visible:"#moove_gdpr_cookie_info_bar:not(.moove-gdpr-info-bar-hidden)"}],optIn:[{waitForThenClick:".moove-gdpr-infobar-allow-all"}],optOut:[{if:{exists:"#moove_gdpr_cookie_info_bar .change-settings-button"},then:[{click:"#moove_gdpr_cookie_info_bar .change-settings-button"},{waitForVisible:"#moove_gdpr_cookie_modal"},{eval:"EVAL_MOOVE_0"},{click:".moove-gdpr-modal-save-settings"}],else:[{hide:"#moove_gdpr_cookie_info_bar"}]}],test:[{visible:"#moove_gdpr_cookie_info_bar",check:"none"}]},{name:"national-lottery.co.uk",detectCmp:[{exists:".cuk_cookie_consent"}],detectPopup:[{visible:".cuk_cookie_consent",check:"any"}],optOut:[{click:".cuk_cookie_consent_manage_pref"},{click:".cuk_cookie_consent_save_pref"},{click:".cuk_cookie_consent_close"}],optIn:[{click:".cuk_cookie_consent_accept_all"}]},{name:"nba.com",runContext:{urlPattern:"^https://(www\\.)?nba.com/"},cosmetic:!0,prehideSelectors:["#onetrust-banner-sdk"],detectCmp:[{exists:"#onetrust-banner-sdk"}],detectPopup:[{visible:"#onetrust-banner-sdk"}],optIn:[{click:"#onetrust-accept-btn-handler"}],optOut:[{hide:"#onetrust-banner-sdk"}]},{name:"netbeat.de",runContext:{urlPattern:"^https://(www\\.)?netbeat\\.de/"},prehideSelectors:["div#cookieWarning"],detectCmp:[{exists:"div#cookieWarning"}],detectPopup:[{visible:"div#cookieWarning"}],optIn:[{waitForThenClick:"a#btnCookiesAcceptAll"}],optOut:[{waitForThenClick:"a#btnCookiesDenyAll"}]},{name:"netflix.de",detectCmp:[{exists:"#cookie-disclosure"}],detectPopup:[{visible:".cookie-disclosure-message",check:"any"}],optIn:[{click:".btn-accept"}],optOut:[{hide:"#cookie-disclosure"},{click:".btn-reject"}]},{name:"nhs.uk",prehideSelectors:["#nhsuk-cookie-banner"],detectCmp:[{exists:"#nhsuk-cookie-banner"}],detectPopup:[{exists:"#nhsuk-cookie-banner"}],optOut:[{click:"#nhsuk-cookie-banner__link_accept"}],optIn:[{click:"#nhsuk-cookie-banner__link_accept_analytics"}]},{name:"nike",vendorUrl:"https://nike.com",runContext:{urlPattern:"^https://(www\\.)?nike\\.com/"},prehideSelectors:[],detectCmp:[{exists:"[data-testid=cookie-dialog-root]"}],detectPopup:[{visible:"[data-testid=cookie-dialog-root]"}],optIn:[{waitForThenClick:"[data-testid=dialog-accept-button]"}],optOut:[{waitForThenClick:"input[type=radio][id$=-declineLabel]",all:!0},{waitForThenClick:"[data-testid=confirm-choice-button]"}]},{name:"notice-cookie",prehideSelectors:[".button--notice"],cosmetic:!0,detectCmp:[{exists:".notice--cookie"}],detectPopup:[{visible:".notice--cookie"}],optIn:[{click:".button--notice"}],optOut:[{hide:".notice--cookie"}]},{name:"nrk.no",cosmetic:!0,prehideSelectors:[".nrk-masthead__info-banner--cookie"],detectCmp:[{exists:".nrk-masthead__info-banner--cookie"}],detectPopup:[{exists:".nrk-masthead__info-banner--cookie"}],optIn:[{click:"div.nrk-masthead__info-banner--cookie button > span:has(+ svg.nrk-close)"}],optOut:[{hide:".nrk-masthead__info-banner--cookie"}]},{name:"obi.de",prehideSelectors:[".disc-cp--active"],detectCmp:[{exists:".disc-cp-modal__modal"}],detectPopup:[{visible:".disc-cp-modal__modal"}],optIn:[{click:".js-disc-cp-accept-all"}],optOut:[{click:".js-disc-cp-deny-all"}]},{name:"om",vendorUrl:"https://olli-machts.de/en/extension/cookie-manager",prehideSelectors:[".tx-om-cookie-consent"],detectCmp:[{exists:".tx-om-cookie-consent .active[data-omcookie-panel]"}],detectPopup:[{exists:".tx-om-cookie-consent .active[data-omcookie-panel]"}],optIn:[{waitForThenClick:"[data-omcookie-panel-save=all]"}],optOut:[{if:{exists:"[data-omcookie-panel-save=min]"},then:[{waitForThenClick:"[data-omcookie-panel-save=min]"}],else:[{click:"input[data-omcookie-panel-grp]:checked:not(:disabled)",all:!0,optional:!0},{waitForThenClick:"[data-omcookie-panel-save=save]"}]}]},{name:"onlyFans.com",runContext:{urlPattern:"^https://onlyfans\\.com/"},prehideSelectors:["div.b-cookies-informer"],detectCmp:[{exists:"div.b-cookies-informer"}],detectPopup:[{exists:"div.b-cookies-informer"}],optIn:[{click:"div.b-cookies-informer__nav > button:nth-child(2)"}],optOut:[{click:"div.b-cookies-informer__nav > button:nth-child(1)"},{if:{exists:"div.b-cookies-informer__switchers"},then:[{click:"div.b-cookies-informer__switchers input:not([disabled])",all:!0},{click:"div.b-cookies-informer__nav > button"}]}]},{name:"openai",vendorUrl:"https://platform.openai.com/",cosmetic:!1,runContext:{urlPattern:"^https://([a-z0-9-]+\\.)?openai\\.com/"},prehideSelectors:["[data-testid=cookie-consent-banner]"],detectCmp:[{exists:"[data-testid=cookie-consent-banner]"}],detectPopup:[{visible:"[data-testid=cookie-consent-banner]"}],optIn:[{waitForThenClick:"xpath///button[contains(., 'Accept all')]"}],optOut:[{waitForThenClick:"xpath///button[contains(., 'Reject all')]"}],test:[{wait:500},{eval:"EVAL_OPENAI_TEST"}]},{name:"openli",vendorUrl:"https://openli.com",prehideSelectors:[".legalmonster-cleanslate"],detectCmp:[{exists:".legalmonster-cleanslate"}],detectPopup:[{visible:".legalmonster-cleanslate #lm-cookie-wall-container",check:"any"}],optIn:[{waitForThenClick:"#lm-accept-all"}],optOut:[{waitForThenClick:"#lm-accept-necessary"}]},{name:"opera.com",vendorUrl:"https://unknown",cosmetic:!1,runContext:{main:!0,frame:!1},intermediate:!1,prehideSelectors:[],detectCmp:[{exists:"#cookie-consent .manage-cookies__btn"}],detectPopup:[{visible:"#cookie-consent .cookie-basic-consent__btn"}],optIn:[{waitForThenClick:"#cookie-consent .cookie-basic-consent__btn"}],optOut:[{waitForThenClick:"#cookie-consent .manage-cookies__btn"},{waitForThenClick:"#cookie-consent .active.marketing_option_switch.cookie-consent__switch",all:!0},{waitForThenClick:"#cookie-consent .cookie-selection__btn"}],test:[{eval:"EVAL_OPERA_0"}]},{name:"osano",prehideSelectors:[".osano-cm-window,.osano-cm-dialog"],detectCmp:[{exists:".osano-cm-window"}],detectPopup:[{visible:".osano-cm-dialog"}],optIn:[{click:".osano-cm-accept-all",optional:!0}],optOut:[{waitForThenClick:".osano-cm-denyAll"}]},{name:"otto.de",prehideSelectors:[".cookieBanner--visibility"],detectCmp:[{exists:".cookieBanner--visibility"}],detectPopup:[{visible:".cookieBanner__wrapper"}],optIn:[{click:".js_cookieBannerPermissionButton"}],optOut:[{click:".js_cookieBannerProhibitionButton"}]},{name:"ourworldindata",vendorUrl:"https://ourworldindata.org/",runContext:{urlPattern:"^https://ourworldindata\\.org/"},prehideSelectors:[".cookie-manager"],detectCmp:[{exists:".cookie-manager"}],detectPopup:[{visible:".cookie-manager .cookie-notice.open"}],optIn:[{waitForThenClick:".cookie-notice [data-test=accept]"}],optOut:[{waitForThenClick:".cookie-notice [data-test=reject]"}]},{name:"pabcogypsum",vendorUrl:"https://unknown",prehideSelectors:[".js-cookie-notice:has(#cookie_settings-form)"],detectCmp:[{exists:".js-cookie-notice #cookie_settings-form"}],detectPopup:[{visible:".js-cookie-notice #cookie_settings-form"}],optIn:[{waitForThenClick:".js-cookie-notice button[value=allow]"}],optOut:[{waitForThenClick:".js-cookie-notice button[value=disable]"}]},{name:"paypal-us",prehideSelectors:["#ccpaCookieContent_wrapper, article.ppvx_modal--overpanel"],detectCmp:[{exists:"#ccpaCookieBanner, .privacy-sheet-content"}],detectPopup:[{visible:"#ccpaCookieBanner, .privacy-sheet-content"}],optIn:[{click:"#acceptAllButton"}],optOut:[{if:{exists:"#bannerDeclineButton"},then:[{click:"#bannerDeclineButton"}],else:[{if:{exists:"a#manageCookiesLink"},then:[{click:"a#manageCookiesLink"}],else:[{waitForVisible:".privacy-sheet-content #formContent"},{click:"#formContent .cookiepref-11m2iee-checkbox_base input:checked",all:!0,optional:!0},{click:".cookieAction.saveCookie,.confirmCookie #submitCookiesBtn"}]}]}]},{name:"paypal.com",prehideSelectors:["#gdprCookieBanner"],detectCmp:[{exists:"#gdprCookieBanner"}],detectPopup:[{visible:"#gdprCookieContent_wrapper"}],optIn:[{click:"#acceptAllButton"}],optOut:[{wait:200},{click:".gdprCookieBanner_decline-button"}],test:[{wait:500},{eval:"EVAL_PAYPAL_0"}]},{name:"pinetools.com",cosmetic:!0,prehideSelectors:["#aviso_cookies"],detectCmp:[{exists:"#aviso_cookies"}],detectPopup:[{exists:".lang_en #aviso_cookies"}],optIn:[{click:"#aviso_cookies .a_boton_cerrar"}],optOut:[{hide:"#aviso_cookies"}]},{name:"pinterest-business",vendorUrl:"https://business.pinterest.com/",runContext:{urlPattern:"^https://.*\\.pinterest\\.com/"},prehideSelectors:[".BusinessCookieConsent"],detectCmp:[{exists:".BusinessCookieConsent"}],detectPopup:[{visible:".BusinessCookieConsent [data-id=cookie-consent-banner-buttons]"}],optIn:[{waitForThenClick:"[data-id=cookie-consent-banner-buttons] > div:nth-child(1) button"}],optOut:[{waitForThenClick:"[data-id=cookie-consent-banner-buttons] > div:nth-child(2) button"}]},{name:"pmc",cosmetic:!0,prehideSelectors:["#pmc-pp-tou--notice"],detectCmp:[{exists:"#pmc-pp-tou--notice"}],detectPopup:[{visible:"#pmc-pp-tou--notice"}],optIn:[{click:"span.pmc-pp-tou--notice-close-btn"}],optOut:[{hide:"#pmc-pp-tou--notice"}]},{name:"pornhub.com",runContext:{urlPattern:"^https://(www\\.)?pornhub\\.com/"},cosmetic:!1,prehideSelectors:["#cookieBanner #cookieBannerContent"],detectCmp:[{exists:"#cookieBanner #cookieBannerContent"}],detectPopup:[{visible:"#cookieBanner #cookieBannerContent"}],optIn:[{waitForThenClick:"#cookieBanner [data-label=accept_all]"}],optOut:[{waitForThenClick:"#cookieBanner [data-label=accept_essential]"}]},{name:"pornpics.com",cosmetic:!0,prehideSelectors:["#cookie-contract"],detectCmp:[{exists:"#cookie-contract"}],detectPopup:[{visible:"#cookie-contract"}],optIn:[{click:"#cookie-contract .icon-cross"}],optOut:[{hide:"#cookie-contract"}]},{name:"PrimeBox CookieBar",prehideSelectors:["#cookie-bar"],detectCmp:[{exists:"#cookie-bar .cb-enable,#cookie-bar .cb-disable,#cookie-bar .cb-policy"}],detectPopup:[{visible:"#cookie-bar .cb-enable,#cookie-bar .cb-disable,#cookie-bar .cb-policy",check:"any"}],optIn:[{waitForThenClick:"#cookie-bar .cb-enable"}],optOut:[{click:"#cookie-bar .cb-disable",optional:!0},{hide:"#cookie-bar"}],test:[{eval:"EVAL_PRIMEBOX_0"}]},{name:"privacymanager.io",prehideSelectors:["#gdpr-consent-tool-wrapper",'iframe[src^="https://cmp-consent-tool.privacymanager.io"]'],runContext:{urlPattern:"^https://cmp-consent-tool\\.privacymanager\\.io/",main:!1,frame:!0},detectCmp:[{exists:"button#save"}],detectPopup:[{visible:"button#save"}],optIn:[{click:"button#save"}],optOut:[{if:{exists:"#denyAll"},then:[{click:"#denyAll"},{waitForThenClick:".okButton"}],else:[{waitForThenClick:"#manageSettings"},{waitFor:".purposes-overview-list"},{waitFor:"button#saveAndExit"},{click:"span[role=checkbox][aria-checked=true]",all:!0,optional:!0},{click:"button#saveAndExit"}]}]},{name:"productz.com",vendorUrl:"https://productz.com/",runContext:{urlPattern:"^https://productz\\.com/"},prehideSelectors:[],detectCmp:[{exists:".c-modal.is-active"}],detectPopup:[{visible:".c-modal.is-active"}],optIn:[{waitForThenClick:".c-modal.is-active .is-accept"}],optOut:[{waitForThenClick:".c-modal.is-active .is-dismiss"}]},{name:"pubtech",prehideSelectors:["#pubtech-cmp"],detectCmp:[{exists:"#pubtech-cmp"}],detectPopup:[{visible:"#pubtech-cmp #pt-actions"}],optIn:[{if:{exists:"#pt-accept-all"},then:[{click:"#pubtech-cmp #pt-actions #pt-accept-all"}],else:[{click:"#pubtech-cmp #pt-actions button:nth-of-type(2)"}]}],optOut:[{click:"#pubtech-cmp #pt-close"}],test:[{eval:"EVAL_PUBTECH_0"}]},{name:"quantcast",prehideSelectors:["#qc-cmp2-main,#qc-cmp2-container"],detectCmp:[{exists:"#qc-cmp2-container"}],detectPopup:[{visible:"#qc-cmp2-ui"}],optOut:[{waitFor:'.qc-cmp2-summary-buttons > button[mode="secondary"]',timeout:2e3},{if:{exists:'.qc-cmp2-summary-buttons > button[mode="secondary"]:nth-of-type(2)'},then:[{click:'.qc-cmp2-summary-buttons > button[mode="secondary"]:nth-of-type(2)'}],else:[{click:'.qc-cmp2-summary-buttons > button[mode="secondary"]:nth-of-type(1)'},{waitFor:"#qc-cmp2-ui"},{click:'.qc-cmp2-toggle-switch > button[aria-checked="true"]',all:!0,optional:!0},{click:'.qc-cmp2-main button[aria-label="REJECT ALL"]',optional:!0},{waitForThenClick:'.qc-cmp2-main button[aria-label="SAVE & EXIT"],.qc-cmp2-buttons-desktop > button[mode="primary"]',timeout:5e3}]}],optIn:[{click:'.qc-cmp2-summary-buttons > button[mode="primary"]'}]},{name:"reddit.com",runContext:{urlPattern:"^https://www\\.reddit\\.com/"},prehideSelectors:["[bundlename=reddit_cookie_banner]"],detectCmp:[{exists:"reddit-cookie-banner"}],detectPopup:[{visible:"reddit-cookie-banner"}],optIn:[{waitForThenClick:["reddit-cookie-banner","#accept-all-cookies-button > button"]}],optOut:[{waitForThenClick:["reddit-cookie-banner","#reject-nonessential-cookies-button > button"]}],test:[{eval:"EVAL_REDDIT_0"}]},{name:"roblox",vendorUrl:"https://roblox.com",cosmetic:!1,runContext:{main:!0,frame:!1,urlPattern:"^https://(www\\.)?roblox\\.com/"},prehideSelectors:[],detectCmp:[{exists:".cookie-banner-wrapper"}],detectPopup:[{visible:".cookie-banner-wrapper .cookie-banner"}],optIn:[{waitForThenClick:".cookie-banner-wrapper button.btn-cta-lg"}],optOut:[{waitForThenClick:".cookie-banner-wrapper button.btn-secondary-lg"}],test:[{eval:"EVAL_ROBLOX_TEST"}]},{name:"rog-forum.asus.com",runContext:{urlPattern:"^https://rog-forum\\.asus\\.com/"},prehideSelectors:["#cookie-policy-info"],detectCmp:[{exists:"#cookie-policy-info"}],detectPopup:[{visible:"#cookie-policy-info"}],optIn:[{click:'div.cookie-btn-box > div[aria-label="Accept"]'}],optOut:[{click:'div.cookie-btn-box > div[aria-label="Reject"]'},{waitForThenClick:'.cookie-policy-lightbox-bottom > div[aria-label="Save Settings"]'}]},{name:"roofingmegastore.co.uk",runContext:{urlPattern:"^https://(www\\.)?roofingmegastore\\.co\\.uk"},prehideSelectors:["#m-cookienotice"],detectCmp:[{exists:"#m-cookienotice"}],detectPopup:[{visible:"#m-cookienotice"}],optIn:[{click:"#accept-cookies"}],optOut:[{click:"#manage-cookies"},{waitForThenClick:"#accept-selected"}]},{name:"samsung.com",runContext:{urlPattern:"^https://www\\.samsung\\.com/"},cosmetic:!0,prehideSelectors:["div.cookie-bar"],detectCmp:[{exists:"div.cookie-bar"}],detectPopup:[{visible:"div.cookie-bar"}],optIn:[{click:"div.cookie-bar__manage > a"}],optOut:[{hide:"div.cookie-bar"}]},{name:"setapp.com",vendorUrl:"https://setapp.com/",cosmetic:!0,runContext:{urlPattern:"^https://setapp\\.com/"},prehideSelectors:[],detectCmp:[{exists:".cookie-banner.js-cookie-banner"}],detectPopup:[{visible:".cookie-banner.js-cookie-banner"}],optIn:[{waitForThenClick:".cookie-banner.js-cookie-banner button"}],optOut:[{hide:".cookie-banner.js-cookie-banner"}]},{name:"sibbo",prehideSelectors:["sibbo-cmp-layout"],detectCmp:[{exists:"sibbo-cmp-layout"}],detectPopup:[{visible:"#rejectAllMain"}],optIn:[{click:"#acceptAllMain"}],optOut:[{click:"#rejectAllMain"}]},{name:"similarweb.com",cosmetic:!0,prehideSelectors:[".app-cookies-notification"],detectCmp:[{exists:".app-cookies-notification"}],detectPopup:[{exists:".app-layout .app-cookies-notification"}],optIn:[{click:"button.app-cookies-notification__dismiss"}],optOut:[{hide:".app-layout .app-cookies-notification"}]},{name:"Sirdata",cosmetic:!1,prehideSelectors:["#sd-cmp"],detectCmp:[{exists:"#sd-cmp"}],detectPopup:[{visible:"#sd-cmp"}],optIn:[{waitForThenClick:"#sd-cmp .sd-cmp-3cRQ2"}],optOut:[{waitForThenClick:["#sd-cmp","xpath///span[contains(., 'Do not accept') or contains(., 'Acceptera inte') or contains(., 'No aceptar') or contains(., 'Ikke acceptere') or contains(., 'Nicht akzeptieren') or contains(., 'Не приемам') or contains(., 'Να μην γίνει αποδοχή') or contains(., 'Niet accepteren') or contains(., 'Nepřijímat') or contains(., 'Nie akceptuj') or contains(., 'Nu acceptați') or contains(., 'Não aceitar') or contains(., 'Continuer sans accepter') or contains(., 'Non accettare') or contains(., 'Nem fogad el')]"]}]},{name:"skyscanner",vendorUrl:"https://skyscanner.com",cosmetic:!1,runContext:{main:!0,frame:!1,urlPattern:"^https://(www\\.)?skyscanner[\\.a-z]+/"},prehideSelectors:[".cookie-banner-wrapper"],detectCmp:[{exists:"#cookieBannerContent"}],detectPopup:[{visible:"#cookieBannerContent"}],optIn:[{waitForThenClick:"[data-tracking-element-id=cookie_banner_accept_all]"}],optOut:[{waitForThenClick:"[data-tracking-element-id=cookie_banner_essential_only]"},{waitForVisible:"#cookieBannerContent",check:"none"}],test:[{eval:"EVAL_SKYSCANNER_TEST"}]},{name:"snigel",detectCmp:[{exists:".snigel-cmp-framework"}],detectPopup:[{visible:".snigel-cmp-framework"}],optOut:[{click:"#sn-b-custom"},{click:"#sn-b-save"}],test:[{eval:"EVAL_SNIGEL_0"}],optIn:[{click:".snigel-cmp-framework #accept-choices"}]},{name:"steampowered.com",detectCmp:[{exists:".cookiepreferences_popup"},{visible:".cookiepreferences_popup"}],detectPopup:[{visible:".cookiepreferences_popup"}],optOut:[{click:"#rejectAllButton"}],optIn:[{click:"#acceptAllButton"}],test:[{wait:1e3},{eval:"EVAL_STEAMPOWERED_0"}]},{name:"strato.de",prehideSelectors:[".consent__wrapper"],runContext:{urlPattern:"^https://www\\.strato\\.de/"},detectCmp:[{exists:".consent"}],detectPopup:[{visible:".consent"}],optIn:[{click:"button.consentAgree"}],optOut:[{click:"button.consentSettings"},{waitForThenClick:"button#consentSubmit"}]},{name:"svt.se",vendorUrl:"https://www.svt.se/",runContext:{urlPattern:"^https://www\\.svt\\.se/"},prehideSelectors:["[class*=CookieConsent__root___]"],detectCmp:[{exists:"[class*=CookieConsent__root___]"}],detectPopup:[{visible:"[class*=CookieConsent__modal___]"}],optIn:[{waitForThenClick:"[class*=CookieConsent__modal___] > div > button[class*=primary]"}],optOut:[{waitForThenClick:"[class*=CookieConsent__modal___] > div > button[class*=secondary]:nth-child(2)"}],test:[{eval:"EVAL_SVT_TEST"}]},{name:"takealot.com",cosmetic:!0,prehideSelectors:['div[class^="cookies-banner-module_"]'],detectCmp:[{exists:'div[class^="cookies-banner-module_cookie-banner_"]'}],detectPopup:[{exists:'div[class^="cookies-banner-module_cookie-banner_"]'}],optIn:[{click:'button[class*="cookies-banner-module_dismiss-button_"]'}],optOut:[{hide:'div[class^="cookies-banner-module_"]'},{if:{exists:'div[class^="cookies-banner-module_small-cookie-banner_"]'},then:[{eval:"EVAL_TAKEALOT_0"}],else:[]}]},{name:"tarteaucitron.js",prehideSelectors:["#tarteaucitronRoot"],detectCmp:[{exists:"#tarteaucitronRoot"}],detectPopup:[{visible:"#tarteaucitronRoot #tarteaucitronAlertBig",check:"any"}],optIn:[{eval:"EVAL_TARTEAUCITRON_1"}],optOut:[{eval:"EVAL_TARTEAUCITRON_0"}],test:[{eval:"EVAL_TARTEAUCITRON_2",comment:"sometimes there are required categories, so we check that at least something is false"}]},{name:"taunton",vendorUrl:"https://www.taunton.com/",prehideSelectors:["#taunton-user-consent__overlay"],detectCmp:[{exists:"#taunton-user-consent__overlay"}],detectPopup:[{exists:"#taunton-user-consent__overlay:not([aria-hidden=true])"}],optIn:[{click:"#taunton-user-consent__toolbar input[type=checkbox]:not(:checked)"},{click:"#taunton-user-consent__toolbar button[type=submit]"}],optOut:[{click:"#taunton-user-consent__toolbar input[type=checkbox]:checked",optional:!0,all:!0},{click:"#taunton-user-consent__toolbar button[type=submit]"}],test:[{eval:"EVAL_TAUNTON_TEST"}]},{name:"Tealium",prehideSelectors:["#__tealiumGDPRecModal,#__tealiumGDPRcpPrefs,#__tealiumImplicitmodal,#consent-layer"],detectCmp:[{exists:"#__tealiumGDPRecModal *,#__tealiumGDPRcpPrefs *,#__tealiumImplicitmodal *"},{eval:"EVAL_TEALIUM_0"}],detectPopup:[{visible:"#__tealiumGDPRecModal *,#__tealiumGDPRcpPrefs *,#__tealiumImplicitmodal *",check:"any"}],optOut:[{eval:"EVAL_TEALIUM_1"},{eval:"EVAL_TEALIUM_DONOTSELL"},{hide:"#__tealiumGDPRecModal,#__tealiumGDPRcpPrefs,#__tealiumImplicitmodal"},{waitForThenClick:"#cm-acceptNone,.js-accept-essential-cookies,#continueWithoutAccepting",timeout:1e3,optional:!0}],optIn:[{hide:"#__tealiumGDPRecModal,#__tealiumGDPRcpPrefs"},{eval:"EVAL_TEALIUM_2"}],test:[{eval:"EVAL_TEALIUM_3"},{eval:"EVAL_TEALIUM_DONOTSELL_CHECK"},{visible:"#__tealiumGDPRecModal,#__tealiumGDPRcpPrefs",check:"none"}]},{name:"temu",vendorUrl:"https://temu.com",runContext:{urlPattern:"^https://([a-z0-9-]+\\.)?temu\\.com/"},prehideSelectors:[],detectCmp:[{exists:'div > div > div > div > span[href*="/cookie-and-similar-technologies-policy.html"]'}],detectPopup:[{visible:'div > div > div > div > span[href*="/cookie-and-similar-technologies-policy.html"]'}],optIn:[{waitForThenClick:'div > div > div:has(> div > span[href*="/cookie-and-similar-technologies-policy.html"]) > [role=button]:nth-child(3)'}],optOut:[{if:{exists:"xpath///span[contains(., 'Alle afwijzen') or contains(., 'Reject all') or contains(., 'Tümünü reddet') or contains(., 'Odrzuć wszystko')]"},then:[{waitForThenClick:"xpath///span[contains(., 'Alle afwijzen') or contains(., 'Reject all') or contains(., 'Tümünü reddet') or contains(., 'Odrzuć wszystko')]"}],else:[{waitForThenClick:'div > div > div:has(> div > span[href*="/cookie-and-similar-technologies-policy.html"]) > [role=button]:nth-child(2)'}]}]},{name:"Termly",prehideSelectors:["#termly-code-snippet-support"],detectCmp:[{exists:"#termly-code-snippet-support"}],detectPopup:[{visible:"#termly-code-snippet-support div"}],optIn:[{waitForThenClick:'[data-tid="banner-accept"]'}],optOut:[{if:{exists:'[data-tid="banner-decline"]'},then:[{click:'[data-tid="banner-decline"]'}],else:[{click:".t-preference-button"},{wait:500},{if:{exists:".t-declineAllButton"},then:[{click:".t-declineAllButton"}],else:[{waitForThenClick:".t-preference-modal input[type=checkbox][checked]:not([disabled])",all:!0},{waitForThenClick:".t-saveButton"}]}]}]},{name:"termsfeed",vendorUrl:"https://termsfeed.com",comment:"v4.x.x",prehideSelectors:[".termsfeed-com---nb"],detectCmp:[{exists:".termsfeed-com---nb"}],detectPopup:[{visible:".termsfeed-com---nb"}],optIn:[{waitForThenClick:".cc-nb-okagree"}],optOut:[{waitForThenClick:".cc-nb-reject"}]},{name:"termsfeed3",vendorUrl:"https://termsfeed.com",comment:"v3.x.x",prehideSelectors:[".cc_dialog.cc_css_reboot,.cc_overlay_lock"],detectCmp:[{exists:".cc_dialog.cc_css_reboot"}],detectPopup:[{visible:".cc_dialog.cc_css_reboot"}],optIn:[{waitForThenClick:".cc_dialog.cc_css_reboot .cc_b_ok"}],optOut:[{if:{exists:".cc_dialog.cc_css_reboot .cc_b_cp"},then:[{click:".cc_dialog.cc_css_reboot .cc_b_cp"},{waitForVisible:".cookie-consent-preferences-dialog .cc_cp_f_save button"},{waitForThenClick:".cookie-consent-preferences-dialog .cc_cp_f_save button"}],else:[{hide:".cc_dialog.cc_css_reboot,.cc_overlay_lock"}]}]},{name:"tesco",vendorUrl:"https://www.tesco.com",cosmetic:!1,runContext:{urlPattern:"^https://(www\\.)?tesco\\.com/"},prehideSelectors:["[class*=CookieBanner__Sizer]"],detectCmp:[{exists:"[aria-label=consent-banner]"}],detectPopup:[{visible:"[aria-label=consent-banner]"}],optIn:[{wait:1e3},{waitForThenClick:"xpath///button[contains(., 'Accept all')]"}],optOut:[{wait:1e3},{waitForThenClick:"xpath///button[contains(., 'Reject all')]"}]},{name:"tesla",vendorUrl:"https://tesla.com/",runContext:{main:!0,frame:!1,urlPattern:"^https://(www\\.)?tesla\\.com/"},prehideSelectors:[],detectCmp:[{exists:"#cookie_banner"}],detectPopup:[{visible:"#cookie_banner"}],optIn:[{waitForThenClick:"#tsla-accept-cookie"}],optOut:[{waitForThenClick:"#tsla-reject-cookie"}],test:[{eval:"EVAL_TESLA_TEST"}]},{name:"Test page cosmetic CMP",cosmetic:!0,prehideSelectors:["#privacy-test-page-cmp-test-prehide"],detectCmp:[{exists:"#privacy-test-page-cmp-test-banner"}],detectPopup:[{visible:"#privacy-test-page-cmp-test-banner"}],optIn:[{waitFor:"#accept-all"},{click:"#accept-all"}],optOut:[{hide:"#privacy-test-page-cmp-test-banner"}],test:[{wait:500},{eval:"EVAL_TESTCMP_COSMETIC_0"}]},{name:"Test page CMP",prehideSelectors:["#reject-all"],detectCmp:[{exists:"#privacy-test-page-cmp-test"}],detectPopup:[{visible:"#privacy-test-page-cmp-test"}],optIn:[{waitFor:"#accept-all"},{click:"#accept-all"}],optOut:[{waitFor:"#reject-all"},{eval:"EVAL_TESTCMP_STEP"},{click:"#reject-all"}],test:[{eval:"EVAL_TESTCMP_0"}]},{name:"thalia.de",prehideSelectors:[".consent-banner-box"],detectCmp:[{exists:"consent-banner[component=consent-banner]"}],detectPopup:[{visible:".consent-banner-box"}],optIn:[{click:".button-zustimmen"}],optOut:[{click:"button[data-consent=disagree]"}]},{name:"thefreedictionary.com",prehideSelectors:["#cmpBanner"],detectCmp:[{exists:"#cmpBanner"}],detectPopup:[{visible:"#cmpBanner"}],optIn:[{eval:"EVAL_THEFREEDICTIONARY_1"}],optOut:[{eval:"EVAL_THEFREEDICTIONARY_0"}]},{name:"theverge",runContext:{frame:!1,main:!0,urlPattern:"^https://(www)?\\.theverge\\.com"},intermediate:!1,prehideSelectors:[".duet--cta--cookie-banner"],detectCmp:[{exists:".duet--cta--cookie-banner"}],detectPopup:[{visible:".duet--cta--cookie-banner"}],optIn:[{click:".duet--cta--cookie-banner button.tracking-12",all:!1}],optOut:[{click:".duet--cta--cookie-banner button.tracking-12 > span"}],test:[{eval:"EVAL_THEVERGE_0"}]},{name:"tidbits-com",cosmetic:!0,prehideSelectors:["#eu_cookie_law_widget-2"],detectCmp:[{exists:"#eu_cookie_law_widget-2"}],detectPopup:[{visible:"#eu_cookie_law_widget-2"}],optIn:[{click:"#eu-cookie-law form > input.accept"}],optOut:[{hide:"#eu_cookie_law_widget-2"}]},{name:"tractor-supply",runContext:{urlPattern:"^https://www\\.tractorsupply\\.com/"},cosmetic:!0,prehideSelectors:[".tsc-cookie-banner"],detectCmp:[{exists:".tsc-cookie-banner"}],detectPopup:[{visible:".tsc-cookie-banner"}],optIn:[{click:"#cookie-banner-cancel"}],optOut:[{hide:".tsc-cookie-banner"}]},{name:"trader-joes-com",cosmetic:!0,prehideSelectors:['div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]'],detectCmp:[{exists:'div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]'}],detectPopup:[{visible:'div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]'}],optIn:[{click:'div[class^="CookiesAlert_cookiesAlert__container__"] button'}],optOut:[{hide:'div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]'}]},{name:"transcend",vendorUrl:"https://unknown",cosmetic:!0,prehideSelectors:["#transcend-consent-manager"],detectCmp:[{exists:"#transcend-consent-manager"}],detectPopup:[{visible:"#transcend-consent-manager"}],optIn:[{waitForThenClick:["#transcend-consent-manager","#consentManagerMainDialog .inner-container button"]}],optOut:[{hide:"#transcend-consent-manager"}]},{name:"transip-nl",runContext:{urlPattern:"^https://www\\.transip\\.nl/"},prehideSelectors:["#consent-modal"],detectCmp:[{any:[{exists:"#consent-modal"},{exists:"#privacy-settings-content"}]}],detectPopup:[{any:[{visible:"#consent-modal"},{visible:"#privacy-settings-content"}]}],optIn:[{click:'button[type="submit"]'}],optOut:[{if:{exists:"#privacy-settings-content"},then:[{click:'button[type="submit"]'}],else:[{click:"div.one-modal__action-footer-column--secondary > a"}]}]},{name:"tropicfeel-com",prehideSelectors:["#shopify-section-cookies-controller"],detectCmp:[{exists:"#shopify-section-cookies-controller"}],detectPopup:[{visible:"#shopify-section-cookies-controller #cookies-controller-main-pane",check:"any"}],optIn:[{waitForThenClick:"#cookies-controller-main-pane form[data-form-allow-all] button"}],optOut:[{click:"#cookies-controller-main-pane a[data-tab-target=manage-cookies]"},{waitFor:"#manage-cookies-pane.active"},{click:"#manage-cookies-pane.active input[type=checkbox][checked]:not([disabled])",all:!0},{click:"#manage-cookies-pane.active button[type=submit]"}],test:[]},{name:"true-car",runContext:{urlPattern:"^https://www\\.truecar\\.com/"},cosmetic:!0,prehideSelectors:[['div[aria-labelledby="cookie-banner-heading"]']],detectCmp:[{exists:'div[aria-labelledby="cookie-banner-heading"]'}],detectPopup:[{visible:'div[aria-labelledby="cookie-banner-heading"]'}],optIn:[{click:'div[aria-labelledby="cookie-banner-heading"] > button[aria-label="Close"]'}],optOut:[{hide:'div[aria-labelledby="cookie-banner-heading"]'}]},{name:"truyo",prehideSelectors:["#truyo-consent-module"],detectCmp:[{exists:"#truyo-cookieBarContent"}],detectPopup:[{visible:"#truyo-consent-module"}],optIn:[{click:"button#acceptAllCookieButton"}],optOut:[{click:"button#declineAllCookieButton"}]},{name:"twcc",vendorUrl:"https://unknown",cosmetic:!1,runContext:{main:!0,frame:!1,urlPattern:""},prehideSelectors:["#twcc__mechanism"],detectCmp:[{exists:"#twcc__mechanism .twcc__notice"}],detectPopup:[{visible:"#twcc__mechanism .twcc__notice"}],optIn:[{waitForThenClick:"#twcc__accept-button"}],optOut:[{waitForThenClick:"#twcc__decline-button"}],test:[{eval:"EVAL_TWCC_TEST"}]},{name:"twitch-mobile",vendorUrl:"https://m.twitch.tv/",cosmetic:!0,runContext:{urlPattern:"^https?://m\\.twitch\\.tv"},prehideSelectors:[],detectCmp:[{exists:'.ReactModal__Overlay [href="https://www.twitch.tv/p/cookie-policy"]'}],detectPopup:[{visible:'.ReactModal__Overlay [href="https://www.twitch.tv/p/cookie-policy"]'}],optIn:[{waitForThenClick:'.ReactModal__Overlay:has([href="https://www.twitch.tv/p/cookie-policy"]) button'}],optOut:[{hide:'.ReactModal__Overlay:has([href="https://www.twitch.tv/p/cookie-policy"])'}]},{name:"twitch.tv",runContext:{urlPattern:"^https?://(www\\.)?twitch\\.tv"},prehideSelectors:["div:has(> .consent-banner .consent-banner__content--gdpr-v2),.ReactModalPortal:has([data-a-target=consent-modal-save])"],detectCmp:[{exists:".consent-banner .consent-banner__content--gdpr-v2"}],detectPopup:[{visible:".consent-banner .consent-banner__content--gdpr-v2"}],optIn:[{click:'button[data-a-target="consent-banner-accept"]'}],optOut:[{hide:"div:has(> .consent-banner .consent-banner__content--gdpr-v2)"},{click:'button[data-a-target="consent-banner-manage-preferences"]'},{waitFor:"input[type=checkbox][data-a-target=tw-checkbox]"},{click:"input[type=checkbox][data-a-target=tw-checkbox][checked]:not([disabled])",all:!0,optional:!0},{waitForThenClick:"[data-a-target=consent-modal-save]"},{waitForVisible:".ReactModalPortal:has([data-a-target=consent-modal-save])",check:"none"}]},{name:"twitter",runContext:{urlPattern:"^https://([a-z0-9-]+\\.)?(twitter|x)\\.com/"},prehideSelectors:['[data-testid="BottomBar"]'],detectCmp:[{exists:'[data-testid="BottomBar"] div'}],detectPopup:[{visible:'[data-testid="BottomBar"] div'}],optIn:[{waitForThenClick:'[data-testid="BottomBar"] > div:has(>div:first-child>div:last-child>button[role=button]>span) > div:last-child > button[role=button]:first-child'}],optOut:[{waitForThenClick:'[data-testid="BottomBar"] > div:has(>div:first-child>div:last-child>button[role=button]>span) > div:last-child > button[role=button]:last-child'}],TODOtest:[{eval:"EVAL_document.cookie.includes('d_prefs=MjoxLGNvbnNlbnRfdmVyc2lvbjoy')"}]},{name:"ubuntu.com",prehideSelectors:["dialog.cookie-policy"],detectCmp:[{any:[{exists:"dialog.cookie-policy header"},{exists:'xpath///*[@id="modal"]/div/header'}]}],detectPopup:[{any:[{visible:"dialog header"},{visible:'xpath///*[@id="modal"]/div/header'}]}],optIn:[{any:[{waitForThenClick:"#cookie-policy-button-accept"},{waitForThenClick:'xpath///*[@id="cookie-policy-button-accept"]'}]}],optOut:[{any:[{waitForThenClick:"button.js-manage"},{waitForThenClick:'xpath///*[@id="cookie-policy-content"]/p[4]/button[2]'}]},{waitForThenClick:"dialog.cookie-policy .p-switch__input:checked",optional:!0,all:!0,timeout:500},{any:[{waitForThenClick:"dialog.cookie-policy .js-save-preferences"},{waitForThenClick:'xpath///*[@id="modal"]/div/button'}]}],test:[{eval:"EVAL_UBUNTU_COM_0"}]},{name:"UK Cookie Consent",prehideSelectors:["#catapult-cookie-bar"],cosmetic:!0,detectCmp:[{exists:"#catapult-cookie-bar"}],detectPopup:[{exists:".has-cookie-bar #catapult-cookie-bar"}],optIn:[{click:"#catapultCookie"}],optOut:[{hide:"#catapult-cookie-bar"}],test:[{eval:"EVAL_UK_COOKIE_CONSENT_0"}]},{name:"urbanarmorgear-com",cosmetic:!0,prehideSelectors:['div[class^="Layout__CookieBannerContainer-"]'],detectCmp:[{exists:'div[class^="Layout__CookieBannerContainer-"]'}],detectPopup:[{visible:'div[class^="Layout__CookieBannerContainer-"]'}],optIn:[{click:'button[class^="CookieBanner__AcceptButton"]'}],optOut:[{hide:'div[class^="Layout__CookieBannerContainer-"]'}]},{name:"usercentrics-api",detectCmp:[{exists:"#usercentrics-root,#usercentrics-cmp-ui"}],detectPopup:[{eval:"EVAL_USERCENTRICS_API_0"},{if:{exists:"#usercentrics-cmp-ui"},then:[{waitForVisible:"#usercentrics-cmp-ui",timeout:2e3}],else:[{exists:["#usercentrics-root","[data-testid=uc-container]"]},{waitForVisible:"#usercentrics-root",timeout:2e3}]}],optIn:[{eval:"EVAL_USERCENTRICS_API_3"},{eval:"EVAL_USERCENTRICS_API_1"},{eval:"EVAL_USERCENTRICS_API_5"}],optOut:[{eval:"EVAL_USERCENTRICS_API_1"},{eval:"EVAL_USERCENTRICS_API_2"}],test:[{eval:"EVAL_USERCENTRICS_API_6"}]},{name:"usercentrics-button",detectCmp:[{exists:"#usercentrics-button"}],detectPopup:[{visible:"#usercentrics-button #uc-btn-accept-banner"}],optIn:[{click:"#usercentrics-button #uc-btn-accept-banner"}],optOut:[{click:"#usercentrics-button #uc-btn-deny-banner"}],test:[{eval:"EVAL_USERCENTRICS_BUTTON_0"}]},{name:"uswitch.com",runContext:{main:!0,frame:!1,urlPattern:"^https://(www\\.)?uswitch\\.com/"},prehideSelectors:[".ucb"],detectCmp:[{exists:".ucb-banner"}],detectPopup:[{visible:".ucb-banner"}],optIn:[{waitForThenClick:".ucb-banner .ucb-btn-accept"}],optOut:[{waitForThenClick:".ucb-banner .ucb-btn-save"}]},{name:"vodafone.de",runContext:{urlPattern:"^https://www\\.vodafone\\.de/"},prehideSelectors:[".dip-consent,.dip-consent-container"],detectCmp:[{exists:".dip-consent-container"}],detectPopup:[{visible:".dip-consent-content"}],optOut:[{click:'.dip-consent-btn[tabindex="2"]'}],optIn:[{click:'.dip-consent-btn[tabindex="1"]'}]},{name:"waitrose.com",prehideSelectors:["div[aria-labelledby=CookieAlertModalHeading]","section[data-test=initial-waitrose-cookie-consent-banner]","section[data-test=cookie-consent-modal]"],detectCmp:[{exists:"section[data-test=initial-waitrose-cookie-consent-banner]"}],detectPopup:[{visible:"section[data-test=initial-waitrose-cookie-consent-banner]"}],optIn:[{click:"button[data-test=accept-all]"}],optOut:[{click:"button[data-test=manage-cookies]"},{wait:200},{eval:"EVAL_WAITROSE_0"},{click:"button[data-test=submit]"}],test:[{eval:"EVAL_WAITROSE_1"}]},{name:"webflow",vendorUrl:"https://webflow.com/",prehideSelectors:[".fs-cc-components"],detectCmp:[{exists:".fs-cc-components"}],detectPopup:[{visible:".fs-cc-components"},{visible:"[fs-cc=banner]"}],optIn:[{wait:500},{waitForThenClick:"[fs-cc=banner] [fs-cc=allow]"}],optOut:[{wait:500},{waitForThenClick:"[fs-cc=banner] [fs-cc=deny]"}]},{name:"wetransfer.com",detectCmp:[{exists:".welcome__cookie-notice"}],detectPopup:[{visible:".welcome__cookie-notice"}],optIn:[{click:".welcome__button--accept"}],optOut:[{click:".welcome__button--decline"}]},{name:"whitepages.com",runContext:{urlPattern:"^https://www\\.whitepages\\.com/"},cosmetic:!0,prehideSelectors:[".cookie-wrapper, .cookie-overlay"],detectCmp:[{exists:".cookie-wrapper"}],detectPopup:[{visible:".cookie-overlay"}],optIn:[{click:'button[aria-label="Got it"]'}],optOut:[{hide:".cookie-wrapper"}]},{name:"wolframalpha",vendorUrl:"https://www.wolframalpha.com",prehideSelectors:[],cosmetic:!0,runContext:{urlPattern:"^https://www\\.wolframalpha\\.com/"},detectCmp:[{exists:"section._a_yb"}],detectPopup:[{visible:"section._a_yb"}],optIn:[{waitForThenClick:"section._a_yb button"}],optOut:[{hide:"section._a_yb"}]},{name:"woo-commerce-com",prehideSelectors:[".wccom-comp-privacy-banner .wccom-privacy-banner"],detectCmp:[{exists:".wccom-comp-privacy-banner .wccom-privacy-banner"}],detectPopup:[{exists:".wccom-comp-privacy-banner .wccom-privacy-banner"}],optIn:[{click:".wccom-privacy-banner__content-buttons button.is-primary"}],optOut:[{click:".wccom-privacy-banner__content-buttons button.is-secondary"},{waitForThenClick:"input[type=checkbox][checked]:not([disabled])",all:!0},{click:"div.wccom-modal__footer > button"}]},{name:"WP Cookie Notice for GDPR",vendorUrl:"https://wordpress.org/plugins/gdpr-cookie-consent/",prehideSelectors:["#gdpr-cookie-consent-bar"],detectCmp:[{exists:"#gdpr-cookie-consent-bar"}],detectPopup:[{visible:"#gdpr-cookie-consent-bar"}],optIn:[{waitForThenClick:"#gdpr-cookie-consent-bar #cookie_action_accept"}],optOut:[{waitForThenClick:"#gdpr-cookie-consent-bar #cookie_action_reject"}],test:[{eval:"EVAL_WP_COOKIE_NOTICE_0"}]},{name:"wpcc",cosmetic:!0,prehideSelectors:[".wpcc-container"],detectCmp:[{exists:".wpcc-container"}],detectPopup:[{exists:".wpcc-container .wpcc-message"}],optIn:[{click:".wpcc-compliance .wpcc-btn"}],optOut:[{hide:".wpcc-container"}]},{name:"xe.com",vendorUrl:"https://www.xe.com/",runContext:{urlPattern:"^https://www\\.xe\\.com/"},prehideSelectors:["[class*=ConsentBanner]"],detectCmp:[{exists:"[class*=ConsentBanner]"}],detectPopup:[{visible:"[class*=ConsentBanner]"}],optIn:[{waitForThenClick:"[class*=ConsentBanner] .egnScw"}],optOut:[{wait:1e3},{waitForThenClick:"[class*=ConsentBanner] .frDWEu"},{waitForThenClick:"[class*=ConsentBanner] .hXIpFU"}],test:[{eval:"EVAL_XE_TEST"}]},{name:"xhamster-eu",prehideSelectors:[".cookies-modal"],detectCmp:[{exists:".cookies-modal"}],detectPopup:[{exists:".cookies-modal"}],optIn:[{click:"button.cmd-button-accept-all"}],optOut:[{click:"button.cmd-button-reject-all"}]},{name:"xhamster-us",runContext:{urlPattern:"^https://(www\\.)?xhamster\\d?\\.com"},cosmetic:!0,prehideSelectors:[".cookie-announce"],detectCmp:[{exists:".cookie-announce"}],detectPopup:[{visible:".cookie-announce .announce-text"}],optIn:[{click:".cookie-announce button.xh-button"}],optOut:[{hide:".cookie-announce"}]},{name:"xing.com",detectCmp:[{exists:"div[class^=cookie-consent-CookieConsent]"}],detectPopup:[{exists:"div[class^=cookie-consent-CookieConsent]"}],optIn:[{click:"#consent-accept-button"}],optOut:[{click:"#consent-settings-button"},{click:".consent-banner-button-accept-overlay"}],test:[{eval:"EVAL_XING_0"}]},{name:"xnxx-com",cosmetic:!0,prehideSelectors:["#cookies-use-alert"],detectCmp:[{exists:"#cookies-use-alert"}],detectPopup:[{visible:"#cookies-use-alert"}],optIn:[{click:"#cookies-use-alert .close"}],optOut:[{hide:"#cookies-use-alert"}]},{name:"xvideos",vendorUrl:"https://xvideos.com",runContext:{urlPattern:"^https://[^/]*xvideos\\.com/"},prehideSelectors:[],detectCmp:[{exists:".disclaimer-opened #disclaimer-cookies"}],detectPopup:[{visible:".disclaimer-opened #disclaimer-cookies"}],optIn:[{waitForThenClick:"#disclaimer-accept_cookies"}],optOut:[{waitForThenClick:"#disclaimer-reject_cookies"}]},{name:"Yahoo",runContext:{urlPattern:"^https://consent\\.yahoo\\.com/v2/"},prehideSelectors:["#reject-all"],detectCmp:[{exists:"#consent-page"}],detectPopup:[{visible:"#consent-page"}],optIn:[{waitForThenClick:"#consent-page button[value=agree]"}],optOut:[{waitForThenClick:"#consent-page button[value=reject]"}]},{name:"youporn.com",cosmetic:!0,prehideSelectors:[".euCookieModal, #js_euCookieModal"],detectCmp:[{exists:".euCookieModal"}],detectPopup:[{exists:".euCookieModal, #js_euCookieModal"}],optIn:[{click:'button[name="user_acceptCookie"]'}],optOut:[{hide:".euCookieModal"}]},{name:"youtube-desktop",prehideSelectors:["tp-yt-iron-overlay-backdrop.opened","ytd-consent-bump-v2-lightbox"],detectCmp:[{exists:"ytd-consent-bump-v2-lightbox tp-yt-paper-dialog"},{exists:'ytd-consent-bump-v2-lightbox tp-yt-paper-dialog a[href^="https://consent.youtube.com/"]'}],detectPopup:[{visible:"ytd-consent-bump-v2-lightbox tp-yt-paper-dialog"}],optIn:[{waitForThenClick:"ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:last-child #button,ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:last-child button"},{wait:500}],optOut:[{waitForThenClick:"ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:first-child #button,ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:first-child button"},{wait:500}],test:[{wait:500},{eval:"EVAL_YOUTUBE_DESKTOP_0"}]},{name:"youtube-mobile",prehideSelectors:[".consent-bump-v2-lightbox"],detectCmp:[{exists:"ytm-consent-bump-v2-renderer"}],detectPopup:[{visible:"ytm-consent-bump-v2-renderer"}],optIn:[{waitForThenClick:"ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons c3-material-button:first-child button, ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons ytm-button-renderer:first-child button"},{wait:500}],optOut:[{waitForThenClick:"ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons c3-material-button:nth-child(2) button, ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons ytm-button-renderer:nth-child(2) button"},{wait:500}],test:[{wait:500},{eval:"EVAL_YOUTUBE_MOBILE_0"}]},{name:"zdf",prehideSelectors:["#zdf-cmp-banner-sdk"],detectCmp:[{exists:"#zdf-cmp-banner-sdk"}],detectPopup:[{visible:"#zdf-cmp-main.zdf-cmp-show"}],optIn:[{waitForThenClick:"#zdf-cmp-main #zdf-cmp-accept-btn"}],optOut:[{waitForThenClick:"#zdf-cmp-main #zdf-cmp-deny-btn"}],test:[]},{name:"zentralruf-de",runContext:{urlPattern:"^https://(www\\.)?zentralruf\\.de"},prehideSelectors:["#cookie_modal_wrapper"],detectCmp:[{exists:"#cookie_modal_wrapper"}],detectPopup:[{visible:"#cookie_modal_wrapper"}],optIn:[{waitForThenClick:"#cookie_modal_wrapper #cookie_modal_button_consent_all"}],optOut:[{waitForThenClick:"#cookie_modal_wrapper #cookie_modal_button_choose"}]}],kn={"didomi.io":{detectors:[{presentMatcher:{target:{selector:"#didomi-host, #didomi-notice"},type:"css"},showingMatcher:{target:{selector:"body.didomi-popup-open, .didomi-notice-banner"},type:"css"}}],methods:[{action:{target:{selector:".didomi-popup-notice-buttons .didomi-button:not(.didomi-button-highlight), .didomi-notice-banner .didomi-learn-more-button"},type:"click"},name:"OPEN_OPTIONS"},{action:{actions:[{retries:50,target:{selector:"#didomi-purpose-cookies"},type:"waitcss",waitTime:50},{consents:[{description:"Share (everything) with others",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-share_whith_others]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-share_whith_others]:last-child"},type:"click"},type:"X"},{description:"Information storage and access",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-cookies]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-cookies]:last-child"},type:"click"},type:"D"},{description:"Content selection, offers and marketing",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-CL-T1Rgm7]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-CL-T1Rgm7]:last-child"},type:"click"},type:"E"},{description:"Analytics",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-analytics]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-analytics]:last-child"},type:"click"},type:"B"},{description:"Analytics",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-M9NRHJe3G]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-M9NRHJe3G]:last-child"},type:"click"},type:"B"},{description:"Ad and content selection",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-advertising_personalization]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-advertising_personalization]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection",falseAction:{parent:{childFilter:{target:{selector:"#didomi-purpose-pub-ciblee"}},selector:".didomi-consent-popup-data-processing, .didomi-components-accordion-label-container"},target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-pub-ciblee]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-pub-ciblee]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - basics",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-q4zlJqdcD]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-q4zlJqdcD]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - partners and subsidiaries",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-partenaire-cAsDe8jC]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-partenaire-cAsDe8jC]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - social networks",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-p4em9a8m]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-p4em9a8m]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - others",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-autres-pub]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-autres-pub]:last-child"},type:"click"},type:"F"},{description:"Social networks",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-reseauxsociaux]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-reseauxsociaux]:last-child"},type:"click"},type:"A"},{description:"Social networks",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-social_media]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-social_media]:last-child"},type:"click"},type:"A"},{description:"Content selection",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-content_personalization]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-content_personalization]:last-child"},type:"click"},type:"E"},{description:"Ad delivery",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-ad_delivery]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-ad_delivery]:last-child"},type:"click"},type:"F"}],type:"consent"},{action:{consents:[{matcher:{childFilter:{target:{selector:":not(.didomi-components-radio__option--selected)"}},type:"css"},trueAction:{target:{selector:":nth-child(2)"},type:"click"},falseAction:{target:{selector:":first-child"},type:"click"},type:"X"}],type:"consent"},target:{selector:".didomi-components-radio"},type:"foreach"}],type:"list"},name:"DO_CONSENT"},{action:{parent:{selector:".didomi-consent-popup-footer .didomi-consent-popup-actions"},target:{selector:".didomi-components-button:first-child"},type:"click"},name:"SAVE_CONSENT"}]},oil:{detectors:[{presentMatcher:{target:{selector:".as-oil-content-overlay"},type:"css"},showingMatcher:{target:{selector:".as-oil-content-overlay"},type:"css"}}],methods:[{action:{actions:[{target:{selector:".as-js-advanced-settings"},type:"click"},{retries:"10",target:{selector:".as-oil-cpc__purpose-container"},type:"waitcss",waitTime:"250"}],type:"list"},name:"OPEN_OPTIONS"},{action:{actions:[{consents:[{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Information storage and access","Opbevaring af og adgang til oplysninger på din enhed"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Information storage and access","Opbevaring af og adgang til oplysninger på din enhed"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"D"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personlige annoncer","Personalisation"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personlige annoncer","Personalisation"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"E"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Annoncevalg, levering og rapportering","Ad selection, delivery, reporting"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Annoncevalg, levering og rapportering","Ad selection, delivery, reporting"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"F"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personalisering af indhold","Content selection, delivery, reporting"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personalisering af indhold","Content selection, delivery, reporting"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"E"},{matcher:{parent:{childFilter:{target:{selector:".as-oil-cpc__purpose-header",textFilter:["Måling","Measurement"]}},selector:".as-oil-cpc__purpose-container"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{childFilter:{target:{selector:".as-oil-cpc__purpose-header",textFilter:["Måling","Measurement"]}},selector:".as-oil-cpc__purpose-container"},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"B"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:"Google"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:"Google"},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"F"}],type:"consent"}],type:"list"},name:"DO_CONSENT"},{action:{target:{selector:".as-oil__btn-optin"},type:"click"},name:"SAVE_CONSENT"},{action:{target:{selector:"div.as-oil"},type:"hide"},name:"HIDE_CMP"}]},optanon:{detectors:[{presentMatcher:{target:{selector:"#optanon-menu, .optanon-alert-box-wrapper"},type:"css"},showingMatcher:{target:{displayFilter:!0,selector:".optanon-alert-box-wrapper"},type:"css"}}],methods:[{action:{actions:[{target:{selector:".optanon-alert-box-wrapper .optanon-toggle-display, a[onclick*='OneTrust.ToggleInfoDisplay()'], a[onclick*='Optanon.ToggleInfoDisplay()']"},type:"click"}],type:"list"},name:"OPEN_OPTIONS"},{action:{actions:[{target:{selector:".preference-menu-item #Your-privacy"},type:"click"},{target:{selector:"#optanon-vendor-consent-text"},type:"click"},{action:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"X"}],type:"consent"},target:{selector:"#optanon-vendor-consent-list .vendor-item"},type:"foreach"},{target:{selector:".vendor-consent-back-link"},type:"click"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-performance"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-performance"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-functional"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-functional"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-advertising"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-advertising"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-social"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-social"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Social Media Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Social Media Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalisation"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalisation"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Site monitoring cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Site monitoring cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Third party privacy-enhanced content"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Third party privacy-enhanced content"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"X"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Performance & Advertising Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Performance & Advertising Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Information storage and access"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Information storage and access"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"D"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad selection, delivery, reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad selection, delivery, reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content selection, delivery, reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content selection, delivery, reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Measurement"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Measurement"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Recommended Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Recommended Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"X"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Unclassified Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Unclassified Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"X"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Analytical Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Analytical Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Marketing Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Marketing Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalization"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalization"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad Selection, Delivery & Reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad Selection, Delivery & Reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content Selection, Delivery & Reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content Selection, Delivery & Reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"}],type:"list"},name:"DO_CONSENT"},{action:{parent:{selector:".optanon-save-settings-button"},target:{selector:".optanon-white-button-middle"},type:"click"},name:"SAVE_CONSENT"},{action:{actions:[{target:{selector:"#optanon-popup-wrapper"},type:"hide"},{target:{selector:"#optanon-popup-bg"},type:"hide"},{target:{selector:".optanon-alert-box-wrapper"},type:"hide"}],type:"list"},name:"HIDE_CMP"}]},quantcast2:{detectors:[{presentMatcher:{target:{selector:"[data-tracking-opt-in-overlay]"},type:"css"},showingMatcher:{target:{selector:"[data-tracking-opt-in-overlay] [data-tracking-opt-in-learn-more]"},type:"css"}}],methods:[{action:{target:{selector:"[data-tracking-opt-in-overlay] [data-tracking-opt-in-learn-more]"},type:"click"},name:"OPEN_OPTIONS"},{action:{actions:[{type:"wait",waitTime:500},{action:{actions:[{target:{selector:"div",textFilter:["Information storage and access"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"D"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Personalization"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"F"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Ad selection, delivery, reporting"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"F"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Content selection, delivery, reporting"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"E"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Measurement"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"B"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Other Partners"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"X"}],type:"consent"},type:"ifcss"}],type:"list"},parent:{childFilter:{target:{selector:"input"}},selector:"[data-tracking-opt-in-overlay] > div > div"},target:{childFilter:{target:{selector:"input"}},selector:":scope > div"},type:"foreach"}],type:"list"},name:"DO_CONSENT"},{action:{target:{selector:"[data-tracking-opt-in-overlay] [data-tracking-opt-in-save]"},type:"click"},name:"SAVE_CONSENT"}]},springer:{detectors:[{presentMatcher:{parent:null,target:{selector:".cmp-app_gdpr"},type:"css"},showingMatcher:{parent:null,target:{displayFilter:!0,selector:".cmp-popup_popup"},type:"css"}}],methods:[{action:{actions:[{target:{selector:".cmp-intro_rejectAll"},type:"click"},{type:"wait",waitTime:250},{target:{selector:".cmp-purposes_purposeItem:not(.cmp-purposes_selectedPurpose)"},type:"click"}],type:"list"},name:"OPEN_OPTIONS"},{action:{consents:[{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Przechowywanie informacji na urządzeniu lub dostęp do nich",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Przechowywanie informacji na urządzeniu lub dostęp do nich",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"D"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór podstawowych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór podstawowych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"F"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"F"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"E"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"E"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"B"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"B"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"B"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Stosowanie badań rynkowych w celu generowania opinii odbiorców",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Stosowanie badań rynkowych w celu generowania opinii odbiorców",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"X"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Opracowywanie i ulepszanie produktów",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Opracowywanie i ulepszanie produktów",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"X"}],type:"consent"},name:"DO_CONSENT"},{action:{target:{selector:".cmp-details_save"},type:"click"},name:"SAVE_CONSENT"}]},wordpressgdpr:{detectors:[{presentMatcher:{parent:null,target:{selector:".wpgdprc-consent-bar"},type:"css"},showingMatcher:{parent:null,target:{displayFilter:!0,selector:".wpgdprc-consent-bar"},type:"css"}}],methods:[{action:{parent:null,target:{selector:".wpgdprc-consent-bar .wpgdprc-consent-bar__settings",textFilter:null},type:"click"},name:"OPEN_OPTIONS"},{action:{actions:[{target:{selector:".wpgdprc-consent-modal .wpgdprc-button",textFilter:"Eyeota"},type:"click"},{consents:[{description:"Eyeota Cookies",matcher:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Eyeota"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Eyeota"},target:{selector:"label"},type:"click"},type:"X"}],type:"consent"},{target:{selector:".wpgdprc-consent-modal .wpgdprc-button",textFilter:"Advertising"},type:"click"},{consents:[{description:"Advertising Cookies",matcher:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Advertising"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Advertising"},target:{selector:"label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},name:"DO_CONSENT"},{action:{parent:null,target:{selector:".wpgdprc-button",textFilter:"Save my settings"},type:"click"},name:"SAVE_CONSENT"}]}},bn={autoconsent:fn,consentomatic:kn},yn=Object.freeze({__proto__:null,autoconsent:fn,consentomatic:kn,default:bn}); +!function(){"use strict";var e=class e{static setBase(t){e.base=t}static findElement(t,o=null,i=!1){let c=null;return c=null!=o?Array.from(o.querySelectorAll(t.selector)):null!=e.base?Array.from(e.base.querySelectorAll(t.selector)):Array.from(document.querySelectorAll(t.selector)),null!=t.textFilter&&(c=c.filter((e=>{const o=e.textContent.toLowerCase();if(Array.isArray(t.textFilter)){let e=!1;for(const i of t.textFilter)if(-1!==o.indexOf(i.toLowerCase())){e=!0;break}return e}return null!=t.textFilter&&-1!==o.indexOf(t.textFilter.toLowerCase())}))),null!=t.styleFilters&&(c=c.filter((e=>{const o=window.getComputedStyle(e);let i=!0;for(const e of t.styleFilters){const t=o[e.option];i=e.negated?i&&t!==e.value:i&&t===e.value}return i}))),null!=t.displayFilter&&(c=c.filter((e=>t.displayFilter?0!==e.offsetHeight:0===e.offsetHeight))),null!=t.iframeFilter&&(c=c.filter((()=>t.iframeFilter?window.location!==window.parent.location:window.location===window.parent.location))),null!=t.childFilter&&(c=c.filter((o=>{const i=e.base;e.setBase(o);const c=e.find(t.childFilter);return e.setBase(i),null!=c.target}))),i?c:(c.length>1&&console.warn("Multiple possible targets: ",c,t,o),c[0])}static find(t,o=!1){const i=[];if(null!=t.parent){const c=e.findElement(t.parent,null,o);if(null!=c){if(c instanceof Array)return c.forEach((c=>{const n=e.findElement(t.target,c,o);n instanceof Array?n.forEach((e=>{i.push({parent:c,target:e})})):i.push({parent:c,target:n})})),i;{const n=e.findElement(t.target,c,o);n instanceof Array?n.forEach((e=>{i.push({parent:c,target:e})})):i.push({parent:c,target:n})}}}else{const c=e.findElement(t.target,null,o);c instanceof Array?c.forEach((e=>{i.push({parent:null,target:e})})):i.push({parent:null,target:c})}return 0===i.length&&i.push({parent:null,target:null}),o?i:(1!==i.length&&console.warn("Multiple results found, even though multiple false",i),i[0])}};e.base=null;var t=e;function o(e){const o=t.find(e);return"css"===e.type?!!o.target:"checkbox"===e.type?!!o.target&&o.target.checked:void 0}async function i(e,a){switch(e.type){case"click":return async function(e){const o=t.find(e);null!=o.target&&o.target.click();return n(c)}(e);case"list":return async function(e,t){for(const o of e.actions)await i(o,t)}(e,a);case"consent":return async function(e,t){for(const c of e.consents){const e=-1!==t.indexOf(c.type);if(c.matcher&&c.toggleAction){o(c.matcher)!==e&&await i(c.toggleAction)}else e?await i(c.trueAction):await i(c.falseAction)}}(e,a);case"ifcss":return async function(e,o){const c=t.find(e);c.target?e.falseAction&&await i(e.falseAction,o):e.trueAction&&await i(e.trueAction,o)}(e,a);case"waitcss":return async function(e){await new Promise((o=>{let i=e.retries||10;const c=e.waitTime||250,n=()=>{const a=t.find(e);(e.negated&&a.target||!e.negated&&!a.target)&&i>0?(i-=1,setTimeout(n,c)):o()};n()}))}(e);case"foreach":return async function(e,o){const c=t.find(e,!0),n=t.base;for(const n of c)n.target&&(t.setBase(n.target),await i(e.action,o));t.setBase(n)}(e,a);case"hide":return async function(e){const o=t.find(e);o.target&&o.target.classList.add("Autoconsent-Hidden")}(e);case"slide":return async function(e){const o=t.find(e),i=t.find(e.dragTarget);if(o.target){const e=o.target.getBoundingClientRect(),t=i.target.getBoundingClientRect();let c=t.top-e.top,n=t.left-e.left;"y"===this.config.axis.toLowerCase()&&(n=0),"x"===this.config.axis.toLowerCase()&&(c=0);const a=window.screenX+e.left+e.width/2,s=window.screenY+e.top+e.height/2,r=e.left+e.width/2,l=e.top+e.height/2,p=document.createEvent("MouseEvents");p.initMouseEvent("mousedown",!0,!0,window,0,a,s,r,l,!1,!1,!1,!1,0,o.target);const d=document.createEvent("MouseEvents");d.initMouseEvent("mousemove",!0,!0,window,0,a+n,s+c,r+n,l+c,!1,!1,!1,!1,0,o.target);const u=document.createEvent("MouseEvents");u.initMouseEvent("mouseup",!0,!0,window,0,a+n,s+c,r+n,l+c,!1,!1,!1,!1,0,o.target),o.target.dispatchEvent(p),await this.waitTimeout(10),o.target.dispatchEvent(d),await this.waitTimeout(10),o.target.dispatchEvent(u)}}(e);case"close":return async function(){window.close()}();case"wait":return async function(e){await n(e.waitTime)}(e);case"eval":return async function(e){return console.log("eval!",e.code),new Promise((t=>{try{e.async?(window.eval(e.code),setTimeout((()=>{t(window.eval("window.__consentCheckResult"))}),e.timeout||250)):t(window.eval(e.code))}catch(o){console.warn("eval error",o,e.code),t(!1)}}))}(e);default:throw new Error("Unknown action type: "+e.type)}}var c=0;function n(e){return new Promise((t=>{setTimeout((()=>{t()}),e)}))}function a(){return crypto&&void 0!==crypto.randomUUID?crypto.randomUUID():Math.random().toString()}var s=class{constructor(e,t=1e3){this.id=e,this.promise=new Promise(((e,t)=>{this.resolve=e,this.reject=t})),this.timer=window.setTimeout((()=>{this.reject(new Error("timeout"))}),t)}},r={pending:new Map,sendContentMessage:null};var l={EVAL_0:()=>console.log(1),EVAL_CONSENTMANAGER_1:()=>window.__cmp&&"object"==typeof __cmp("getCMPData"),EVAL_CONSENTMANAGER_2:()=>!__cmp("consentStatus").userChoiceExists,EVAL_CONSENTMANAGER_3:()=>__cmp("setConsent",0),EVAL_CONSENTMANAGER_4:()=>__cmp("setConsent",1),EVAL_CONSENTMANAGER_5:()=>__cmp("consentStatus").userChoiceExists,EVAL_COOKIEBOT_1:()=>!!window.Cookiebot,EVAL_COOKIEBOT_2:()=>!window.Cookiebot.hasResponse&&!0===window.Cookiebot.dialog?.visible,EVAL_COOKIEBOT_3:()=>window.Cookiebot.withdraw()||!0,EVAL_COOKIEBOT_4:()=>window.Cookiebot.hide()||!0,EVAL_COOKIEBOT_5:()=>!0===window.Cookiebot.declined,EVAL_KLARO_1:()=>{const e=globalThis.klaroConfig||globalThis.klaro?.getManager&&globalThis.klaro.getManager().config;if(!e)return!0;const t=(e.services||e.apps).filter((e=>!e.required)).map((e=>e.name));if(klaro&&klaro.getManager){const e=klaro.getManager();return t.every((t=>!e.consents[t]))}if(klaroConfig&&"cookie"===klaroConfig.storageMethod){const e=klaroConfig.cookieName||klaroConfig.storageName,o=JSON.parse(decodeURIComponent(document.cookie.split(";").find((t=>t.trim().startsWith(e))).split("=")[1]));return Object.keys(o).filter((e=>t.includes(e))).every((e=>!1===o[e]))}},EVAL_KLARO_OPEN_POPUP:()=>{klaro.show(void 0,!0)},EVAL_KLARO_TRY_API_OPT_OUT:()=>{if(window.klaro&&"function"==typeof klaro.show&&"function"==typeof klaro.getManager)try{return klaro.getManager().changeAll(!1),klaro.getManager().saveAndApplyConsents(),!0}catch(e){return console.warn(e),!1}return!1},EVAL_ONETRUST_1:()=>window.OnetrustActiveGroups.split(",").filter((e=>e.length>0)).length<=1,EVAL_TRUSTARC_TOP:()=>window&&window.truste&&"0"===window.truste.eu.bindMap.prefCookie,EVAL_TRUSTARC_FRAME_TEST:()=>window&&window.QueryString&&"0"===window.QueryString.preferences,EVAL_TRUSTARC_FRAME_GTM:()=>window&&window.QueryString&&"1"===window.QueryString.gtm,EVAL_ABC_TEST:()=>document.cookie.includes("trackingconsent"),EVAL_ADROLL_0:()=>!document.cookie.includes("__adroll_fpc"),EVAL_ALMACMP_0:()=>document.cookie.includes('"name":"Google","consent":false'),EVAL_AFFINITY_SERIF_COM_0:()=>document.cookie.includes("serif_manage_cookies_viewed")&&!document.cookie.includes("serif_allow_analytics"),EVAL_ARBEITSAGENTUR_TEST:()=>document.cookie.includes("cookie_consent=denied"),EVAL_AXEPTIO_0:()=>document.cookie.includes("axeptio_authorized_vendors=%2C%2C"),EVAL_BAHN_TEST:()=>1===utag.gdpr.getSelectedCategories().length,EVAL_BING_0:()=>document.cookie.includes("AD=0"),EVAL_BLOCKSY_0:()=>document.cookie.includes("blocksy_cookies_consent_accepted=no"),EVAL_BORLABS_0:()=>!JSON.parse(decodeURIComponent(document.cookie.split(";").find((e=>-1!==e.indexOf("borlabs-cookie"))).split("=",2)[1])).consents.statistics,EVAL_BUNDESREGIERUNG_DE_0:()=>document.cookie.match("cookie-allow-tracking=0"),EVAL_CANVA_0:()=>!document.cookie.includes("gtm_fpc_engagement_event"),EVAL_CC_BANNER2_0:()=>!!document.cookie.match(/sncc=[^;]+D%3Dtrue/),EVAL_CLICKIO_0:()=>document.cookie.includes("__lxG__consent__v2_daisybit="),EVAL_CLINCH_0:()=>document.cookie.includes("ctc_rejected=1"),EVAL_COOKIECONSENT2_TEST:()=>document.cookie.includes("cc_cookie="),EVAL_COOKIECONSENT3_TEST:()=>document.cookie.includes("cc_cookie="),EVAL_COINBASE_0:()=>JSON.parse(decodeURIComponent(document.cookie.match(/cm_(eu|default)_preferences=([0-9a-zA-Z\\{\\}\\[\\]%:]*);?/)[2])).consent.length<=1,EVAL_COMPLIANZ_BANNER_0:()=>document.cookie.includes("cmplz_banner-status=dismissed"),EVAL_COOKIE_LAW_INFO_0:()=>CLI.disableAllCookies()||CLI.reject_close()||!0,EVAL_COOKIE_LAW_INFO_1:()=>-1===document.cookie.indexOf("cookielawinfo-checkbox-non-necessary=yes"),EVAL_COOKIE_LAW_INFO_DETECT:()=>!!window.CLI,EVAL_COOKIE_MANAGER_POPUP_0:()=>!1===JSON.parse(document.cookie.split(";").find((e=>e.trim().startsWith("CookieLevel"))).split("=")[1]).social,EVAL_COOKIEALERT_0:()=>document.querySelector("body").removeAttribute("style")||!0,EVAL_COOKIEALERT_1:()=>document.querySelector("body").removeAttribute("style")||!0,EVAL_COOKIEALERT_2:()=>!0===window.CookieConsent.declined,EVAL_COOKIEFIRST_0:()=>{return!1===(e=JSON.parse(decodeURIComponent(document.cookie.split(";").find((e=>-1!==e.indexOf("cookiefirst"))).trim()).split("=")[1])).performance&&!1===e.functional&&!1===e.advertising;var e},EVAL_COOKIEFIRST_1:()=>document.querySelectorAll("button[data-cookiefirst-accent-color=true][role=checkbox]:not([disabled])").forEach((e=>"true"===e.getAttribute("aria-checked")&&e.click()))||!0,EVAL_COOKIEINFORMATION_0:()=>CookieInformation.declineAllCategories()||!0,EVAL_COOKIEINFORMATION_1:()=>CookieInformation.submitAllCategories()||!0,EVAL_COOKIEINFORMATION_2:()=>document.cookie.includes("CookieInformationConsent="),EVAL_COOKIEYES_0:()=>document.cookie.includes("advertisement:no"),EVAL_DAILYMOTION_0:()=>!!document.cookie.match("dm-euconsent-v2"),EVAL_DNDBEYOND_TEST:()=>document.cookie.includes("cookie-consent=denied"),EVAL_DSGVO_0:()=>!document.cookie.includes("sp_dsgvo_cookie_settings"),EVAL_DUNELM_0:()=>document.cookie.includes("cc_functional=0")&&document.cookie.includes("cc_targeting=0"),EVAL_ETSY_0:()=>document.querySelectorAll(".gdpr-overlay-body input").forEach((e=>{e.checked=!1}))||!0,EVAL_ETSY_1:()=>document.querySelector(".gdpr-overlay-view button[data-wt-overlay-close]").click()||!0,EVAL_EU_COOKIE_COMPLIANCE_0:()=>-1===document.cookie.indexOf("cookie-agreed=2"),EVAL_EU_COOKIE_LAW_0:()=>!document.cookie.includes("euCookie"),EVAL_EZOIC_0:()=>ezCMP.handleAcceptAllClick(),EVAL_EZOIC_1:()=>!!document.cookie.match(/ez-consent-tcf/),EVAL_FIDES_DETECT_POPUP:()=>window.Fides?.initialized,EVAL_GOOGLE_0:()=>!!document.cookie.match(/SOCS=CAE/),EVAL_HEMA_TEST_0:()=>document.cookie.includes("cookies_rejected=1"),EVAL_IUBENDA_0:()=>document.querySelectorAll(".purposes-item input[type=checkbox]:not([disabled])").forEach((e=>{e.checked&&e.click()}))||!0,EVAL_IUBENDA_1:()=>!!document.cookie.match(/_iub_cs-\d+=/),EVAL_IWINK_TEST:()=>document.cookie.includes("cookie_permission_granted=no"),EVAL_JQUERY_COOKIEBAR_0:()=>!document.cookie.includes("cookies-state=accepted"),EVAL_KETCH_TEST:()=>document.cookie.includes("_ketch_consent_v1_"),EVAL_MEDIAVINE_0:()=>document.querySelectorAll('[data-name="mediavine-gdpr-cmp"] input[type=checkbox]').forEach((e=>e.checked&&e.click()))||!0,EVAL_MICROSOFT_0:()=>Array.from(document.querySelectorAll("div > button")).filter((e=>e.innerText.match("Reject|Ablehnen")))[0].click()||!0,EVAL_MICROSOFT_1:()=>Array.from(document.querySelectorAll("div > button")).filter((e=>e.innerText.match("Accept|Annehmen")))[0].click()||!0,EVAL_MICROSOFT_2:()=>!!document.cookie.match("MSCC|GHCC"),EVAL_MOOVE_0:()=>document.querySelectorAll("#moove_gdpr_cookie_modal input").forEach((e=>{e.disabled||(e.checked="moove_gdpr_strict_cookies"===e.name||"moove_gdpr_strict_cookies"===e.id)}))||!0,EVAL_ONENINETWO_0:()=>document.cookie.includes("CC_ADVERTISING=NO")&&document.cookie.includes("CC_ANALYTICS=NO"),EVAL_OPENAI_TEST:()=>document.cookie.includes("oai-allow-ne=false"),EVAL_OPERA_0:()=>document.cookie.includes("cookie_consent_essential=true")&&!document.cookie.includes("cookie_consent_marketing=true"),EVAL_PAYPAL_0:()=>!0===document.cookie.includes("cookie_prefs"),EVAL_PRIMEBOX_0:()=>!document.cookie.includes("cb-enabled=accepted"),EVAL_PUBTECH_0:()=>document.cookie.includes("euconsent-v2")&&(document.cookie.match(/.YAAAAAAAAAAA/)||document.cookie.match(/.aAAAAAAAAAAA/)||document.cookie.match(/.YAAACFgAAAAA/)),EVAL_REDDIT_0:()=>document.cookie.includes("eu_cookie={%22opted%22:true%2C%22nonessential%22:false}"),EVAL_ROBLOX_TEST:()=>document.cookie.includes("RBXcb"),EVAL_SKYSCANNER_TEST:()=>document.cookie.match(/gdpr=[^;]*adverts:::false/)&&!document.cookie.match(/gdpr=[^;]*init:::true/),EVAL_SIRDATA_UNBLOCK_SCROLL:()=>(document.documentElement.classList.forEach((e=>{e.startsWith("sd-cmp-")&&document.documentElement.classList.remove(e)})),!0),EVAL_SNIGEL_0:()=>!!document.cookie.match("snconsent"),EVAL_STEAMPOWERED_0:()=>2===JSON.parse(decodeURIComponent(document.cookie.split(";").find((e=>e.trim().startsWith("cookieSettings"))).split("=")[1])).preference_state,EVAL_SVT_TEST:()=>document.cookie.includes('cookie-consent-1={"optedIn":true,"functionality":false,"statistics":false}'),EVAL_TAKEALOT_0:()=>document.body.classList.remove("freeze")||(document.body.style="")||!0,EVAL_TARTEAUCITRON_0:()=>tarteaucitron.userInterface.respondAll(!1)||!0,EVAL_TARTEAUCITRON_1:()=>tarteaucitron.userInterface.respondAll(!0)||!0,EVAL_TARTEAUCITRON_2:()=>document.cookie.match(/tarteaucitron=[^;]*/)?.[0].includes("false"),EVAL_TAUNTON_TEST:()=>document.cookie.includes("taunton_user_consent_submitted=true"),EVAL_TEALIUM_0:()=>void 0!==window.utag&&"object"==typeof utag.gdpr,EVAL_TEALIUM_1:()=>utag.gdpr.setConsentValue(!1)||!0,EVAL_TEALIUM_DONOTSELL:()=>utag.gdpr.dns?.setDnsState(!1)||!0,EVAL_TEALIUM_2:()=>utag.gdpr.setConsentValue(!0)||!0,EVAL_TEALIUM_3:()=>1!==utag.gdpr.getConsentState(),EVAL_TEALIUM_DONOTSELL_CHECK:()=>1!==utag.gdpr.dns?.getDnsState(),EVAL_TESLA_TEST:()=>document.cookie.includes("tsla-cookie-consent=rejected"),EVAL_TESTCMP_STEP:()=>!!document.querySelector("#reject-all"),EVAL_TESTCMP_0:()=>"button_clicked"===window.results.results[0],EVAL_TESTCMP_COSMETIC_0:()=>"banner_hidden"===window.results.results[0],EVAL_THEFREEDICTIONARY_0:()=>cmpUi.showPurposes()||cmpUi.rejectAll()||!0,EVAL_THEFREEDICTIONARY_1:()=>cmpUi.allowAll()||!0,EVAL_THEVERGE_0:()=>document.cookie.includes("_duet_gdpr_acknowledged=1"),EVAL_TWCC_TEST:()=>document.cookie.includes("twCookieConsent="),EVAL_UBUNTU_COM_0:()=>document.cookie.includes("_cookies_accepted=essential"),EVAL_UK_COOKIE_CONSENT_0:()=>!document.cookie.includes("catAccCookies"),EVAL_USERCENTRICS_API_0:()=>"object"==typeof UC_UI,EVAL_USERCENTRICS_API_1:()=>!!UC_UI.closeCMP(),EVAL_USERCENTRICS_API_2:()=>!!UC_UI.denyAllConsents(),EVAL_USERCENTRICS_API_3:()=>!!UC_UI.acceptAllConsents(),EVAL_USERCENTRICS_API_4:()=>!!UC_UI.closeCMP(),EVAL_USERCENTRICS_API_5:()=>!0===UC_UI.areAllConsentsAccepted(),EVAL_USERCENTRICS_API_6:()=>!1===UC_UI.areAllConsentsAccepted(),EVAL_USERCENTRICS_BUTTON_0:()=>JSON.parse(localStorage.getItem("usercentrics")).consents.every((e=>e.isEssential||!e.consentStatus)),EVAL_WAITROSE_0:()=>Array.from(document.querySelectorAll("label[id$=cookies-deny-label]")).forEach((e=>e.click()))||!0,EVAL_WAITROSE_1:()=>document.cookie.includes("wtr_cookies_advertising=0")&&document.cookie.includes("wtr_cookies_analytics=0"),EVAL_WP_COOKIE_NOTICE_0:()=>document.cookie.includes("wpl_viewed_cookie=no"),EVAL_XE_TEST:()=>document.cookie.includes("xeConsentState={%22performance%22:false%2C%22marketing%22:false%2C%22compliance%22:false}"),EVAL_XING_0:()=>document.cookie.includes("userConsent=%7B%22marketing%22%3Afalse"),EVAL_YOUTUBE_DESKTOP_0:()=>!!document.cookie.match(/SOCS=CAE/),EVAL_YOUTUBE_MOBILE_0:()=>!!document.cookie.match(/SOCS=CAE/)};var p={main:!0,frame:!1,urlPattern:""},d=class{constructor(e){this.runContext=p,this.autoconsent=e}get hasSelfTest(){throw new Error("Not Implemented")}get isIntermediate(){throw new Error("Not Implemented")}get isCosmetic(){throw new Error("Not Implemented")}mainWorldEval(e){const t=l[e];if(!t)return console.warn("Snippet not found",e),Promise.resolve(!1);const o=this.autoconsent.config.logs;if(this.autoconsent.config.isMainWorld){o.evals&&console.log("inline eval:",e,t);let i=!1;try{i=!!t.call(globalThis)}catch(t){o.evals&&console.error("error evaluating rule",e,t)}return Promise.resolve(i)}const i=`(${t.toString()})()`;return o.evals&&console.log("async eval:",e,i),function(e,t){const o=a();r.sendContentMessage({type:"eval",id:o,code:e,snippetId:t});const i=new s(o);return r.pending.set(i.id,i),i.promise}(i,e).catch((t=>(o.evals&&console.error("error evaluating rule",e,t),!1)))}checkRunContext(){const e={...p,...this.runContext},t=window.top===window;return!(t&&!e.main)&&(!(!t&&!e.frame)&&!(e.urlPattern&&!window.location.href.match(e.urlPattern)))}detectCmp(){throw new Error("Not Implemented")}async detectPopup(){return!1}optOut(){throw new Error("Not Implemented")}optIn(){throw new Error("Not Implemented")}openCmp(){throw new Error("Not Implemented")}async test(){return Promise.resolve(!0)}click(e,t=!1){return this.autoconsent.domActions.click(e,t)}elementExists(e){return this.autoconsent.domActions.elementExists(e)}elementVisible(e,t){return this.autoconsent.domActions.elementVisible(e,t)}waitForElement(e,t){return this.autoconsent.domActions.waitForElement(e,t)}waitForVisible(e,t,o){return this.autoconsent.domActions.waitForVisible(e,t,o)}waitForThenClick(e,t,o){return this.autoconsent.domActions.waitForThenClick(e,t,o)}wait(e){return this.autoconsent.domActions.wait(e)}hide(e,t){return this.autoconsent.domActions.hide(e,t)}prehide(e){return this.autoconsent.domActions.prehide(e)}undoPrehide(){return this.autoconsent.domActions.undoPrehide()}querySingleReplySelector(e,t){return this.autoconsent.domActions.querySingleReplySelector(e,t)}querySelectorChain(e){return this.autoconsent.domActions.querySelectorChain(e)}elementSelector(e){return this.autoconsent.domActions.elementSelector(e)}},u=class extends d{constructor(e,t){super(t),this.rule=e,this.name=e.name,this.runContext=e.runContext||p}get hasSelfTest(){return!!this.rule.test}get isIntermediate(){return!!this.rule.intermediate}get isCosmetic(){return!!this.rule.cosmetic}get prehideSelectors(){return this.rule.prehideSelectors}async detectCmp(){return!!this.rule.detectCmp&&this._runRulesParallel(this.rule.detectCmp)}async detectPopup(){return!!this.rule.detectPopup&&this._runRulesSequentially(this.rule.detectPopup)}async optOut(){const e=this.autoconsent.config.logs;return!!this.rule.optOut&&(e.lifecycle&&console.log("Initiated optOut()",this.rule.optOut),this._runRulesSequentially(this.rule.optOut))}async optIn(){const e=this.autoconsent.config.logs;return!!this.rule.optIn&&(e.lifecycle&&console.log("Initiated optIn()",this.rule.optIn),this._runRulesSequentially(this.rule.optIn))}async openCmp(){return!!this.rule.openCmp&&this._runRulesSequentially(this.rule.openCmp)}async test(){return this.hasSelfTest?this._runRulesSequentially(this.rule.test):super.test()}async evaluateRuleStep(e){const t=[],o=this.autoconsent.config.logs;if(e.exists&&t.push(this.elementExists(e.exists)),e.visible&&t.push(this.elementVisible(e.visible,e.check)),e.eval){const o=this.mainWorldEval(e.eval);t.push(o)}if(e.waitFor&&t.push(this.waitForElement(e.waitFor,e.timeout)),e.waitForVisible&&t.push(this.waitForVisible(e.waitForVisible,e.timeout,e.check)),e.click&&t.push(this.click(e.click,e.all)),e.waitForThenClick&&t.push(this.waitForThenClick(e.waitForThenClick,e.timeout,e.all)),e.wait&&t.push(this.wait(e.wait)),e.hide&&t.push(this.hide(e.hide,e.method)),e.if){if(!e.if.exists&&!e.if.visible)return console.error("invalid conditional rule",e.if),!1;const i=await this.evaluateRuleStep(e.if);o.rulesteps&&console.log("Condition is",i),i?t.push(this._runRulesSequentially(e.then)):e.else?t.push(this._runRulesSequentially(e.else)):t.push(!0)}if(e.any){for(const t of e.any)if(await this.evaluateRuleStep(t))return!0;return!1}if(0===t.length)return o.errors&&console.warn("Unrecognized rule",e),!1;return(await Promise.all(t)).reduce(((e,t)=>e&&t),!0)}async _runRulesParallel(e){const t=e.map((e=>this.evaluateRuleStep(e)));return(await Promise.all(t)).every((e=>!!e))}async _runRulesSequentially(e){const t=this.autoconsent.config.logs;for(const o of e){t.rulesteps&&console.log("Running rule...",o);const e=await this.evaluateRuleStep(o);if(t.rulesteps&&console.log("...rule result",e),!e&&!o.optional)return!1}return!0}},m=class{constructor(e,t){this.name=e,this.config=t,this.methods=new Map,this.runContext=p,this.isCosmetic=!1,t.methods.forEach((e=>{e.action&&this.methods.set(e.name,e.action)})),this.hasSelfTest=!1}get isIntermediate(){return!1}checkRunContext(){return!0}async detectCmp(){return this.config.detectors.map((e=>o(e.presentMatcher))).some((e=>!!e))}async detectPopup(){return this.config.detectors.map((e=>o(e.showingMatcher))).some((e=>!!e))}async executeAction(e,t){return!this.methods.has(e)||i(this.methods.get(e),t)}async optOut(){return await this.executeAction("HIDE_CMP"),await this.executeAction("OPEN_OPTIONS"),await this.executeAction("HIDE_CMP"),await this.executeAction("DO_CONSENT",[]),await this.executeAction("SAVE_CONSENT"),!0}async optIn(){return await this.executeAction("HIDE_CMP"),await this.executeAction("OPEN_OPTIONS"),await this.executeAction("HIDE_CMP"),await this.executeAction("DO_CONSENT",["D","A","B","E","F","X"]),await this.executeAction("SAVE_CONSENT"),!0}async openCmp(){return await this.executeAction("HIDE_CMP"),await this.executeAction("OPEN_OPTIONS"),!0}async test(){return!0}};function A(e="autoconsent-css-rules"){const t=`style#${e}`,o=document.querySelector(t);if(o&&o instanceof HTMLStyleElement)return o;{const t=document.head||document.getElementsByTagName("head")[0]||document.documentElement,o=document.createElement("style");return o.id=e,t.appendChild(o),o}}function h(e,t,o="display"){const i=`${t} { ${function(e){return("opacity"===e?"opacity: 0":"display: none")+" !important; z-index: -1 !important; pointer-events: none !important;"}(o)} } `;return e instanceof HTMLStyleElement&&(e.innerText+=i,t.length>0)}async function k(e,t,o){const i=await e();return!i&&t>0?new Promise((i=>{setTimeout((async()=>{i(k(e,t-1,o))}),o)})):Promise.resolve(i)}function b(e){if(!e)return!1;if(null!==e.offsetParent)return!0;{const t=window.getComputedStyle(e);if("fixed"===t.position&&"none"!==t.display)return!0}return!1}function g(e){const t={enabled:!0,autoAction:"optOut",disabledCmps:[],enablePrehide:!0,enableCosmeticRules:!0,enableHeuristicDetection:!1,detectRetries:20,isMainWorld:!1,prehideTimeout:2e3,enableFilterList:!1,logs:{lifecycle:!1,rulesteps:!1,evals:!1,errors:!0,messages:!1,waits:!1}},o=(i=t,globalThis.structuredClone?structuredClone(i):JSON.parse(JSON.stringify(i)));var i;for(const i of Object.keys(t))void 0!==e[i]&&(o[i]=e[i]);return o}var _="#truste-show-consent",y="#truste-consent-track",w=[class extends d{constructor(e){super(e),this.name="TrustArc-top",this.prehideSelectors=[".trustarc-banner-container",`.truste_popframe,.truste_overlay,.truste_box_overlay,${y}`],this.runContext={main:!0,frame:!1},this._shortcutButton=null,this._optInDone=!1}get hasSelfTest(){return!0}get isIntermediate(){return!this._optInDone&&!this._shortcutButton}get isCosmetic(){return!1}async detectCmp(){const e=this.elementExists(`${_},${y}`);return e&&(this._shortcutButton=document.querySelector("#truste-consent-required")),e}async detectPopup(){return this.elementVisible(`#truste-consent-content,#trustarc-banner-overlay,${y}`,"any")}openFrame(){this.click(_)}async optOut(){return this._shortcutButton?(this._shortcutButton.click(),!0):(h(A(),`.truste_popframe, .truste_overlay, .truste_box_overlay, ${y}`),this.click(_),setTimeout((()=>{A().remove()}),1e4),!0)}async optIn(){return this._optInDone=!0,this.click("#truste-consent-button")}async openCmp(){return!0}async test(){return await this.wait(500),await this.mainWorldEval("EVAL_TRUSTARC_TOP")}},class extends d{constructor(){super(...arguments),this.name="TrustArc-frame",this.runContext={main:!1,frame:!0,urlPattern:"^https://consent-pref\\.trustarc\\.com/\\?"}}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return!0}async detectPopup(){return this.elementVisible("#defaultpreferencemanager","any")&&this.elementVisible(".mainContent","any")}async navigateToSettings(){return await k((async()=>this.elementExists(".shp")||this.elementVisible(".advance","any")||this.elementExists(".switch span:first-child")),10,500),this.elementExists(".shp")&&this.click(".shp"),await this.waitForElement(".prefPanel",5e3),this.elementVisible(".advance","any")&&this.click(".advance"),await k((()=>this.elementVisible(".switch span:first-child","any")),5,1e3)}async optOut(){if(await this.mainWorldEval("EVAL_TRUSTARC_FRAME_TEST"))return!0;let e=3e3;return await this.mainWorldEval("EVAL_TRUSTARC_FRAME_GTM")&&(e=1500),await k((()=>"complete"===document.readyState),20,100),await this.waitForElement(".mainContent[aria-hidden=false]",e),!!this.click(".rejectAll")||(this.elementExists(".prefPanel")&&await this.waitForElement('.prefPanel[style="visibility: visible;"]',e),this.click("#catDetails0")?(this.click(".submit"),this.waitForThenClick("#gwt-debug-close_id",e),!0):this.click(".required")?(this.waitForThenClick("#gwt-debug-close_id",e),!0):(await this.navigateToSettings(),this.click(".switch span:nth-child(1):not(.active)",!0),this.click(".submit"),this.waitForThenClick("#gwt-debug-close_id",10*e),!0))}async optIn(){return this.click(".call")||(await this.navigateToSettings(),this.click(".switch span:nth-child(2)",!0),this.click(".submit"),this.waitForElement("#gwt-debug-close_id",3e5).then((()=>{this.click("#gwt-debug-close_id")}))),!0}async test(){return await this.wait(500),await this.mainWorldEval("EVAL_TRUSTARC_FRAME_TEST")}},class extends d{constructor(){super(...arguments),this.name="Cybotcookiebot",this.prehideSelectors=["#CybotCookiebotDialog,#CybotCookiebotDialogBodyUnderlay,#dtcookie-container,#cookiebanner,#cb-cookieoverlay,.modal--cookie-banner,#cookiebanner_outer,#CookieBanner"]}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return await this.mainWorldEval("EVAL_COOKIEBOT_1")}async detectPopup(){return this.mainWorldEval("EVAL_COOKIEBOT_2")}async optOut(){await this.wait(500);let e=await this.mainWorldEval("EVAL_COOKIEBOT_3");return await this.wait(500),e=e&&await this.mainWorldEval("EVAL_COOKIEBOT_4"),e}async optIn(){return this.elementExists("#dtcookie-container")?this.click(".h-dtcookie-accept"):(this.click(".CybotCookiebotDialogBodyLevelButton:not(:checked):enabled",!0),this.click("#CybotCookiebotDialogBodyLevelButtonAccept"),this.click("#CybotCookiebotDialogBodyButtonAccept"),!0)}async test(){return await this.wait(500),await this.mainWorldEval("EVAL_COOKIEBOT_5")}},class extends d{constructor(){super(...arguments),this.name="Sourcepoint-frame",this.prehideSelectors=["div[id^='sp_message_container_'],.message-overlay","#sp_privacy_manager_container"],this.ccpaNotice=!1,this.ccpaPopup=!1,this.runContext={main:!0,frame:!0}}get hasSelfTest(){return!1}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){const e=new URL(location.href);return e.searchParams.has("message_id")&&"ccpa-notice.sp-prod.net"===e.hostname?(this.ccpaNotice=!0,!0):"ccpa-pm.sp-prod.net"===e.hostname?(this.ccpaPopup=!0,!0):("/index.html"===e.pathname||"/privacy-manager/index.html"===e.pathname||"/ccpa_pm/index.html"===e.pathname)&&(e.searchParams.has("message_id")||e.searchParams.has("requestUUID")||e.searchParams.has("consentUUID"))}async detectPopup(){return!!this.ccpaNotice||(this.ccpaPopup?await this.waitForElement(".priv-save-btn",2e3):(await this.waitForElement(".sp_choice_type_11,.sp_choice_type_12,.sp_choice_type_13,.sp_choice_type_ACCEPT_ALL,.sp_choice_type_SAVE_AND_EXIT",2e3),!this.elementExists(".sp_choice_type_9")))}async optIn(){return await this.waitForElement(".sp_choice_type_11,.sp_choice_type_ACCEPT_ALL",2e3),!!this.click(".sp_choice_type_11")||!!this.click(".sp_choice_type_ACCEPT_ALL")}isManagerOpen(){return"/privacy-manager/index.html"===location.pathname||"/ccpa_pm/index.html"===location.pathname}async optOut(){const e=this.autoconsent.config.logs;if(this.ccpaPopup){const e=document.querySelectorAll(".priv-purpose-container .sp-switch-arrow-block a.neutral.on .right");for(const t of e)t.click();const t=document.querySelectorAll(".priv-purpose-container .sp-switch-arrow-block a.switch-bg.on");for(const e of t)e.click();return this.click(".priv-save-btn")}if(!this.isManagerOpen()){if(!await this.waitForElement(".sp_choice_type_12,.sp_choice_type_13"))return!1;if(!this.elementExists(".sp_choice_type_12"))return this.click(".sp_choice_type_13");this.click(".sp_choice_type_12"),await k((()=>this.isManagerOpen()),200,100)}await this.waitForElement(".type-modal",2e4),this.waitForThenClick(".ccpa-stack .pm-switch[aria-checked=true] .slider",500,!0);try{const e=".sp_choice_type_REJECT_ALL",t=".reject-toggle",o=await Promise.race([this.waitForElement(e,2e3).then((e=>e?0:-1)),this.waitForElement(t,2e3).then((e=>e?1:-1)),this.waitForElement(".pm-features",2e3).then((e=>e?2:-1))]);if(0===o)return await this.waitForVisible(e),this.click(e);1===o?this.click(t):2===o&&(await this.waitForElement(".pm-features",1e4),this.click(".checked > span",!0),this.click(".chevron"))}catch(t){e.errors&&console.warn(t)}return this.click(".sp_choice_type_SAVE_AND_EXIT")}},class extends d{constructor(){super(...arguments),this.name="consentmanager.net",this.prehideSelectors=["#cmpbox,#cmpbox2"],this.apiAvailable=!1}get hasSelfTest(){return this.apiAvailable}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.apiAvailable=await this.mainWorldEval("EVAL_CONSENTMANAGER_1"),!!this.apiAvailable||this.elementExists("#cmpbox")}async detectPopup(){return this.apiAvailable?(await this.wait(500),await this.mainWorldEval("EVAL_CONSENTMANAGER_2")):this.elementVisible("#cmpbox .cmpmore","any")}async optOut(){return await this.wait(500),this.apiAvailable?await this.mainWorldEval("EVAL_CONSENTMANAGER_3"):!!this.click(".cmpboxbtnno")||(this.elementExists(".cmpwelcomeprpsbtn")?(this.click(".cmpwelcomeprpsbtn > a[aria-checked=true]",!0),this.click(".cmpboxbtnsave"),!0):(this.click(".cmpboxbtncustom"),await this.waitForElement(".cmptblbox",2e3),this.click(".cmptdchoice > a[aria-checked=true]",!0),this.click(".cmpboxbtnyescustomchoices"),this.hide("#cmpwrapper,#cmpbox","display"),!0))}async optIn(){return this.apiAvailable?await this.mainWorldEval("EVAL_CONSENTMANAGER_4"):this.click(".cmpboxbtnyes")}async test(){if(this.apiAvailable)return await this.mainWorldEval("EVAL_CONSENTMANAGER_5")}},class extends d{constructor(){super(...arguments),this.name="Evidon"}get hasSelfTest(){return!1}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists("#_evidon_banner")}async detectPopup(){return this.elementVisible("#_evidon_banner","any")}async optOut(){return this.click("#_evidon-decline-button")||(h(A(),"#evidon-prefdiag-overlay,#evidon-prefdiag-background,#_evidon-background"),await this.waitForThenClick("#_evidon-option-button"),await this.waitForElement("#evidon-prefdiag-overlay",5e3),await this.wait(500),await this.waitForThenClick("#evidon-prefdiag-decline")),!0}async optIn(){return this.click("#_evidon-accept-button")}},class extends d{constructor(){super(...arguments),this.name="Onetrust",this.prehideSelectors=["#onetrust-banner-sdk,#onetrust-consent-sdk,.onetrust-pc-dark-filter,.js-consent-banner"],this.runContext={urlPattern:"^(?!.*https://www\\.nba\\.com/)"}}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists("#onetrust-banner-sdk,#onetrust-pc-sdk")}async detectPopup(){return this.elementVisible("#onetrust-banner-sdk,#onetrust-pc-sdk","any")}async optOut(){return this.elementVisible("#onetrust-reject-all-handler,.ot-pc-refuse-all-handler,.js-reject-cookies","any")?this.click("#onetrust-reject-all-handler,.ot-pc-refuse-all-handler,.js-reject-cookies"):(this.elementExists("#onetrust-pc-btn-handler")?this.click("#onetrust-pc-btn-handler"):this.click(".ot-sdk-show-settings,button.js-cookie-settings"),await this.waitForElement("#onetrust-consent-sdk",2e3),await this.wait(1e3),this.click("#onetrust-consent-sdk input.category-switch-handler:checked,.js-editor-toggle-state:checked",!0),await this.wait(1e3),await this.waitForElement(".save-preference-btn-handler,.js-consent-save",2e3),this.click(".save-preference-btn-handler,.js-consent-save"),await this.waitForVisible("#onetrust-banner-sdk",5e3,"none"),!0)}async optIn(){return this.click("#onetrust-accept-btn-handler,#accept-recommended-btn-handler,.js-accept-cookies")}async test(){return await k((()=>this.mainWorldEval("EVAL_ONETRUST_1")),10,500)}},class extends d{constructor(){super(...arguments),this.name="Klaro",this.prehideSelectors=[".klaro"],this.settingsOpen=!1}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists(".klaro > .cookie-modal")?(this.settingsOpen=!0,!0):this.elementExists(".klaro > .cookie-notice")}async detectPopup(){return this.elementVisible(".klaro > .cookie-notice,.klaro > .cookie-modal","any")}async optOut(){return!!await this.mainWorldEval("EVAL_KLARO_TRY_API_OPT_OUT")||(!!this.click(".klaro .cn-decline")||(await this.mainWorldEval("EVAL_KLARO_OPEN_POPUP"),!!this.click(".klaro .cn-decline")||(this.click(".cm-purpose:not(.cm-toggle-all) > input:not(.half-checked,.required,.only-required),.cm-purpose:not(.cm-toggle-all) > div > input:not(.half-checked,.required,.only-required)",!0),this.click(".cm-btn-accept,.cm-button"))))}async optIn(){return!!this.click(".klaro .cm-btn-accept-all")||(this.settingsOpen?(this.click(".cm-purpose:not(.cm-toggle-all) > input.half-checked",!0),this.click(".cm-btn-accept")):this.click(".klaro .cookie-notice .cm-btn-success"))}async test(){return await this.mainWorldEval("EVAL_KLARO_1")}},class extends d{constructor(){super(...arguments),this.name="Uniconsent"}get prehideSelectors(){return[".unic",".modal:has(.unic)"]}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists(".unic .unic-box,.unic .unic-bar,.unic .unic-modal")}async detectPopup(){return this.elementVisible(".unic .unic-box,.unic .unic-bar,.unic .unic-modal","any")}async optOut(){if(await this.waitForElement(".unic button",1e3),document.querySelectorAll(".unic button").forEach((e=>{const t=e.textContent;(t.includes("Manage Options")||t.includes("Optionen verwalten"))&&e.click()})),await this.waitForElement(".unic input[type=checkbox]",1e3)){await this.waitForElement(".unic button",1e3),document.querySelectorAll(".unic input[type=checkbox]").forEach((e=>{e.checked&&e.click()}));for(const e of document.querySelectorAll(".unic button")){const t=e.textContent;for(const o of["Confirm Choices","Save Choices","Auswahl speichern"])if(t.includes(o))return e.click(),await this.wait(500),!0}}return!1}async optIn(){return this.waitForThenClick(".unic #unic-agree")}async test(){await this.wait(1e3);return!this.elementExists(".unic .unic-box,.unic .unic-bar")}},class extends d{constructor(){super(...arguments),this.prehideSelectors=[".cmp-root"],this.name="Conversant"}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists(".cmp-root .cmp-receptacle")}async detectPopup(){return this.elementVisible(".cmp-root .cmp-receptacle","any")}async optOut(){if(!await this.waitForThenClick(".cmp-main-button:not(.cmp-main-button--primary)"))return!1;if(!await this.waitForElement(".cmp-view-tab-tabs"))return!1;await this.waitForThenClick(".cmp-view-tab-tabs > :first-child"),await this.waitForThenClick(".cmp-view-tab-tabs > .cmp-view-tab--active:first-child");for(const e of Array.from(document.querySelectorAll(".cmp-accordion-item"))){e.querySelector(".cmp-accordion-item-title").click(),await k((()=>!!e.querySelector(".cmp-accordion-item-content.cmp-active")),10,50);const t=e.querySelector(".cmp-accordion-item-content.cmp-active");t.querySelectorAll(".cmp-toggle-actions .cmp-toggle-deny:not(.cmp-toggle-deny--active)").forEach((e=>e.click())),t.querySelectorAll(".cmp-toggle-actions .cmp-toggle-checkbox:not(.cmp-toggle-checkbox--active)").forEach((e=>e.click()))}return await this.click(".cmp-main-button:not(.cmp-main-button--primary)"),!0}async optIn(){return this.waitForThenClick(".cmp-main-button.cmp-main-button--primary")}async test(){return document.cookie.includes("cmp-data=0")}},class extends d{constructor(){super(...arguments),this.name="tiktok.com",this.runContext={urlPattern:"tiktok"}}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}getShadowRoot(){const e=document.querySelector("tiktok-cookie-banner");return e?e.shadowRoot:null}async detectCmp(){return this.elementExists("tiktok-cookie-banner")}async detectPopup(){return b(this.getShadowRoot().querySelector(".tiktok-cookie-banner"))}async optOut(){const e=this.autoconsent.config.logs,t=this.getShadowRoot().querySelector(".button-wrapper button:first-child");return t?(e.rulesteps&&console.log("[clicking]",t),t.click(),!0):(e.errors&&console.log("no decline button found"),!1)}async optIn(){const e=this.autoconsent.config.logs,t=this.getShadowRoot().querySelector(".button-wrapper button:last-child");return t?(e.rulesteps&&console.log("[clicking]",t),t.click(),!0):(e.errors&&console.log("no accept button found"),!1)}async test(){const e=document.cookie.match(/cookie-consent=([^;]+)/);if(!e)return!1;const t=JSON.parse(decodeURIComponent(e[1]));return Object.values(t).every((e=>"boolean"!=typeof e||!1===e))}},class extends d{constructor(){super(...arguments),this.name="airbnb",this.runContext={urlPattern:"^https://(www\\.)?airbnb\\.[^/]+/"},this.prehideSelectors=["div[data-testid=main-cookies-banner-container]",'div:has(> div:first-child):has(> div:last-child):has(> section [data-testid="strictly-necessary-cookies"])']}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists("div[data-testid=main-cookies-banner-container]")}async detectPopup(){return this.elementVisible("div[data-testid=main-cookies-banner-container","any")}async optOut(){let e;for(await this.waitForThenClick("div[data-testid=main-cookies-banner-container] button._snbhip0");e=document.querySelector("[data-testid=modal-container] button[aria-checked=true]:not([disabled])");)e.click();return this.waitForThenClick("button[data-testid=save-btn]")}async optIn(){return this.waitForThenClick("div[data-testid=main-cookies-banner-container] button._148dgdpk")}async test(){return await k((()=>!!document.cookie.match("OptanonAlertBoxClosed")),20,200)}},class extends d{constructor(){super(...arguments),this.name="tumblr-com",this.runContext={urlPattern:"^https://(www\\.)?tumblr\\.com/"}}get hasSelfTest(){return!1}get isIntermediate(){return!1}get isCosmetic(){return!1}get prehideSelectors(){return["#cmp-app-container"]}async detectCmp(){return this.elementExists("#cmp-app-container")}async detectPopup(){return this.elementVisible("#cmp-app-container","any")}async optOut(){let e=document.querySelector("#cmp-app-container iframe"),t=e.contentDocument?.querySelector(".cmp-components-button.is-secondary");return!!t&&(t.click(),await k((()=>{const e=document.querySelector("#cmp-app-container iframe");return!!e.contentDocument?.querySelector(".cmp__dialog input")}),5,500),e=document.querySelector("#cmp-app-container iframe"),t=e.contentDocument?.querySelector(".cmp-components-button.is-secondary"),!!t&&(t.click(),!0))}async optIn(){const e=document.querySelector("#cmp-app-container iframe").contentDocument.querySelector(".cmp-components-button.is-primary");return!!e&&(e.click(),!0)}},class extends d{constructor(){super(...arguments),this.name="Admiral"}get hasSelfTest(){return!1}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.elementExists("div > div[class*=Card] > div[class*=Frame] > div[class*=Pills] > button[class*=Pills__StyledPill]")}async detectPopup(){return this.elementVisible("div > div[class*=Card] > div[class*=Frame] > div[class*=Pills] > button[class*=Pills__StyledPill]","any")}async optOut(){const e="xpath///button[contains(., 'Afvis alle') or contains(., 'Reject all') or contains(., 'Odbaci sve') or contains(., 'Rechazar todo') or contains(., 'Atmesti visus') or contains(., 'Odmítnout vše') or contains(., 'Απόρριψη όλων') or contains(., 'Rejeitar tudo') or contains(., 'Tümünü reddet') or contains(., 'Отклонить все') or contains(., 'Noraidīt visu') or contains(., 'Avvisa alla') or contains(., 'Odrzuć wszystkie') or contains(., 'Alles afwijzen') or contains(., 'Отхвърляне на всички') or contains(., 'Rifiuta tutto') or contains(., 'Zavrni vse') or contains(., 'Az összes elutasítása') or contains(., 'Respingeți tot') or contains(., 'Alles ablehnen') or contains(., 'Tout rejeter') or contains(., 'Odmietnuť všetko') or contains(., 'Lükka kõik tagasi') or contains(., 'Hylkää kaikki')]";if(await this.waitForElement(e,500))return this.click(e);const t="xpath///button[contains(., 'Spara & avsluta') or contains(., 'Save & exit') or contains(., 'Uložit a ukončit') or contains(., 'Enregistrer et quitter') or contains(., 'Speichern & Verlassen') or contains(., 'Tallenna ja poistu') or contains(., 'Išsaugoti ir išeiti') or contains(., 'Opslaan & afsluiten') or contains(., 'Guardar y salir') or contains(., 'Shrani in zapri') or contains(., 'Uložiť a ukončiť') or contains(., 'Kaydet ve çıkış yap') or contains(., 'Сохранить и выйти') or contains(., 'Salvesta ja välju') or contains(., 'Salva ed esci') or contains(., 'Gem & afslut') or contains(., 'Αποθήκευση και έξοδος') or contains(., 'Saglabāt un iziet') or contains(., 'Mentés és kilépés') or contains(., 'Guardar e sair') or contains(., 'Zapisz & zakończ') or contains(., 'Salvare și ieșire') or contains(., 'Spremi i izađi') or contains(., 'Запазване и изход')]";if(await this.waitForThenClick("xpath///button[contains(., 'Zwecke') or contains(., 'Σκοποί') or contains(., 'Purposes') or contains(., 'Цели') or contains(., 'Eesmärgid') or contains(., 'Tikslai') or contains(., 'Svrhe') or contains(., 'Cele') or contains(., 'Účely') or contains(., 'Finalidades') or contains(., 'Mērķi') or contains(., 'Scopuri') or contains(., 'Fines') or contains(., 'Ändamål') or contains(., 'Finalités') or contains(., 'Doeleinden') or contains(., 'Tarkoitukset') or contains(., 'Scopi') or contains(., 'Amaçlar') or contains(., 'Nameni') or contains(., 'Célok') or contains(., 'Formål')]")&&await this.waitForVisible(t)){return this.elementSelector(t)[0].parentElement.parentElement.querySelectorAll("input[type=checkbox]:checked").forEach((e=>e.click())),this.click(t)}return!1}async optIn(){return this.click("xpath///button[contains(., 'Sprejmi vse') or contains(., 'Prihvati sve') or contains(., 'Godkänn alla') or contains(., 'Prijať všetko') or contains(., 'Принять все') or contains(., 'Aceptar todo') or contains(., 'Αποδοχή όλων') or contains(., 'Zaakceptuj wszystkie') or contains(., 'Accetta tutto') or contains(., 'Priimti visus') or contains(., 'Pieņemt visu') or contains(., 'Tümünü kabul et') or contains(., 'Az összes elfogadása') or contains(., 'Accept all') or contains(., 'Приемане на всички') or contains(., 'Accepter alle') or contains(., 'Hyväksy kaikki') or contains(., 'Tout accepter') or contains(., 'Alles accepteren') or contains(., 'Aktsepteeri kõik') or contains(., 'Přijmout vše') or contains(., 'Alles akzeptieren') or contains(., 'Aceitar tudo') or contains(., 'Acceptați tot')]")}}],C=class{constructor(e){this.autoconsentInstance=e}click(e,t=!1){const o=this.elementSelector(e);return this.autoconsentInstance.config.logs.rulesteps&&console.log("[click]",e,t,o),o.length>0&&(t?o.forEach((e=>e.click())):o[0].click()),o.length>0}elementExists(e){return this.elementSelector(e).length>0}elementVisible(e,t){const o=this.elementSelector(e),i=new Array(o.length);return o.forEach(((e,t)=>{i[t]=b(e)})),"none"===t?i.every((e=>!e)):0!==i.length&&("any"===t?i.some((e=>e)):i.every((e=>e)))}waitForElement(e,t=1e4){const o=Math.ceil(t/200);return this.autoconsentInstance.config.logs.rulesteps&&console.log("[waitForElement]",e),k((()=>this.elementSelector(e).length>0),o,200)}waitForVisible(e,t=1e4,o="any"){const i=Math.ceil(t/200);return this.autoconsentInstance.config.logs.rulesteps&&console.log("[waitForVisible]",e),k((()=>this.elementVisible(e,o)),i,200)}async waitForThenClick(e,t=1e4,o=!1){return await this.waitForElement(e,t),this.click(e,o)}wait(e){return this.autoconsentInstance.config.logs.rulesteps&&this.autoconsentInstance.config.logs.waits&&console.log("[wait]",e),new Promise((t=>{setTimeout((()=>{t(!0)}),e)}))}hide(e,t){this.autoconsentInstance.config.logs.rulesteps&&console.log("[hide]",e);return h(A(),e,t)}prehide(e){const t=A("autoconsent-prehide");return this.autoconsentInstance.config.logs.lifecycle&&console.log("[prehide]",t,location.href),h(t,e,"opacity")}undoPrehide(){const e=A("autoconsent-prehide");return this.autoconsentInstance.config.logs.lifecycle&&console.log("[undoprehide]",e,location.href),e&&e.remove(),!!e}async createOrUpdateStyleSheet(e,t){return t||(t=new CSSStyleSheet),t=await t.replace(e)}removeStyleSheet(e){return!!e&&(e.replace(""),!0)}querySingleReplySelector(e,t=document){if(e.startsWith("aria/"))return[];if(e.startsWith("xpath/")){const o=e.slice(6),i=document.evaluate(o,t,null,XPathResult.ANY_TYPE,null);let c=null;const n=[];for(;c=i.iterateNext();)n.push(c);return n}return e.startsWith("text/")||e.startsWith("pierce/")?[]:t.shadowRoot?Array.from(t.shadowRoot.querySelectorAll(e)):Array.from(t.querySelectorAll(e))}querySelectorChain(e){let t,o=document;for(const i of e){if(t=this.querySingleReplySelector(i,o),0===t.length)return[];o=t[0]}return t}elementSelector(e){return"string"==typeof e?this.querySingleReplySelector(e):this.querySelectorChain(e)}};(()=>{let e=0;const t=new Int32Array(256);for(let o=0;256!==o;o+=1)e=o,e=1&e?-306674912^e>>>1:e>>>1,e=1&e?-306674912^e>>>1:e>>>1,e=1&e?-306674912^e>>>1:e>>>1,e=1&e?-306674912^e>>>1:e>>>1,e=1&e?-306674912^e>>>1:e>>>1,e=1&e?-306674912^e>>>1:e>>>1,e=1&e?-306674912^e>>>1:e>>>1,e=1&e?-306674912^e>>>1:e>>>1,t[o]=e})(),new Int8Array(new Int16Array([1]).buffer)[0];var v,f,E={attribute:/\[\s*(?:(?\*|[-\w]*)\|)?(?[-\w\u{0080}-\u{FFFF}]+)\s*(?:(?\W?=)\s*(?.+?)\s*(?[iIsS])?\s*)?\]/gu,id:/#(?(?:[-\w\u{0080}-\u{FFFF}]|\\.)+)/gu,class:/\.(?(?:[-\w\u{0080}-\u{FFFF}]|\\.)+)/gu,comma:/\s*,\s*/g,combinator:/\s*[\s>+~]\s*/g,"pseudo-element":/::(?[-\w\u{0080}-\u{FFFF}]+)(?:\((?:¶*)\))?/gu,"pseudo-class":/:(?[-\w\u{0080}-\u{FFFF}]+)(?:\((?¶*)\))?/gu,type:/(?:(?\*|[-\w]*)\|)?(?[-\w\u{0080}-\u{FFFF}]+)|\*/gu},x=Object.assign({},E);x["pseudo-element"]=RegExp(E["pseudo-element"].source.replace("(?¶*)","(?.*?)"),"gu"),x["pseudo-class"]=RegExp(E["pseudo-class"].source.replace("(?¶*)","(?.*)"),"gu"),(f=v||(v={}))[f.Normal=0]="Normal",f[f.Extended=1]="Extended",f[f.Invalid=2]="Invalid";var O,S,T,I,P,F,L,V,R=37,B=5011;function N(e){return"string"!=typeof e||0===e.length?B:function(e,t,o){let i=B;for(let c=t;c>>0}(e,0,e.length)}N("type:beacon"),N("type:csp"),N("type:csp"),N("type:cspviolationreport"),N("type:document"),N("type:other"),N("type:xhr"),N("type:font"),N("type:image"),N("type:image"),N("type:document"),N("type:document"),N("type:other"),N("type:media"),N("type:object"),N("type:object"),N("type:other"),N("type:ping"),N("type:other"),N("type:preflight"),N("type:script"),N("type:signedexchange"),N("type:other"),N("type:stylesheet"),N("type:subdocument"),N("type:subdocument"),N("type:other"),N("type:websocket"),N("type:other"),N("type:websocket"),N("type:xhr"),N("type:other"),N("type:xhr"),N("type:other"),(()=>{const e="undefined"!=typeof document?document.createElement("div"):{matches:()=>{}},t=/^[#.]?[\w-.]+$/})(),(S=O||(O={}))[S.unhide=1]="unhide",S[S.scriptInject=2]="scriptInject",S[S.isUnicode=4]="isUnicode",S[S.isClassSelector=8]="isClassSelector",S[S.isIdSelector=16]="isIdSelector",S[S.isHrefSelector=32]="isHrefSelector",S[S.remove=64]="remove",S[S.extended=128]="extended",S[S.isPureHasSelector=256]="isPureHasSelector",N("http"),N("https"),(I=T||(T={}))[I.fromDocument=1]="fromDocument",I[I.fromFont=2]="fromFont",I[I.fromHttp=4]="fromHttp",I[I.fromHttps=8]="fromHttps",I[I.fromImage=16]="fromImage",I[I.fromMedia=32]="fromMedia",I[I.fromObject=64]="fromObject",I[I.fromOther=128]="fromOther",I[I.fromPing=256]="fromPing",I[I.fromScript=512]="fromScript",I[I.fromStylesheet=1024]="fromStylesheet",I[I.fromSubdocument=2048]="fromSubdocument",I[I.fromWebsocket=4096]="fromWebsocket",I[I.fromXmlHttpRequest=8192]="fromXmlHttpRequest",I[I.firstParty=16384]="firstParty",I[I.thirdParty=32768]="thirdParty",I[I.isReplace=65536]="isReplace",I[I.isBadFilter=131072]="isBadFilter",I[I.isCSP=262144]="isCSP",I[I.isGenericHide=524288]="isGenericHide",I[I.isImportant=1048576]="isImportant",I[I.isSpecificHide=2097152]="isSpecificHide",I[I.isFullRegex=4194304]="isFullRegex",I[I.isRegex=8388608]="isRegex",I[I.isUnicode=16777216]="isUnicode",I[I.isLeftAnchor=33554432]="isLeftAnchor",I[I.isRightAnchor=67108864]="isRightAnchor",I[I.isException=134217728]="isException",I[I.isHostnameAnchor=268435456]="isHostnameAnchor",I[I.isRedirectRule=536870912]="isRedirectRule",I[I.isRedirect=1073741824]="isRedirect",T.fromDocument,T.fromFont,T.fromImage,T.fromMedia,T.fromObject,T.fromOther,T.fromPing,T.fromScript,T.fromStylesheet,T.fromSubdocument,T.fromWebsocket,T.fromXmlHttpRequest,T.fromPing,T.fromDocument,T.fromOther,T.fromXmlHttpRequest,T.fromFont,T.fromImage,T.fromImage,T.fromDocument,T.fromDocument,T.fromMedia,T.fromObject,T.fromObject,T.fromPing,T.fromScript,T.fromStylesheet,T.fromSubdocument,T.fromSubdocument,T.fromWebsocket,T.fromWebsocket,T.fromXmlHttpRequest,T.fromXmlHttpRequest,T.fromOther,T.fromOther,T.fromOther,T.fromOther,T.fromOther,T.fromOther,T.fromOther,T.fromOther,T.fromOther,T.fromOther,T.fromOther,T.fromOther,T.fromOther,(F=P||(P={}))[F.INVALID=0]="INVALID",F[F.BEGIF=1]="BEGIF",F[F.ELSE=2]="ELSE",F[F.ENDIF=3]="ENDIF",(V=L||(L={}))[V.NOT_SUPPORTED=0]="NOT_SUPPORTED",V[V.NETWORK=1]="NETWORK",V[V.COSMETIC=2]="COSMETIC",V[V.NOT_SUPPORTED_EMPTY=100]="NOT_SUPPORTED_EMPTY",V[V.NOT_SUPPORTED_COMMENT=101]="NOT_SUPPORTED_COMMENT",V[V.NOT_SUPPORTED_ADGUARD=102]="NOT_SUPPORTED_ADGUARD";var M="video/flv",j={contentType:`${M};base64`,aliases:[M,".flv","flv"],body:"RkxWAQEAAAAJAAAAABIAALgAAAAAAAAAAgAKb25NZXRhRGF0YQgAAAAIAAhkdXJhdGlvbgAAAAAAAAAAAAAFd2lkdGgAP/AAAAAAAAAABmhlaWdodAA/8AAAAAAAAAANdmlkZW9kYXRhcmF0ZQBAaGoAAAAAAAAJZnJhbWVyYXRlAEBZAAAAAAAAAAx2aWRlb2NvZGVjaWQAQAAAAAAAAAAAB2VuY29kZXICAA1MYXZmNTcuNDEuMTAwAAhmaWxlc2l6ZQBAaoAAAAAAAAAACQAAAMM="},U="image/gif",D={contentType:`${U};base64`,aliases:[U,".gif","gif"],body:"R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"},z="text/html",G={contentType:z,aliases:[z,".html","html",".htm","htm","noopframe","noop.html"],body:""},q="image/vnd.microsoft.icon",H={contentType:`${q};base64`,aliases:[q,".ico","ico"],body:"AAABAAEAAQEAAAEAGAAwAAAAFgAAACgAAAABAAAAAgAAAAEAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP8AAAAAAA=="},W="image/jpeg",K={contentType:`${W};base64`,aliases:[W,".jpg","jpg",".jpeg","jpeg"],body:"/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k="},Q="application/javascript",Y={contentType:Q,aliases:[Q,".js","js","javascript",".jsx","jsx","typescript",".ts","ts","noop.js","noopjs"],body:""},X="application/json",Z={contentType:X,aliases:[X,".json","json"],body:"0"},$="audio/mpeg",J={contentType:`${$};base64`,aliases:[$,".mp3","mp3","noop-0.1s.mp3","noopmp3-0.1s"],body:"/+MYxAAAAANIAAAAAExBTUUzLjk4LjIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"},ee="video/mp4",te={contentType:`${ee};base64`,aliases:[ee,".mp4","mp4",".m4a","m4a",".m4p","m4p",".m4b","m4b",".m4r","m4r",".m4v","m4v","noop-1s.mp4","noopmp4-1s"],body:"AAAAHGZ0eXBpc29tAAACAGlzb21pc28ybXA0MQAAAAhmcmVlAAAC721kYXQhEAUgpBv/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3pwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcCEQBSCkG//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADengAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAAsJtb292AAAAbG12aGQAAAAAAAAAAAAAAAAAAAPoAAAALwABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAB7HRyYWsAAABcdGtoZAAAAAMAAAAAAAAAAAAAAAIAAAAAAAAALwAAAAAAAAAAAAAAAQEAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAAC8AAAAAAAEAAAAAAWRtZGlhAAAAIG1kaGQAAAAAAAAAAAAAAAAAAKxEAAAIAFXEAAAAAAAtaGRscgAAAAAAAAAAc291bgAAAAAAAAAAAAAAAFNvdW5kSGFuZGxlcgAAAAEPbWluZgAAABBzbWhkAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAADTc3RibAAAAGdzdHNkAAAAAAAAAAEAAABXbXA0YQAAAAAAAAABAAAAAAAAAAAAAgAQAAAAAKxEAAAAAAAzZXNkcwAAAAADgICAIgACAASAgIAUQBUAAAAAAfQAAAHz+QWAgIACEhAGgICAAQIAAAAYc3R0cwAAAAAAAAABAAAAAgAABAAAAAAcc3RzYwAAAAAAAAABAAAAAQAAAAIAAAABAAAAHHN0c3oAAAAAAAAAAAAAAAIAAAFzAAABdAAAABRzdGNvAAAAAAAAAAEAAAAsAAAAYnVkdGEAAABabWV0YQAAAAAAAAAhaGRscgAAAAAAAAAAbWRpcmFwcGwAAAAAAAAAAAAAAAAtaWxzdAAAACWpdG9vAAAAHWRhdGEAAAABAAAAAExhdmY1Ni40MC4xMDE="},oe="application/pdf",ie={contentType:`${oe};base64`,aliases:[oe,".pdf","pdf"],body:"JVBERi0xLgoxIDAgb2JqPDwvUGFnZXMgMiAwIFI+PmVuZG9iagoyIDAgb2JqPDwvS2lkc1szIDAgUl0vQ291bnQgMT4+ZW5kb2JqCjMgMCBvYmo8PC9QYXJlbnQgMiAwIFI+PmVuZG9iagp0cmFpbGVyIDw8L1Jvb3QgMSAwIFI+Pg=="},ce="image/png",ne={contentType:`${ce};base64`,aliases:[ce,".png","png"],body:"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg=="},ae="image/svg+xml",se={contentType:ae,aliases:[ae,".svg","svg"],body:"https://raw.githubusercontent.com/mathiasbynens/small/master/svg.svg"},re="text/plain",le={contentType:re,aliases:[re,".txt","txt","text","nooptext","noop.txt"],body:""},pe="audio/wav",de={contentType:`${pe};base64`,aliases:[pe,".wav","wav"],body:"UklGRiQAAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQAAAAA="},ue="video/webm",me={contentType:`${ue};base64`,aliases:[ue,".webm","webm"],body:"GkXfo0AgQoaBAUL3gQFC8oEEQvOBCEKCQAR3ZWJtQoeBAkKFgQIYU4BnQI0VSalmQCgq17FAAw9CQE2AQAZ3aGFtbXlXQUAGd2hhbW15RIlACECPQAAAAAAAFlSua0AxrkAu14EBY8WBAZyBACK1nEADdW5khkAFVl9WUDglhohAA1ZQOIOBAeBABrCBCLqBCB9DtnVAIueBAKNAHIEAAIAwAQCdASoIAAgAAUAmJaQAA3AA/vz0AAA="},Ae="image/webp",he={contentType:`${Ae};base64`,aliases:[Ae,".webp","webp"],body:"UklGRhIAAABXRUJQVlA4TAYAAAAvQWxvAGs="},ke="video/wmv",be={contentType:`${ke};base64`,aliases:[ke,".wmv","wmv"],body:"MCaydY5mzxGm2QCqAGLObOUBAAAAAAAABQAAAAECodyrjEepzxGO5ADADCBTZWgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABcCAAAAAAAAAIA+1d6xnQEAAAAAAAAAAMAF2QEAAAAAAAAAAAAAAAAcDAAAAAAAAAIAAACADAAAgAwAAEANAwC1A79fLqnPEY7jAMAMIFNlLgAAAAAAAAAR0tOruqnPEY7mAMAMIFNlBgAAAAAAQKTQ0gfj0hGX8ACgyV6oUGQAAAAAAAAAAQAoAFcATQAvAEUAbgBjAG8AZABpAG4AZwBTAGUAdAB0AGkAbgBnAHMAAAAAABwATABhAHYAZgA1ADcALgA0ADEALgAxADAAMAAAAJEH3Le3qc8RjuYAwAwgU2WBAAAAAAAAAMDvGbxNW88RqP0AgF9cRCsAV/sgVVvPEaj9AIBfXEQrAAAAAAAAAAAzAAAAAAAAAAEAAAAAAAEAAAABAAAAAigAKAAAAAEAAAABAAAAAQAYAE1QNDMDAAAAAAAAAAAAAAAAAAAAAAAAAEBS0YYdMdARo6QAoMkDSPZMAAAAAAAAAEFS0YYdMdARo6QAoMkDSPYBAAAAAQAKAG0AcwBtAHAAZQBnADQAdgAzAAAAAAAEAE1QNDM2JrJ1jmbPEabZAKoAYs5sMgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQ=="};(()=>{const e={};for(const t of[j,D,G,H,K,Y,Z,J,te,ie,ne,se,le,de,me,he,be])for(const o of t.aliases)e[o]=t})();var ge=[/accept cookies/gi,/accept all/gi,/reject all/gi,/only necessary cookies/gi,/by clicking.*(accept|agree|allow)/gi,/by continuing/gi,/we (use|serve)( optional)? cookies/gi,/we are using cookies/gi,/use of cookies/gi,/(this|our) (web)?site.*cookies/gi,/cookies (and|or) .* technologies/gi,/such as cookies/gi,/read more about.*cookies/gi,/consent to.*cookies/gi,/we and our partners.*cookies/gi,/we.*store.*information.*such as.*cookies/gi,/store and\/or access information.*on a device/gi,/personalised ads and content, ad and content measurement/gi];var _e=[{name:"192.com",detectCmp:[{exists:".ont-cookies"}],detectPopup:[{visible:".ont-cookies"}],optIn:[{click:".ont-btn-main.ont-cookies-btn.js-ont-btn-ok2"}],optOut:[{click:".ont-cookes-btn-manage"},{click:".ont-btn-main.ont-cookies-btn.js-ont-btn-choose"}],test:[{eval:"EVAL_ONENINETWO_0"}]},{name:"1password-com",cosmetic:!0,prehideSelectors:['footer #footer-root [aria-label="Cookie Consent"]'],detectCmp:[{exists:'footer #footer-root [aria-label="Cookie Consent"]'}],detectPopup:[{visible:'footer #footer-root [aria-label="Cookie Consent"]'}],optIn:[{click:'footer #footer-root [aria-label="Cookie Consent"] button'}],optOut:[{hide:'footer #footer-root [aria-label="Cookie Consent"]'}]},{name:"aa",vendorUrl:"https://aa.com",prehideSelectors:[],cosmetic:!0,detectCmp:[{exists:"#aa_optoutmulti-Modal,#cookieBannerMessage"}],detectPopup:[{visible:"#aa_optoutmulti-Modal,#cookieBannerMessage"}],optIn:[{hide:"#aa_optoutmulti-Modal,#cookieBannerMessage"},{waitForThenClick:"#aa_optoutmulti_checkBox"},{waitForThenClick:"#aa_optoutmulti-Modal button.optoutmulti_button"}],optOut:[{hide:"#aa_optoutmulti-Modal,#cookieBannerMessage"}]},{name:"abc",vendorUrl:"https://abc.net.au",runContext:{urlPattern:"^https://([a-z0-9-]+\\.)?abc\\.net\\.au/"},prehideSelectors:[],detectCmp:[{exists:"[data-component=CookieBanner]"}],detectPopup:[{visible:"[data-component=CookieBanner] [data-component=CookieBanner_AcceptAll]"}],optIn:[{waitForThenClick:"[data-component=CookieBanner] [data-component=CookieBanner_AcceptAll]"}],optOut:[{waitForThenClick:"[data-component=CookieBanner] [data-component=CookieBanner_AcceptABCRequired]"}],test:[{eval:"EVAL_ABC_TEST"}]},{name:"abconcerts.be",vendorUrl:"https://unknown",intermediate:!1,prehideSelectors:["dialog.cookie-consent"],detectCmp:[{exists:"dialog.cookie-consent form.cookie-consent__form"}],detectPopup:[{visible:"dialog.cookie-consent form.cookie-consent__form"}],optIn:[{waitForThenClick:"dialog.cookie-consent form.cookie-consent__form button[value=yes]"}],optOut:[{if:{exists:"dialog.cookie-consent form.cookie-consent__form button[value=no]"},then:[{click:"dialog.cookie-consent form.cookie-consent__form button[value=no]"}],else:[{click:"dialog.cookie-consent form.cookie-consent__form button.cookie-consent__options-toggle"},{waitForThenClick:'dialog.cookie-consent form.cookie-consent__form button[value="save_options"]'}]}]},{name:"acris",prehideSelectors:["div.acris-cookie-consent"],detectCmp:[{exists:"[data-acris-cookie-consent]"}],detectPopup:[{visible:".acris-cookie-consent.is--modal"}],optIn:[{waitForVisible:"#ccConsentAcceptAllButton",check:"any"},{wait:500},{waitForThenClick:"#ccConsentAcceptAllButton"}],optOut:[{waitForVisible:"#ccAcceptOnlyFunctional",check:"any"},{wait:500},{waitForThenClick:"#ccAcceptOnlyFunctional"}]},{name:"activobank.pt",runContext:{urlPattern:"^https://(www\\.)?activobank\\.pt"},prehideSelectors:["aside#cookies,.overlay-cookies"],detectCmp:[{exists:"#cookies .cookies-btn"}],detectPopup:[{visible:"#cookies #submitCookies"}],optIn:[{waitForThenClick:"#cookies #submitCookies"}],optOut:[{waitForThenClick:"#cookies #rejectCookies"}]},{name:"Adroll",prehideSelectors:["#adroll_consent_container"],detectCmp:[{exists:"#adroll_consent_container"}],detectPopup:[{visible:"#adroll_consent_container"}],optIn:[{waitForThenClick:"#adroll_consent_accept"}],optOut:[{waitForThenClick:"#adroll_consent_reject"}],test:[{eval:"EVAL_ADROLL_0"}]},{name:"affinity.serif.com",detectCmp:[{exists:".c-cookie-banner button[data-qa='allow-all-cookies']"}],detectPopup:[{visible:".c-cookie-banner"}],optIn:[{click:'button[data-qa="allow-all-cookies"]'}],optOut:[{click:'button[data-qa="manage-cookies"]'},{waitFor:'.c-cookie-banner ~ [role="dialog"]'},{waitForThenClick:'.c-cookie-banner ~ [role="dialog"] input[type="checkbox"][value="true"]',all:!0},{click:'.c-cookie-banner ~ [role="dialog"] .c-modal__action button'}],test:[{wait:500},{eval:"EVAL_AFFINITY_SERIF_COM_0"}]},{name:"agolde.com",cosmetic:!0,prehideSelectors:["#modal-1 div[data-micromodal-close]"],detectCmp:[{exists:"#modal-1 div[aria-labelledby=modal-1-title]"}],detectPopup:[{exists:"#modal-1 div[data-micromodal-close]"}],optIn:[{click:'button[aria-label="Close modal"]'}],optOut:[{hide:"#modal-1 div[data-micromodal-close]"}]},{name:"aliexpress",vendorUrl:"https://aliexpress.com/",runContext:{urlPattern:"^https://.*\\.aliexpress\\.com/"},prehideSelectors:["#gdpr-new-container"],detectCmp:[{exists:"#gdpr-new-container,#voyager-gdpr > div"}],detectPopup:[{visible:"#gdpr-new-container,#voyager-gdpr > div"}],optIn:[{waitForThenClick:"#gdpr-new-container .btn-accept,#voyager-gdpr > div > div > button:nth-child(1)"}],optOut:[{if:{exists:"#voyager-gdpr > div"},then:[{waitForThenClick:"#voyager-gdpr > div > div > button:nth-child(2)"}],else:[{waitForThenClick:"#gdpr-new-container .btn-more"},{waitFor:"#gdpr-new-container .gdpr-dialog-switcher"},{click:"#gdpr-new-container .switcher-on",all:!0,optional:!0},{click:"#gdpr-new-container .btn-save"}]}]},{name:"almacmp",prehideSelectors:["#alma-cmpv2-container"],detectCmp:[{exists:"#alma-cmpv2-container"}],detectPopup:[{visible:"#alma-cmpv2-container #almacmp-modal-layer1"}],optIn:[{waitForThenClick:"#alma-cmpv2-container #almacmp-modal-layer1 #almacmp-modalConfirmBtn"}],optOut:[{waitForThenClick:"#alma-cmpv2-container #almacmp-modal-layer1 #almacmp-modalSettingBtn"},{waitFor:"#alma-cmpv2-container #almacmp-modal-layer2"},{waitForThenClick:"#alma-cmpv2-container #almacmp-modal-layer2 #almacmp-reject-all-layer2"}],test:[{eval:"EVAL_ALMACMP_0"}]},{name:"altium.com",cosmetic:!0,prehideSelectors:[".altium-privacy-bar"],detectCmp:[{exists:".altium-privacy-bar"}],detectPopup:[{exists:".altium-privacy-bar"}],optIn:[{click:"a.altium-privacy-bar__btn"}],optOut:[{hide:".altium-privacy-bar"}]},{name:"amazon.com",prehideSelectors:['span[data-action="sp-cc"][data-sp-cc*="rejectAllAction"]'],detectCmp:[{exists:'span[data-action="sp-cc"][data-sp-cc*="rejectAllAction"]'}],detectPopup:[{visible:'span[data-action="sp-cc"][data-sp-cc*="rejectAllAction"]'}],optIn:[{waitForVisible:"#sp-cc-accept"},{wait:500},{click:"#sp-cc-accept"}],optOut:[{waitForVisible:"#sp-cc-rejectall-link"},{wait:500},{click:"#sp-cc-rejectall-link"}]},{name:"amex",vendorUrl:"https://www.americanexpress.com/",cosmetic:!1,prehideSelectors:["#user-consent-management-granular-banner-overlay"],detectCmp:[{exists:"#user-consent-management-granular-banner-overlay"}],detectPopup:[{visible:"#user-consent-management-granular-banner-overlay"}],optIn:[{waitForThenClick:"[data-testid=granular-banner-button-accept-all]"}],optOut:[{waitForThenClick:"[data-testid=granular-banner-button-decline-all]"}]},{name:"aquasana.com",prehideSelectors:["#consent-tracking"],detectCmp:[{exists:"#consent-tracking"}],detectPopup:[{exists:"#consent-tracking"}],optIn:[{waitForThenClick:"#consent-tracking .affirm.btn"}],optOut:[{if:{exists:"#consent-tracking .decline.btn"},then:[{click:"#consent-tracking .decline.btn"}],else:[{hide:"#consent-tracking"}]}]},{name:"arbeitsagentur",vendorUrl:"https://www.arbeitsagentur.de/",prehideSelectors:[".modal-open bahf-cookie-disclaimer-dpl3"],detectCmp:[{exists:"bahf-cookie-disclaimer-dpl3"}],detectPopup:[{visible:"bahf-cookie-disclaimer-dpl3"}],optIn:[{waitForThenClick:["bahf-cookie-disclaimer-dpl3","#bahf-cookie-disclaimer-modal .ba-btn-primary"]}],optOut:[{waitForThenClick:["bahf-cookie-disclaimer-dpl3","#bahf-cookie-disclaimer-modal .ba-btn-contrast"]}],test:[{eval:"EVAL_ARBEITSAGENTUR_TEST"}]},{name:"asus",vendorUrl:"https://www.asus.com/",runContext:{urlPattern:"^https://www\\.asus\\.com/"},prehideSelectors:["#cookie-policy-info,#cookie-policy-info-bg"],detectCmp:[{exists:"#cookie-policy-info"}],detectPopup:[{visible:"#cookie-policy-info"}],optIn:[{waitForThenClick:'#cookie-policy-info [data-agree="Accept Cookies"]'}],optOut:[{if:{exists:"#cookie-policy-info .btn-reject"},then:[{waitForThenClick:"#cookie-policy-info .btn-reject"}],else:[{waitForThenClick:"#cookie-policy-info .btn-setting"},{waitForThenClick:'#cookie-policy-lightbox-wrapper [data-agree="Save Settings"]'}]}]},{name:"athlinks-com",runContext:{urlPattern:"^https://(www\\.)?athlinks\\.com/"},cosmetic:!0,prehideSelectors:["#footer-container ~ div"],detectCmp:[{exists:"#footer-container ~ div"}],detectPopup:[{visible:"#footer-container > div"}],optIn:[{click:"#footer-container ~ div button"}],optOut:[{hide:"#footer-container ~ div"}]},{name:"ausopen.com",cosmetic:!0,detectCmp:[{exists:".gdpr-popup__message"}],detectPopup:[{visible:".gdpr-popup__message"}],optOut:[{hide:".gdpr-popup__message"}],optIn:[{click:".gdpr-popup__message button"}]},{name:"automattic-cmp-optout",prehideSelectors:['form[class*="cookie-banner"][method="post"]'],detectCmp:[{exists:'form[class*="cookie-banner"][method="post"]'}],detectPopup:[{visible:'form[class*="cookie-banner"][method="post"]'}],optIn:[{click:'a[class*="accept-all-button"]'}],optOut:[{click:'form[class*="cookie-banner"] div[class*="simple-options"] a[class*="customize-button"]'},{waitForThenClick:"input[type=checkbox][checked]:not([disabled])",all:!0},{click:'a[class*="accept-selection-button"]'}]},{name:"aws.amazon.com",prehideSelectors:["#awsccc-cb-content","#awsccc-cs-container","#awsccc-cs-modalOverlay","#awsccc-cs-container-inner"],detectCmp:[{exists:"#awsccc-cb-content"}],detectPopup:[{visible:"#awsccc-cb-content"}],optIn:[{click:"button[data-id=awsccc-cb-btn-accept"}],optOut:[{click:"button[data-id=awsccc-cb-btn-customize]"},{waitFor:"input[aria-checked]"},{click:"input[aria-checked=true]",all:!0,optional:!0},{click:"button[data-id=awsccc-cs-btn-save]"}]},{name:"axeptio",prehideSelectors:[".axeptio_widget"],detectCmp:[{exists:".axeptio_widget"}],detectPopup:[{visible:".axeptio_widget"}],optIn:[{waitFor:".axeptio-widget--open"},{click:"button#axeptio_btn_acceptAll"}],optOut:[{waitFor:".axeptio-widget--open"},{click:"button#axeptio_btn_dismiss"}],test:[{eval:"EVAL_AXEPTIO_0"}]},{name:"baden-wuerttemberg.de",prehideSelectors:[".cookie-alert.t-dark"],cosmetic:!0,detectCmp:[{exists:".cookie-alert.t-dark"}],detectPopup:[{visible:".cookie-alert.t-dark"}],optIn:[{click:".cookie-alert__form input:not([disabled]):not([checked])"},{click:".cookie-alert__button button"}],optOut:[{hide:".cookie-alert.t-dark"}]},{name:"bahn-de",vendorUrl:"https://www.bahn.de/",cosmetic:!1,runContext:{main:!0,frame:!1,urlPattern:"^https://(www\\.)?bahn\\.de/"},intermediate:!1,prehideSelectors:[],detectCmp:[{exists:["body > div:first-child","#consent-layer"]}],detectPopup:[{visible:["body > div:first-child","#consent-layer"]}],optIn:[{waitForThenClick:["body > div:first-child","#consent-layer .js-accept-all-cookies"]}],optOut:[{waitForThenClick:["body > div:first-child","#consent-layer .js-accept-essential-cookies"]}],test:[{eval:"EVAL_BAHN_TEST"}]},{name:"bbb.org",runContext:{urlPattern:"^https://www\\.bbb\\.org/"},cosmetic:!0,prehideSelectors:['div[aria-label="use of cookies on bbb.org"]'],detectCmp:[{exists:'div[aria-label="use of cookies on bbb.org"]'}],detectPopup:[{visible:'div[aria-label="use of cookies on bbb.org"]'}],optIn:[{click:'div[aria-label="use of cookies on bbb.org"] button.bds-button-unstyled span.visually-hidden'}],optOut:[{hide:'div[aria-label="use of cookies on bbb.org"]'}]},{name:"bing.com",prehideSelectors:["#bnp_container"],detectCmp:[{exists:"#bnp_cookie_banner"}],detectPopup:[{visible:"#bnp_cookie_banner"},{visible:"#bnp_btn_accept,#bnp_btn_reject"}],optIn:[{waitForThenClick:"#bnp_btn_accept"}],optOut:[{wait:500},{waitForThenClick:"#bnp_btn_reject"}],test:[{eval:"EVAL_BING_0"}]},{name:"blocksy",vendorUrl:"https://creativethemes.com/blocksy/docs/extensions/cookies-consent/",cosmetic:!1,runContext:{main:!0,frame:!1},intermediate:!1,prehideSelectors:[".cookie-notification"],detectCmp:[{exists:"#blocksy-ext-cookies-consent-styles-css"}],detectPopup:[{visible:".cookie-notification"}],optIn:[{click:".cookie-notification .ct-cookies-decline-button"}],optOut:[{waitForThenClick:".cookie-notification .ct-cookies-decline-button"}],test:[{eval:"EVAL_BLOCKSY_0"}]},{name:"borlabs",detectCmp:[{exists:"._brlbs-block-content"}],detectPopup:[{visible:"._brlbs-bar-wrap,._brlbs-box-wrap"}],optIn:[{click:"a[data-cookie-accept-all]"}],optOut:[{click:"a[data-cookie-individual]"},{waitForVisible:".cookie-preference"},{click:"input[data-borlabs-cookie-checkbox]:checked",all:!0,optional:!0},{click:"#CookiePrefSave"},{wait:500}],prehideSelectors:["#BorlabsCookieBox"],test:[{eval:"EVAL_BORLABS_0"}]},{name:"bundesregierung.de",prehideSelectors:[".bpa-cookie-banner"],detectCmp:[{exists:".bpa-cookie-banner"}],detectPopup:[{visible:".bpa-cookie-banner .bpa-module-full-hero"}],optIn:[{click:".bpa-accept-all-button"}],optOut:[{wait:500,comment:"click is not immediately recognized"},{waitForThenClick:".bpa-close-button"}],test:[{eval:"EVAL_BUNDESREGIERUNG_DE_0"}]},{name:"burpee.com",cosmetic:!0,prehideSelectors:["#notice-cookie-block"],detectCmp:[{exists:"#notice-cookie-block"}],detectPopup:[{exists:"#html-body #notice-cookie-block"}],optIn:[{click:"#btn-cookie-allow"}],optOut:[{hide:"#html-body #notice-cookie-block, #notice-cookie"}]},{name:"canva.com",prehideSelectors:['div[role="dialog"] a[data-anchor-id="cookie-policy"]'],detectCmp:[{exists:'div[role="dialog"] a[data-anchor-id="cookie-policy"]'}],detectPopup:[{exists:'div[role="dialog"] a[data-anchor-id="cookie-policy"]'}],optIn:[{click:'div[role="dialog"] button:nth-child(1)'}],optOut:[{if:{exists:'div[role="dialog"] button:nth-child(3)'},then:[{click:'div[role="dialog"] button:nth-child(2)'}],else:[{click:'div[role="dialog"] button:nth-child(2)'},{waitFor:'div[role="dialog"] a[data-anchor-id="cookie-policy"]'},{waitFor:'div[role="dialog"] button[role=switch]'},{click:'div[role="dialog"] button:nth-child(2):not([role])'},{click:'div[role="dialog"] div:last-child button:only-child'}]}],test:[{eval:"EVAL_CANVA_0"}]},{name:"canyon.com",runContext:{urlPattern:"^https://www\\.canyon\\.com/"},prehideSelectors:["div.modal.cookiesModal.is-open"],detectCmp:[{exists:"div.modal.cookiesModal.is-open"}],detectPopup:[{visible:"div.modal.cookiesModal.is-open"}],optIn:[{click:'div.cookiesModal__buttonWrapper > button[data-closecause="close-by-submit"]'}],optOut:[{click:'div.cookiesModal__buttonWrapper > button[data-closecause="close-by-manage-cookies"]'},{waitForThenClick:"button#js-manage-data-privacy-save-button"}]},{name:"cassie",vendorUrl:"https://trustcassie.com",cosmetic:!1,runContext:{main:!0,frame:!1},prehideSelectors:[".cassie-cookie-module"],detectCmp:[{exists:".cassie-pre-banner"}],detectPopup:[{visible:"#cassie_pre_banner_text"}],optIn:[{waitForThenClick:".cassie-accept-all"}],optOut:[{waitForThenClick:".cassie-reject-all"}]},{name:"cc-banner-springer",prehideSelectors:[".cc-banner[data-cc-banner]"],detectCmp:[{exists:".cc-banner[data-cc-banner]"}],detectPopup:[{visible:".cc-banner[data-cc-banner]"}],optIn:[{waitForThenClick:".cc-banner[data-cc-banner] button[data-cc-action=accept]"}],optOut:[{if:{exists:".cc-banner[data-cc-banner] button[data-cc-action=reject]"},then:[{click:".cc-banner[data-cc-banner] button[data-cc-action=reject]"}],else:[{waitForThenClick:".cc-banner[data-cc-banner] button[data-cc-action=preferences]"},{waitFor:".cc-preferences[data-cc-preferences]"},{click:".cc-preferences[data-cc-preferences] input[type=radio][data-cc-action=toggle-category][value=off]",all:!0,optional:!0},{if:{exists:".cc-preferences[data-cc-preferences] button[data-cc-action=reject]"},then:[{click:".cc-preferences[data-cc-preferences] button[data-cc-action=reject]"}],else:[{click:".cc-preferences[data-cc-preferences] button[data-cc-action=save]"}]}]}],test:[{eval:"EVAL_CC_BANNER2_0"}]},{name:"cc_banner",cosmetic:!0,prehideSelectors:[".cc_banner-wrapper"],detectCmp:[{exists:".cc_banner-wrapper"}],detectPopup:[{visible:".cc_banner"}],optIn:[{click:".cc_btn_accept_all"}],optOut:[{hide:".cc_banner-wrapper"}]},{name:"check24-partnerprogramm-de",prehideSelectors:["[data-modal-content]:has([data-toggle-target^='cookie'])"],detectCmp:[{exists:"[data-toggle-target^='cookie']"}],detectPopup:[{visible:"[data-toggle-target^='cookie']",check:"any"}],optIn:[{waitForThenClick:"[data-cookie-accept-all]"}],optOut:[{waitForThenClick:"[data-cookie-dismiss-all]"}]},{name:"ciaopeople.it",prehideSelectors:["#cp-gdpr-choices"],detectCmp:[{exists:"#cp-gdpr-choices"}],detectPopup:[{visible:"#cp-gdpr-choices"}],optIn:[{waitForThenClick:".gdpr-btm__right > button:nth-child(2)"}],optOut:[{waitForThenClick:".gdpr-top-content > button"},{waitFor:".gdpr-top-back"},{waitForThenClick:".gdpr-btm__right > button:nth-child(1)"}],test:[{visible:"#cp-gdpr-choices",check:"none"}]},{vendorUrl:"https://www.civicuk.com/cookie-control/",name:"civic-cookie-control",prehideSelectors:["#ccc-module,#ccc-overlay,#ccc"],detectCmp:[{exists:"#ccc-module,#ccc-notify"}],detectPopup:[{visible:"#ccc"},{visible:"#ccc-module,#ccc-notify"}],optOut:[{if:{exists:"#ccc-notify"},then:[{waitForThenClick:["#ccc #ccc-notify .ccc-notify-buttons","xpath///button[contains(., 'Settings') or contains(., 'Cookie Preferences')]"]},{waitForVisible:"#ccc-module"}]},{if:{exists:"#ccc-reject-settings"},then:[{waitForThenClick:"#ccc-reject-settings"}],else:[{waitForThenClick:"#ccc-dismiss-button"}]}],optIn:[{waitForThenClick:"#ccc-recommended-settings,#ccc-notify-accept"}]},{name:"click.io",prehideSelectors:["#cl-consent"],detectCmp:[{exists:"#cl-consent"}],detectPopup:[{visible:"#cl-consent"}],optIn:[{waitForThenClick:'#cl-consent [data-role="b_agree"]'}],optOut:[{waitFor:'#cl-consent [data-role="b_options"]'},{wait:500},{click:'#cl-consent [data-role="b_options"]'},{waitFor:'.cl-consent-popup.cl-consent-visible [data-role="alloff"]'},{click:'.cl-consent-popup.cl-consent-visible [data-role="alloff"]',all:!0},{click:'[data-role="b_save"]'}],test:[{eval:"EVAL_CLICKIO_0",comment:"TODO: this only checks if we interacted at all"}]},{name:"clinch",intermediate:!1,runContext:{frame:!1,main:!0},prehideSelectors:[".consent-modal[role=dialog]"],detectCmp:[{exists:".consent-modal[role=dialog]"}],detectPopup:[{visible:".consent-modal[role=dialog]"}],optIn:[{click:"#consent_agree"}],optOut:[{if:{exists:"#consent_reject"},then:[{click:"#consent_reject"}],else:[{click:"#manage_cookie_preferences"},{click:"#cookie_consent_preferences input:checked",all:!0,optional:!0},{click:"#consent_save"}]}],test:[{eval:"EVAL_CLINCH_0"}]},{name:"clustrmaps.com",runContext:{urlPattern:"^https://(www\\.)?clustrmaps\\.com/"},cosmetic:!0,prehideSelectors:["#gdpr-cookie-message"],detectCmp:[{exists:"#gdpr-cookie-message"}],detectPopup:[{visible:"#gdpr-cookie-message"}],optIn:[{click:"button#gdpr-cookie-accept"}],optOut:[{hide:"#gdpr-cookie-message"}]},{name:"coinbase",intermediate:!1,runContext:{frame:!0,main:!0,urlPattern:"^https://(www|help)\\.coinbase\\.com"},prehideSelectors:[],detectCmp:[{exists:"div[class^=CookieBannerContent__Container]"}],detectPopup:[{visible:"div[class^=CookieBannerContent__Container]"}],optIn:[{click:"div[class^=CookieBannerContent__CTA] :nth-last-child(1)"}],optOut:[{click:"button[class^=CookieBannerContent__Settings]"},{click:"div[class^=CookiePreferencesModal__CategoryContainer] input:checked",all:!0,optional:!0},{click:"div[class^=CookiePreferencesModal__ButtonContainer] > button"}],test:[{eval:"EVAL_COINBASE_0"}]},{name:"Complianz banner",prehideSelectors:["#cmplz-cookiebanner-container"],detectCmp:[{exists:"#cmplz-cookiebanner-container .cmplz-cookiebanner"}],detectPopup:[{visible:"#cmplz-cookiebanner-container .cmplz-cookiebanner",check:"any"}],optIn:[{waitForThenClick:".cmplz-cookiebanner .cmplz-accept"}],optOut:[{waitForThenClick:".cmplz-cookiebanner .cmplz-deny"}],test:[{eval:"EVAL_COMPLIANZ_BANNER_0"}]},{name:"Complianz categories",prehideSelectors:['.cc-type-categories[aria-describedby="cookieconsent:desc"]'],detectCmp:[{exists:'.cc-type-categories[aria-describedby="cookieconsent:desc"]'}],detectPopup:[{visible:'.cc-type-categories[aria-describedby="cookieconsent:desc"]'}],optIn:[{any:[{click:".cc-accept-all"},{click:".cc-allow-all"},{click:".cc-allow"},{click:".cc-dismiss"}]}],optOut:[{if:{exists:'.cc-type-categories[aria-describedby="cookieconsent:desc"] .cc-dismiss'},then:[{click:".cc-dismiss"}],else:[{click:".cc-type-categories input[type=checkbox]:not([disabled]):checked",all:!0,optional:!0},{click:".cc-save"}]}]},{name:"Complianz notice",prehideSelectors:['.cc-type-info[aria-describedby="cookieconsent:desc"]'],cosmetic:!0,detectCmp:[{exists:'.cc-type-info[aria-describedby="cookieconsent:desc"] .cc-compliance .cc-btn'}],detectPopup:[{visible:'.cc-type-info[aria-describedby="cookieconsent:desc"] .cc-compliance .cc-btn'}],optIn:[{click:".cc-accept-all",optional:!0},{click:".cc-allow",optional:!0},{click:".cc-dismiss",optional:!0}],optOut:[{if:{exists:".cc-deny"},then:[{click:".cc-deny"}],else:[{hide:'[aria-describedby="cookieconsent:desc"]'}]}]},{name:"Complianz opt-both",prehideSelectors:['[aria-describedby="cookieconsent:desc"] .cc-type-opt-both'],detectCmp:[{exists:'[aria-describedby="cookieconsent:desc"] .cc-type-opt-both'}],detectPopup:[{visible:'[aria-describedby="cookieconsent:desc"] .cc-type-opt-both'}],optIn:[{click:".cc-accept-all",optional:!0},{click:".cc-allow",optional:!0},{click:".cc-dismiss",optional:!0}],optOut:[{waitForThenClick:".cc-deny"}]},{name:"Complianz opt-out",prehideSelectors:['[aria-describedby="cookieconsent:desc"].cc-type-opt-out'],detectCmp:[{exists:'[aria-describedby="cookieconsent:desc"].cc-type-opt-out'}],detectPopup:[{visible:'[aria-describedby="cookieconsent:desc"].cc-type-opt-out'}],optIn:[{click:".cc-accept-all",optional:!0},{click:".cc-allow",optional:!0},{click:".cc-dismiss",optional:!0}],optOut:[{if:{exists:".cc-deny"},then:[{click:".cc-deny"}],else:[{if:{exists:".cmp-pref-link"},then:[{click:".cmp-pref-link"},{waitForThenClick:".cmp-body [id*=rejectAll]"},{waitForThenClick:".cmp-body .cmp-save-btn"}]}]}]},{name:"Complianz optin",prehideSelectors:['.cc-type-opt-in[aria-describedby="cookieconsent:desc"]'],detectCmp:[{exists:'.cc-type-opt-in[aria-describedby="cookieconsent:desc"]'}],detectPopup:[{visible:'.cc-type-opt-in[aria-describedby="cookieconsent:desc"]'}],optIn:[{any:[{click:".cc-accept-all"},{click:".cc-allow"},{click:".cc-dismiss"}]}],optOut:[{if:{visible:".cc-deny"},then:[{click:".cc-deny"}],else:[{if:{visible:".cc-settings"},then:[{waitForThenClick:".cc-settings"},{waitForVisible:".cc-settings-view"},{click:".cc-settings-view input[type=checkbox]:not([disabled]):checked",all:!0,optional:!0},{click:".cc-settings-view .cc-btn-accept-selected"}],else:[{click:".cc-dismiss"}]}]}]},{name:"cookie-consent-spice",cosmetic:!1,runContext:{main:!0,frame:!1},prehideSelectors:[".spicy-consent-wrapper",".spicy-consent-bar"],detectCmp:[{exists:".spicy-consent-bar"}],detectPopup:[{visible:".spicy-consent-bar"}],optIn:[{waitForThenClick:".spicy-consent-bar__action-accept"}],optOut:[{waitForThenClick:".js-decline-all-cookies"}]},{name:"cookie-law-info",prehideSelectors:["#cookie-law-info-bar"],detectCmp:[{exists:"#cookie-law-info-bar"},{eval:"EVAL_COOKIE_LAW_INFO_DETECT"}],detectPopup:[{visible:"#cookie-law-info-bar"}],optIn:[{click:'[data-cli_action="accept_all"]'}],optOut:[{hide:"#cookie-law-info-bar"},{eval:"EVAL_COOKIE_LAW_INFO_0"}],test:[{eval:"EVAL_COOKIE_LAW_INFO_1"}]},{name:"cookie-manager-popup",cosmetic:!1,runContext:{main:!0,frame:!1},intermediate:!1,detectCmp:[{exists:"#notice-cookie-block #allow-functional-cookies, #notice-cookie-block #btn-cookie-settings"}],detectPopup:[{visible:"#notice-cookie-block"}],optIn:[{click:"#btn-cookie-allow"}],optOut:[{if:{exists:"#allow-functional-cookies"},then:[{click:"#allow-functional-cookies"}],else:[{waitForThenClick:"#btn-cookie-settings"},{waitForVisible:".modal-body"},{click:'.modal-body input:checked, .switch[data-switch="on"]',all:!0,optional:!0},{click:'[role="dialog"] .modal-footer button'}]}],prehideSelectors:["#btn-cookie-settings"],test:[{eval:"EVAL_COOKIE_MANAGER_POPUP_0"}]},{name:"cookie-notice",prehideSelectors:["#cookie-notice"],cosmetic:!0,detectCmp:[{visible:"#cookie-notice .cookie-notice-container"}],detectPopup:[{visible:"#cookie-notice"}],optIn:[{click:"#cn-accept-cookie"}],optOut:[{hide:"#cookie-notice"}]},{name:"cookie-script",vendorUrl:"https://cookie-script.com/",prehideSelectors:["#cookiescript_injected"],detectCmp:[{exists:"#cookiescript_injected"}],detectPopup:[{visible:"#cookiescript_injected"}],optOut:[{if:{exists:"#cookiescript_reject"},then:[{wait:100},{click:"#cookiescript_reject"}],else:[{click:"#cookiescript_manage"},{waitForVisible:".cookiescript_fsd_main"},{waitForThenClick:"#cookiescript_reject"}]}],optIn:[{click:"#cookiescript_accept"}]},{name:"cookieacceptbar",vendorUrl:"https://unknown",cosmetic:!0,prehideSelectors:["#cookieAcceptBar.cookieAcceptBar"],detectCmp:[{exists:"#cookieAcceptBar.cookieAcceptBar"}],detectPopup:[{visible:"#cookieAcceptBar.cookieAcceptBar"}],optIn:[{waitForThenClick:"#cookieAcceptBarConfirm"}],optOut:[{hide:"#cookieAcceptBar.cookieAcceptBar"}]},{name:"cookiealert",intermediate:!1,prehideSelectors:[],runContext:{frame:!0,main:!0},detectCmp:[{exists:".cookie-alert-extended"}],detectPopup:[{visible:".cookie-alert-extended-modal"}],optIn:[{click:"button[data-controller='cookie-alert/extended/button/accept']"},{eval:"EVAL_COOKIEALERT_0"}],optOut:[{click:"a[data-controller='cookie-alert/extended/detail-link']"},{click:".cookie-alert-configuration-input:checked",all:!0,optional:!0},{click:"button[data-controller='cookie-alert/extended/button/configuration']"},{eval:"EVAL_COOKIEALERT_0"}],test:[{eval:"EVAL_COOKIEALERT_2"}]},{name:"cookieconsent2",vendorUrl:"https://www.github.com/orestbida/cookieconsent",comment:"supports v2.x.x of the library",prehideSelectors:["#cc--main"],detectCmp:[{exists:"#cc--main"}],detectPopup:[{visible:"#cm"},{exists:"#s-all-bn"}],optIn:[{waitForThenClick:"#s-all-bn"}],optOut:[{waitForThenClick:"#s-rall-bn"}],test:[{eval:"EVAL_COOKIECONSENT2_TEST"}]},{name:"cookieconsent3",vendorUrl:"https://www.github.com/orestbida/cookieconsent",comment:"supports v3.x.x of the library",prehideSelectors:["#cc-main"],detectCmp:[{exists:"#cc-main"}],detectPopup:[{visible:"#cc-main .cm-wrapper"}],optIn:[{waitForThenClick:".cm__btn[data-role=all]"}],optOut:[{waitForThenClick:".cm__btn[data-role=necessary]"}],test:[{eval:"EVAL_COOKIECONSENT3_TEST"}]},{name:"cookiecuttr",vendorUrl:"https://github.com/cdwharton/cookieCuttr",cosmetic:!1,runContext:{main:!0,frame:!1,urlPattern:""},prehideSelectors:[".cc-cookies"],detectCmp:[{exists:".cc-cookies .cc-cookie-accept"}],detectPopup:[{visible:".cc-cookies .cc-cookie-accept"}],optIn:[{waitForThenClick:".cc-cookies .cc-cookie-accept"}],optOut:[{if:{exists:".cc-cookies .cc-cookie-decline"},then:[{click:".cc-cookies .cc-cookie-decline"}],else:[{hide:".cc-cookies"}]}]},{name:"cookiefirst.com",prehideSelectors:["#cookiefirst-root,.cookiefirst-root,[aria-labelledby=cookie-preference-panel-title]"],detectCmp:[{exists:"#cookiefirst-root,.cookiefirst-root"}],detectPopup:[{visible:"#cookiefirst-root,.cookiefirst-root"}],optIn:[{click:"button[data-cookiefirst-action=accept]"}],optOut:[{if:{exists:"button[data-cookiefirst-action=adjust]"},then:[{click:"button[data-cookiefirst-action=adjust]"},{waitForVisible:"[data-cookiefirst-widget=modal]",timeout:1e3},{eval:"EVAL_COOKIEFIRST_1"},{wait:1e3},{click:"button[data-cookiefirst-action=save]"}],else:[{click:"button[data-cookiefirst-action=reject]"}]}],test:[{eval:"EVAL_COOKIEFIRST_0"}]},{name:"Cookie Information Banner",prehideSelectors:["#cookie-information-template-wrapper"],detectCmp:[{exists:"#cookie-information-template-wrapper"}],detectPopup:[{visible:"#cookie-information-template-wrapper"}],optIn:[{eval:"EVAL_COOKIEINFORMATION_1"}],optOut:[{hide:"#cookie-information-template-wrapper",comment:"some templates don't hide the banner automatically"},{eval:"EVAL_COOKIEINFORMATION_0"}],test:[{eval:"EVAL_COOKIEINFORMATION_2"}]},{name:"cookieyes",prehideSelectors:[".cky-overlay,.cky-consent-container"],detectCmp:[{exists:".cky-consent-container"}],detectPopup:[{visible:".cky-consent-container"}],optIn:[{waitForThenClick:".cky-consent-container [data-cky-tag=accept-button]"}],optOut:[{if:{exists:".cky-consent-container [data-cky-tag=reject-button]"},then:[{waitForThenClick:".cky-consent-container [data-cky-tag=reject-button]"}],else:[{if:{exists:".cky-consent-container [data-cky-tag=settings-button]"},then:[{click:".cky-consent-container [data-cky-tag=settings-button]"},{waitFor:".cky-modal-open input[type=checkbox]"},{click:".cky-modal-open input[type=checkbox]:checked",all:!0,optional:!0},{waitForThenClick:".cky-modal [data-cky-tag=detail-save-button]"}],else:[{hide:".cky-consent-container,.cky-overlay"}]}]}],test:[{eval:"EVAL_COOKIEYES_0"}]},{name:"corona-in-zahlen.de",prehideSelectors:[".cookiealert"],detectCmp:[{exists:".cookiealert"}],detectPopup:[{visible:".cookiealert"}],optOut:[{click:".configurecookies"},{click:".confirmcookies"}],optIn:[{click:".acceptcookies"}]},{name:"crossfit-com",cosmetic:!0,prehideSelectors:['body #modal > div > div[class^="_wrapper_"]'],detectCmp:[{exists:'body #modal > div > div[class^="_wrapper_"]'}],detectPopup:[{visible:'body #modal > div > div[class^="_wrapper_"]'}],optIn:[{click:'button[aria-label="accept cookie policy"]'}],optOut:[{hide:'body #modal > div > div[class^="_wrapper_"]'}]},{name:"csu-landtag-de",runContext:{urlPattern:"^https://(www\\.|)?csu-landtag\\.de"},prehideSelectors:["#cookie-disclaimer"],detectCmp:[{exists:"#cookie-disclaimer"}],detectPopup:[{visible:"#cookie-disclaimer"}],optIn:[{click:"#cookieall"}],optOut:[{click:"#cookiesel"}]},{name:"dailymotion-us",cosmetic:!0,prehideSelectors:['div[class*="CookiePopup__desktopContainer"]:has(div[class*="CookiePopup"])'],detectCmp:[{exists:'div[class*="CookiePopup__desktopContainer"]'}],detectPopup:[{visible:'div[class*="CookiePopup__desktopContainer"]'}],optIn:[{click:'div[class*="CookiePopup__desktopContainer"] > button > span'}],optOut:[{hide:'div[class*="CookiePopup__desktopContainer"]'}]},{name:"dailymotion.com",runContext:{urlPattern:"^https://(www\\.)?dailymotion\\.com/"},prehideSelectors:['div[class*="Overlay__container"]:has(div[class*="TCF2Popup"])'],detectCmp:[{exists:'div[class*="TCF2Popup"]'}],detectPopup:[{visible:'[class*="TCF2Popup"] a[href^="https://www.dailymotion.com/legal/cookiemanagement"]'}],optIn:[{waitForThenClick:'button[class*="TCF2Popup__button"]:not([class*="TCF2Popup__personalize"])'}],optOut:[{waitForThenClick:'button[class*="TCF2ContinueWithoutAcceptingButton"]'}],test:[{eval:"EVAL_DAILYMOTION_0"}]},{name:"dan-com",vendorUrl:"https://unknown",runContext:{main:!0,frame:!1},prehideSelectors:[],detectCmp:[{exists:".cookie-banner.show .cookie-banner__content-all-btn"}],detectPopup:[{visible:".cookie-banner.show .cookie-banner__content-all-btn"}],optIn:[{waitForThenClick:".cookie-banner__content-all-btn"}],optOut:[{waitForThenClick:".cookie-banner__content-essential-btn"}]},{name:"deepl.com",prehideSelectors:[".dl_cookieBanner_container"],detectCmp:[{exists:".dl_cookieBanner_container"}],detectPopup:[{visible:".dl_cookieBanner_container"}],optOut:[{click:".dl_cookieBanner--buttonSelected"}],optIn:[{click:".dl_cookieBanner--buttonAll"}]},{name:"delta.com",runContext:{urlPattern:"^https://www\\.delta\\.com/"},cosmetic:!0,prehideSelectors:["ngc-cookie-banner"],detectCmp:[{exists:"div.cookie-footer-container"}],detectPopup:[{visible:"div.cookie-footer-container"}],optIn:[{click:" button.cookie-close-icon"}],optOut:[{hide:"div.cookie-footer-container"}]},{name:"dmgmedia-us",prehideSelectors:["#mol-ads-cmp-iframe, div.mol-ads-cmp > form > div"],detectCmp:[{exists:"div.mol-ads-cmp > form > div"}],detectPopup:[{waitForVisible:"div.mol-ads-cmp > form > div"}],optIn:[{waitForThenClick:"button.mol-ads-cmp--btn-primary"}],optOut:[{waitForThenClick:"div.mol-ads-ccpa--message > u > a"},{waitForVisible:".mol-ads-cmp--modal-dialog"},{waitForThenClick:"a.mol-ads-cmp-footer-privacy"},{waitForThenClick:"button.mol-ads-cmp--btn-secondary"}]},{name:"dmgmedia",prehideSelectors:['[data-project="mol-fe-cmp"]'],detectCmp:[{exists:'[data-project="mol-fe-cmp"] [class*=footer]'}],detectPopup:[{visible:'[data-project="mol-fe-cmp"] [class*=footer]'}],optIn:[{waitForThenClick:'[data-project="mol-fe-cmp"] button[class*=primary]'}],optOut:[{waitForThenClick:'[data-project="mol-fe-cmp"] button[class*=basic]'},{waitForVisible:'[data-project="mol-fe-cmp"] div[class*="tabContent"]'},{waitForThenClick:'[data-project="mol-fe-cmp"] div[class*="toggle"][class*="enabled"]',all:!0},{waitForThenClick:['[data-project="mol-fe-cmp"] [class*=footer]',"xpath///button[contains(., 'Save & Exit')]"]}]},{name:"dndbeyond",vendorUrl:"https://www.dndbeyond.com/",runContext:{urlPattern:"^https://(www\\.)?dndbeyond\\.com/"},prehideSelectors:["[id^=cookie-consent-banner]"],detectCmp:[{exists:"[id^=cookie-consent-banner]"}],detectPopup:[{visible:"[id^=cookie-consent-banner]"}],optIn:[{waitForThenClick:"#cookie-consent-granted"}],optOut:[{waitForThenClick:"#cookie-consent-denied"}],test:[{eval:"EVAL_DNDBEYOND_TEST"}]},{name:"dpgmedia-nl",prehideSelectors:["#pg-shadow-root-host"],detectCmp:[{exists:"#pg-shadow-root-host"}],detectPopup:[{visible:["#pg-shadow-root-host","#pg-modal"]}],optIn:[{waitForThenClick:["#pg-shadow-root-host","#pg-accept-btn"]}],optOut:[{waitForThenClick:["#pg-shadow-root-host","#pg-configure-btn"]},{waitForThenClick:["#pg-shadow-root-host","#pg-reject-btn"]}]},{name:"Drupal",detectCmp:[{exists:"#drupalorg-crosssite-gdpr"}],detectPopup:[{visible:"#drupalorg-crosssite-gdpr"}],optOut:[{click:".no"}],optIn:[{click:".yes"}]},{name:"WP DSGVO Tools",link:"https://wordpress.org/plugins/shapepress-dsgvo/",prehideSelectors:[".sp-dsgvo"],cosmetic:!0,detectCmp:[{exists:".sp-dsgvo.sp-dsgvo-popup-overlay"}],detectPopup:[{visible:".sp-dsgvo.sp-dsgvo-popup-overlay",check:"any"}],optIn:[{click:".sp-dsgvo-privacy-btn-accept-all",all:!0}],optOut:[{hide:".sp-dsgvo.sp-dsgvo-popup-overlay"}],test:[{eval:"EVAL_DSGVO_0"}]},{name:"dunelm.com",prehideSelectors:["div[data-testid=cookie-consent-modal-backdrop]"],detectCmp:[{exists:"div[data-testid=cookie-consent-message-contents]"}],detectPopup:[{visible:"div[data-testid=cookie-consent-message-contents]"}],optIn:[{click:'[data-testid="cookie-consent-allow-all"]'}],optOut:[{click:"button[data-testid=cookie-consent-adjust-settings]"},{click:"button[data-testid=cookie-consent-preferences-save]"}],test:[{eval:"EVAL_DUNELM_0"}]},{name:"ebay",vendorUrl:"https://ebay.com",cosmetic:!1,runContext:{main:!0,frame:!1,urlPattern:"^https://(www\\.)?ebay\\.([.a-z]+)/"},prehideSelectors:["#gdpr-banner"],detectCmp:[{exists:"#gdpr-banner"}],detectPopup:[{visible:"#gdpr-banner"}],optIn:[{waitForThenClick:"#gdpr-banner-accept"}],optOut:[{waitForThenClick:"#gdpr-banner-decline"}]},{name:"ecosia",vendorUrl:"https://www.ecosia.org/",runContext:{urlPattern:"^https://www\\.ecosia\\.org/"},prehideSelectors:[".cookie-wrapper"],detectCmp:[{exists:".cookie-wrapper > .cookie-notice"}],detectPopup:[{visible:".cookie-wrapper > .cookie-notice"}],optIn:[{waitForThenClick:"[data-test-id=cookie-notice-accept]"}],optOut:[{waitForThenClick:"[data-test-id=cookie-notice-reject]"}]},{name:"Ensighten ensModal",prehideSelectors:[".ensModal"],detectCmp:[{exists:".ensModal"},{visible:"#ensModalWrapper[style*=block]"}],detectPopup:[{visible:"#ensModalWrapper[style*=block]"}],optIn:[{waitForThenClick:"#modalAcceptButton"}],optOut:[{wait:500},{visible:"#ensModalWrapper[style*=block]"},{waitForThenClick:".ensCheckbox:checked",all:!0},{waitForThenClick:"#ensSave"}]},{name:"Ensighten ensNotifyBanner",prehideSelectors:["#ensNotifyBanner"],detectCmp:[{exists:"#ensNotifyBanner"}],detectPopup:[{visible:"#ensNotifyBanner[style*=block]"}],optIn:[{waitForThenClick:"#ensCloseBanner"}],optOut:[{wait:500},{visible:"#ensNotifyBanner[style*=block]"},{waitForThenClick:"#ensRejectAll,#rejectAll,#ensRejectBanner,.rejectAll,#ensCloseBanner",timeout:2e3}]},{name:"espace-personnel.agirc-arrco.fr",runContext:{urlPattern:"^https://espace-personnel\\.agirc-arrco\\.fr/"},prehideSelectors:[".cdk-overlay-container"],detectCmp:[{exists:".cdk-overlay-container app-esaa-cookie-component"}],detectPopup:[{visible:".cdk-overlay-container app-esaa-cookie-component"}],optIn:[{waitForThenClick:".btn-cookie-accepter"}],optOut:[{waitForThenClick:".btn-cookie-refuser"}]},{name:"etsy",prehideSelectors:["#gdpr-single-choice-overlay","#gdpr-privacy-settings"],detectCmp:[{exists:"#gdpr-single-choice-overlay"}],detectPopup:[{visible:"#gdpr-single-choice-overlay"}],optOut:[{click:"button[data-gdpr-open-full-settings]"},{waitForVisible:".gdpr-overlay-body input",timeout:3e3},{wait:1e3},{eval:"EVAL_ETSY_0"},{eval:"EVAL_ETSY_1"}],optIn:[{click:"button[data-gdpr-single-choice-accept]"}]},{name:"eu-cookie-compliance-banner",detectCmp:[{exists:"body.eu-cookie-compliance-popup-open"}],detectPopup:[{exists:"body.eu-cookie-compliance-popup-open"}],optIn:[{click:".agree-button"}],optOut:[{if:{visible:".decline-button,.eu-cookie-compliance-save-preferences-button"},then:[{click:".decline-button,.eu-cookie-compliance-save-preferences-button"}]},{hide:".eu-cookie-compliance-banner-info, #sliding-popup"}],test:[{eval:"EVAL_EU_COOKIE_COMPLIANCE_0"}]},{name:"EU Cookie Law",prehideSelectors:[".pea_cook_wrapper,.pea_cook_more_info_popover"],cosmetic:!0,detectCmp:[{exists:".pea_cook_wrapper"}],detectPopup:[{wait:500},{visible:".pea_cook_wrapper"}],optIn:[{click:"#pea_cook_btn"}],optOut:[{hide:".pea_cook_wrapper"}],test:[{eval:"EVAL_EU_COOKIE_LAW_0"}]},{name:"europa-eu",vendorUrl:"https://ec.europa.eu/",runContext:{urlPattern:"^https://[^/]*europa\\.eu/"},prehideSelectors:["#cookie-consent-banner"],detectCmp:[{exists:".cck-container"}],detectPopup:[{visible:".cck-container"}],optIn:[{waitForThenClick:'.cck-actions-button[href="#accept"]'}],optOut:[{waitForThenClick:'.cck-actions-button[href="#refuse"]',hide:".cck-container"}]},{name:"EZoic",prehideSelectors:["#ez-cookie-dialog-wrapper"],detectCmp:[{exists:"#ez-cookie-dialog-wrapper"}],detectPopup:[{visible:"#ez-cookie-dialog-wrapper"}],optIn:[{click:"#ez-accept-all",optional:!0},{eval:"EVAL_EZOIC_0",optional:!0}],optOut:[{wait:500},{click:"#ez-manage-settings"},{waitFor:"#ez-cookie-dialog input[type=checkbox]"},{click:"#ez-cookie-dialog input[type=checkbox]:checked",all:!0},{click:"#ez-save-settings"}],test:[{eval:"EVAL_EZOIC_1"}]},{name:"facebook",runContext:{urlPattern:"^https://([a-z0-9-]+\\.)?facebook\\.com/"},prehideSelectors:['div[data-testid="cookie-policy-manage-dialog"]'],detectCmp:[{exists:'div[data-testid="cookie-policy-manage-dialog"]'}],detectPopup:[{visible:'div[data-testid="cookie-policy-manage-dialog"]'}],optIn:[{waitForThenClick:'button[data-cookiebanner="accept_button"]'},{waitForVisible:'div[data-testid="cookie-policy-manage-dialog"]',check:"none"}],optOut:[{waitForThenClick:'button[data-cookiebanner="accept_only_essential_button"]'},{waitForVisible:'div[data-testid="cookie-policy-manage-dialog"]',check:"none"}]},{name:"fides",vendorUrl:"https://github.com/ethyca/fides",prehideSelectors:["#fides-overlay"],detectCmp:[{exists:"#fides-overlay #fides-banner"}],detectPopup:[{visible:"#fides-overlay #fides-banner"},{eval:"EVAL_FIDES_DETECT_POPUP"}],optIn:[{waitForThenClick:"#fides-banner .fides-accept-all-button"}],optOut:[{waitForThenClick:"#fides-banner .fides-reject-all-button"}]},{name:"funding-choices",prehideSelectors:[".fc-consent-root,.fc-dialog-container,.fc-dialog-overlay,.fc-dialog-content"],detectCmp:[{exists:".fc-consent-root"}],detectPopup:[{exists:".fc-dialog-container"}],optOut:[{click:".fc-cta-do-not-consent,.fc-cta-manage-options"},{click:".fc-preference-consent:checked,.fc-preference-legitimate-interest:checked",all:!0,optional:!0},{click:".fc-confirm-choices",optional:!0}],optIn:[{click:".fc-cta-consent"}]},{name:"geeks-for-geeks",runContext:{urlPattern:"^https://www\\.geeksforgeeks\\.org/"},cosmetic:!0,prehideSelectors:[".cookie-consent"],detectCmp:[{exists:".cookie-consent"}],detectPopup:[{visible:".cookie-consent"}],optIn:[{click:".cookie-consent button.consent-btn"}],optOut:[{hide:".cookie-consent"}]},{name:"google-consent-standalone",prehideSelectors:[],detectCmp:[{exists:'a[href^="https://policies.google.com/technologies/cookies"'},{exists:'form[action^="https://consent.google."][action$="/save"]'}],detectPopup:[{visible:'a[href^="https://policies.google.com/technologies/cookies"'}],optIn:[{waitForThenClick:'form[action^="https://consent.google."][action$="/save"]:has(input[name=set_eom][value=false]) button'}],optOut:[{waitForThenClick:'form[action^="https://consent.google."][action$="/save"]:has(input[name=set_eom][value=true]) button'}]},{name:"google-cookiebar",vendorUrl:"https://www.android.com/better-together/quick-share-app/",cosmetic:!1,prehideSelectors:[".glue-cookie-notification-bar"],detectCmp:[{exists:".glue-cookie-notification-bar"}],detectPopup:[{visible:".glue-cookie-notification-bar"}],optIn:[{waitForThenClick:".glue-cookie-notification-bar__accept"}],optOut:[{if:{exists:".glue-cookie-notification-bar__reject"},then:[{click:".glue-cookie-notification-bar__reject"}],else:[{hide:".glue-cookie-notification-bar"}]}],test:[]},{name:"google.com",prehideSelectors:[".HTjtHe#xe7COe"],detectCmp:[{exists:".HTjtHe#xe7COe"},{exists:'.HTjtHe#xe7COe a[href^="https://policies.google.com/technologies/cookies"]'}],detectPopup:[{visible:".HTjtHe#xe7COe button#W0wltc"}],optIn:[{waitForThenClick:".HTjtHe#xe7COe button#L2AGLb"}],optOut:[{waitForThenClick:".HTjtHe#xe7COe button#W0wltc"}],test:[{eval:"EVAL_GOOGLE_0"}]},{name:"gov.uk",detectCmp:[{exists:"#global-cookie-message"}],detectPopup:[{exists:"#global-cookie-message"}],optIn:[{click:"button[data-accept-cookies=true]"}],optOut:[{click:"button[data-reject-cookies=true],#reject-cookies"},{click:"button[data-hide-cookie-banner=true],#hide-cookie-decision"}]},{name:"hashicorp",vendorUrl:"https://hashicorp.com/",runContext:{urlPattern:"^https://[^.]*\\.hashicorp\\.com/"},prehideSelectors:["[data-testid=consent-banner]"],detectCmp:[{exists:"[data-testid=consent-banner]"}],detectPopup:[{visible:"[data-testid=consent-banner]"}],optIn:[{waitForThenClick:"[data-testid=accept]"}],optOut:[{waitForThenClick:"[data-testid=manage-preferences]"},{waitForThenClick:"[data-testid=consent-mgr-dialog] [data-ga-button=save-preferences]"}]},{name:"healthline-media",prehideSelectors:["#modal-host > div.no-hash > div.window-wrapper"],detectCmp:[{exists:"#modal-host > div.no-hash > div.window-wrapper, div[data-testid=qualtrics-container]"}],detectPopup:[{exists:"#modal-host > div.no-hash > div.window-wrapper, div[data-testid=qualtrics-container]"}],optIn:[{click:"#modal-host > div.no-hash > div.window-wrapper > div:last-child button"}],optOut:[{if:{exists:'#modal-host > div.no-hash > div.window-wrapper > div:last-child a[href="/privacy-settings"]'},then:[{click:'#modal-host > div.no-hash > div.window-wrapper > div:last-child a[href="/privacy-settings"]'}],else:[{waitForVisible:"div#__next"},{click:"#__next div:nth-child(1) > button:first-child"}]}]},{name:"hema",prehideSelectors:[".cookie-modal"],detectCmp:[{visible:".cookie-modal .cookie-accept-btn"}],detectPopup:[{visible:".cookie-modal .cookie-accept-btn"}],optIn:[{waitForThenClick:".cookie-modal .cookie-accept-btn"}],optOut:[{waitForThenClick:".cookie-modal .js-cookie-reject-btn"}],test:[{eval:"EVAL_HEMA_TEST_0"}]},{name:"hetzner.com",runContext:{urlPattern:"^https://www\\.hetzner\\.com/"},prehideSelectors:["#CookieConsent"],detectCmp:[{exists:"#CookieConsent"}],detectPopup:[{visible:"#CookieConsent"}],optIn:[{click:"#CookieConsentGiven"}],optOut:[{click:"#CookieConsentDeclined"}]},{name:"hl.co.uk",prehideSelectors:[".cookieModalContent","#cookie-banner-overlay"],detectCmp:[{exists:"#cookie-banner-overlay"}],detectPopup:[{exists:"#cookie-banner-overlay"}],optIn:[{click:"#acceptCookieButton"}],optOut:[{click:"#manageCookie"},{hide:".cookieSettingsModal"},{waitFor:"#AOCookieToggle"},{click:"#AOCookieToggle[aria-pressed=true]",optional:!0},{waitFor:"#TPCookieToggle"},{click:"#TPCookieToggle[aria-pressed=true]",optional:!0},{click:"#updateCookieButton"}]},{name:"holidaymedia",vendorUrl:"https://holidaymedia.nl/",prehideSelectors:["dialog[data-cookie-consent]"],detectCmp:[{exists:"dialog[data-cookie-consent]"}],detectPopup:[{visible:"dialog[data-cookie-consent]"}],optIn:[{waitForThenClick:"button.cookie-consent__button--accept-all"}],optOut:[{waitForThenClick:'a[data-cookie-accept="functional"]',timeout:2e3}]},{name:"hu-manity",vendorUrl:"https://hu-manity.co/",prehideSelectors:["#hu.hu-wrapper"],detectCmp:[{exists:"#hu.hu-visible"}],detectPopup:[{visible:"#hu.hu-visible"}],optIn:[{waitForThenClick:"[data-hu-action=cookies-notice-consent-choices-3]"},{waitForThenClick:"#hu-cookies-save"}],optOut:[{waitForThenClick:"#hu-cookies-save"}]},{name:"hubspot",detectCmp:[{exists:"#hs-eu-cookie-confirmation"}],detectPopup:[{visible:"#hs-eu-cookie-confirmation"}],optIn:[{click:"#hs-eu-confirmation-button"}],optOut:[{click:"#hs-eu-decline-button"}]},{name:"indeed.com",cosmetic:!0,prehideSelectors:["#CookiePrivacyNotice"],detectCmp:[{exists:"#CookiePrivacyNotice"}],detectPopup:[{visible:"#CookiePrivacyNotice"}],optIn:[{click:"#CookiePrivacyNotice button[data-gnav-element-name=CookiePrivacyNoticeOk]"}],optOut:[{hide:"#CookiePrivacyNotice"}]},{name:"ing.de",runContext:{urlPattern:"^https://www\\.ing\\.de/"},cosmetic:!0,prehideSelectors:['div[slot="backdrop"]'],detectCmp:[{exists:'[data-tag-name="ing-cc-dialog-frame"]'}],detectPopup:[{visible:'[data-tag-name="ing-cc-dialog-frame"]'}],optIn:[{click:['[data-tag-name="ing-cc-dialog-level0"]','[data-tag-name="ing-cc-button"][class*="accept"]']}],optOut:[{click:['[data-tag-name="ing-cc-dialog-level0"]','[data-tag-name="ing-cc-button"][class*="more"]']}]},{name:"instagram",vendorUrl:"https://instagram.com",runContext:{urlPattern:"^https://www\\.instagram\\.com/"},prehideSelectors:[],detectCmp:[{exists:'xpath///span[contains(., "Vill du tillåta användningen av cookies från Instagram i den här webbläsaren?") or contains(., "Allow the use of cookies from Instagram on this browser?") or contains(., "Povolit v prohlížeči použití souborů cookie z Instagramu?") or contains(., "Dopustiti upotrebu kolačića s Instagrama na ovom pregledniku?") or contains(., "Разрешить использование файлов cookie от Instagram в этом браузере?") or contains(., "Vuoi consentire l\'uso dei cookie di Instagram su questo browser?") or contains(., "Povoliť používanie cookies zo služby Instagram v tomto prehliadači?") or contains(., "Die Verwendung von Cookies durch Instagram in diesem Browser erlauben?") or contains(., "Sallitaanko Instagramin evästeiden käyttö tällä selaimella?") or contains(., "Engedélyezed az Instagram cookie-jainak használatát ebben a böngészőben?") or contains(., "Het gebruik van cookies van Instagram toestaan in deze browser?") or contains(., "Bu tarayıcıda Instagram\'dan çerez kullanımına izin verilsin mi?") or contains(., "Permitir o uso de cookies do Instagram neste navegador?") or contains(., "Permiţi folosirea modulelor cookie de la Instagram în acest browser?") or contains(., "Autoriser l’utilisation des cookies d’Instagram sur ce navigateur ?") or contains(., "¿Permitir el uso de cookies de Instagram en este navegador?") or contains(., "Zezwolić na użycie plików cookie z Instagramu w tej przeglądarce?") or contains(., "Να επιτρέπεται η χρήση cookies από τo Instagram σε αυτό το πρόγραμμα περιήγησης;") or contains(., "Разрешавате ли използването на бисквитки от Instagram на този браузър?") or contains(., "Vil du tillade brugen af cookies fra Instagram i denne browser?") or contains(., "Vil du tillate bruk av informasjonskapsler fra Instagram i denne nettleseren?")]'}],detectPopup:[{visible:'xpath///span[contains(., "Vill du tillåta användningen av cookies från Instagram i den här webbläsaren?") or contains(., "Allow the use of cookies from Instagram on this browser?") or contains(., "Povolit v prohlížeči použití souborů cookie z Instagramu?") or contains(., "Dopustiti upotrebu kolačića s Instagrama na ovom pregledniku?") or contains(., "Разрешить использование файлов cookie от Instagram в этом браузере?") or contains(., "Vuoi consentire l\'uso dei cookie di Instagram su questo browser?") or contains(., "Povoliť používanie cookies zo služby Instagram v tomto prehliadači?") or contains(., "Die Verwendung von Cookies durch Instagram in diesem Browser erlauben?") or contains(., "Sallitaanko Instagramin evästeiden käyttö tällä selaimella?") or contains(., "Engedélyezed az Instagram cookie-jainak használatát ebben a böngészőben?") or contains(., "Het gebruik van cookies van Instagram toestaan in deze browser?") or contains(., "Bu tarayıcıda Instagram\'dan çerez kullanımına izin verilsin mi?") or contains(., "Permitir o uso de cookies do Instagram neste navegador?") or contains(., "Permiţi folosirea modulelor cookie de la Instagram în acest browser?") or contains(., "Autoriser l’utilisation des cookies d’Instagram sur ce navigateur ?") or contains(., "¿Permitir el uso de cookies de Instagram en este navegador?") or contains(., "Zezwolić na użycie plików cookie z Instagramu w tej przeglądarce?") or contains(., "Να επιτρέπεται η χρήση cookies από τo Instagram σε αυτό το πρόγραμμα περιήγησης;") or contains(., "Разрешавате ли използването на бисквитки от Instagram на този браузър?") or contains(., "Vil du tillade brugen af cookies fra Instagram i denne browser?") or contains(., "Vil du tillate bruk av informasjonskapsler fra Instagram i denne nettleseren?")]'}],optIn:[{waitForThenClick:"xpath///button[contains(., 'Tillad alle cookies') or contains(., 'Alle Cookies erlauben') or contains(., 'Allow all cookies') or contains(., 'Разрешаване на всички бисквитки') or contains(., 'Tillåt alla cookies') or contains(., 'Povolit všechny soubory cookie') or contains(., 'Tüm çerezlere izin ver') or contains(., 'Permite toate modulele cookie') or contains(., 'Να επιτρέπονται όλα τα cookies') or contains(., 'Tillat alle informasjonskapsler') or contains(., 'Povoliť všetky cookies') or contains(., 'Permitir todas las cookies') or contains(., 'Permitir todos os cookies') or contains(., 'Alle cookies toestaan') or contains(., 'Salli kaikki evästeet') or contains(., 'Consenti tutti i cookie') or contains(., 'Az összes cookie engedélyezése') or contains(., 'Autoriser tous les cookies') or contains(., 'Zezwól na wszystkie pliki cookie') or contains(., 'Разрешить все cookie') or contains(., 'Dopusti sve kolačiće')]"}],optOut:[{waitForThenClick:"xpath///button[contains(., 'Отклонить необязательные файлы cookie') or contains(., 'Decline optional cookies') or contains(., 'Refuser les cookies optionnels') or contains(., 'Hylkää valinnaiset evästeet') or contains(., 'Afvis valgfrie cookies') or contains(., 'Odmietnuť nepovinné cookies') or contains(., 'Απόρριψη προαιρετικών cookies') or contains(., 'Neka valfria cookies') or contains(., 'Optionale Cookies ablehnen') or contains(., 'Rifiuta cookie facoltativi') or contains(., 'Odbij neobavezne kolačiće') or contains(., 'Avvis valgfrie informasjonskapsler') or contains(., 'İsteğe bağlı çerezleri reddet') or contains(., 'Recusar cookies opcionais') or contains(., 'Optionele cookies afwijzen') or contains(., 'Rechazar cookies opcionales') or contains(., 'Odrzuć opcjonalne pliki cookie') or contains(., 'Отхвърляне на бисквитките по избор') or contains(., 'Odmítnout volitelné soubory cookie') or contains(., 'Refuză modulele cookie opţionale') or contains(., 'A nem kötelező cookie-k elutasítása')]"},{wait:2e3}]},{name:"ionos.de",prehideSelectors:[".privacy-consent--backdrop",".privacy-consent--modal"],detectCmp:[{exists:".privacy-consent--modal"}],detectPopup:[{visible:".privacy-consent--modal"}],optIn:[{click:"#selectAll"}],optOut:[{click:".footer-config-link"},{click:"#confirmSelection"}]},{name:"itopvpn.com",cosmetic:!0,prehideSelectors:[".pop-cookie"],detectCmp:[{exists:".pop-cookie"}],detectPopup:[{exists:".pop-cookie"}],optIn:[{click:"#_pcookie"}],optOut:[{hide:".pop-cookie"}]},{name:"iubenda",prehideSelectors:["#iubenda-cs-banner"],detectCmp:[{exists:"#iubenda-cs-banner"}],detectPopup:[{visible:".iubenda-cs-accept-btn"}],optIn:[{waitForThenClick:".iubenda-cs-accept-btn"}],optOut:[{waitForThenClick:".iubenda-cs-customize-btn"},{eval:"EVAL_IUBENDA_0"},{waitForThenClick:"#iubFooterBtn"}],test:[{eval:"EVAL_IUBENDA_1"}]},{name:"iWink",prehideSelectors:["body.cookies-request #cookie-bar"],detectCmp:[{exists:"body.cookies-request #cookie-bar"}],detectPopup:[{visible:"body.cookies-request #cookie-bar"}],optIn:[{waitForThenClick:"body.cookies-request #cookie-bar .allow-cookies"}],optOut:[{waitForThenClick:"body.cookies-request #cookie-bar .disallow-cookies"}],test:[{eval:"EVAL_IWINK_TEST"}]},{name:"jdsports",vendorUrl:"https://www.jdsports.co.uk/",runContext:{urlPattern:"^https://(www|m)\\.jdsports\\."},prehideSelectors:[".miniConsent,#PrivacyPolicyBanner"],detectCmp:[{exists:".miniConsent,#PrivacyPolicyBanner"}],detectPopup:[{visible:".miniConsent,#PrivacyPolicyBanner"}],optIn:[{waitForThenClick:".miniConsent .accept-all-cookies"}],optOut:[{if:{exists:"#PrivacyPolicyBanner"},then:[{hide:"#PrivacyPolicyBanner"}],else:[{waitForThenClick:"#cookie-settings"},{waitForThenClick:"#reject-all-cookies"}]}]},{name:"johnlewis.com",prehideSelectors:["div[class^=pecr-cookie-banner-]"],detectCmp:[{exists:"div[class^=pecr-cookie-banner-]"}],detectPopup:[{exists:"div[class^=pecr-cookie-banner-]"}],optOut:[{click:"button[data-test^=manage-cookies]"},{wait:"500"},{click:"label[data-test^=toggle][class*=checked]:not([class*=disabled])",all:!0,optional:!0},{click:"button[data-test=save-preferences]"}],optIn:[{click:"button[data-test=allow-all]"}]},{name:"jquery.cookieBar",vendorUrl:"https://github.com/kovarp/jquery.cookieBar",prehideSelectors:[".cookie-bar"],cosmetic:!0,detectCmp:[{exists:".cookie-bar .cookie-bar__message,.cookie-bar .cookie-bar__buttons"}],detectPopup:[{visible:".cookie-bar .cookie-bar__message,.cookie-bar .cookie-bar__buttons",check:"any"}],optIn:[{click:".cookie-bar .cookie-bar__btn"}],optOut:[{hide:".cookie-bar"}],test:[{visible:".cookie-bar .cookie-bar__message,.cookie-bar .cookie-bar__buttons",check:"none"},{eval:"EVAL_JQUERY_COOKIEBAR_0"}]},{name:"justwatch.com",prehideSelectors:[".consent-banner"],detectCmp:[{exists:".consent-banner .consent-banner__actions"}],detectPopup:[{visible:".consent-banner .consent-banner__actions"}],optIn:[{click:".consent-banner__actions button.basic-button.primary"}],optOut:[{click:".consent-banner__actions button.basic-button.secondary"},{waitForThenClick:".consent-modal__footer button.basic-button.secondary"},{waitForThenClick:".consent-modal ion-content > div > a:nth-child(9)"},{click:"label.consent-switch input[type=checkbox]:checked",all:!0,optional:!0},{waitForVisible:".consent-modal__footer button.basic-button.primary"},{click:".consent-modal__footer button.basic-button.primary"}]},{name:"kconsent",cosmetic:!1,runContext:{main:!0,frame:!1},prehideSelectors:[".kc-overlay"],detectCmp:[{exists:"#kconsent"}],detectPopup:[{visible:".kc-dialog"}],optIn:[{waitForThenClick:"#kc-acceptAndHide"}],optOut:[{waitForThenClick:"#kc-denyAndHide"}]},{name:"ketch",vendorUrl:"https://www.ketch.com",runContext:{frame:!1,main:!0},intermediate:!1,prehideSelectors:["#lanyard_root div[role='dialog']"],detectCmp:[{exists:"#lanyard_root div[role='dialog']"}],detectPopup:[{visible:"#lanyard_root div[role='dialog']"}],optIn:[{if:{exists:"#lanyard_root button[class='confirmButton']"},then:[{waitForThenClick:"#lanyard_root div[class*=buttons] > :nth-child(2)"},{click:"#lanyard_root button[class='confirmButton']"}],else:[{waitForThenClick:"#lanyard_root div[class*=buttons] > :nth-child(2)"}]}],optOut:[{if:{exists:"#lanyard_root [aria-describedby=banner-description]"},then:[{waitForThenClick:"#lanyard_root div[class*=buttons] > button[class*=secondaryButton], #lanyard_root button[class*=buttons-secondary]",comment:"can be either settings or reject button"}]},{waitFor:"#lanyard_root [aria-describedby=preference-description],#lanyard_root [aria-describedby=modal-description], #ketch-preferences",timeout:1e3,optional:!0},{if:{exists:"#lanyard_root [aria-describedby=preference-description],#lanyard_root [aria-describedby=modal-description], #ketch-preferences"},then:[{waitForThenClick:"#lanyard_root button[class*=rejectButton], #lanyard_root button[class*=rejectAllButton]"},{click:"#lanyard_root button[class*=confirmButton],#lanyard_root div[class*=actions_] > button:nth-child(1), #lanyard_root button[class*=actionButton]"}]}],test:[{eval:"EVAL_KETCH_TEST"}]},{name:"kleinanzeigen-de",runContext:{urlPattern:"^https?://(www\\.)?kleinanzeigen\\.de"},prehideSelectors:["#gdpr-banner-container"],detectCmp:[{any:[{exists:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-cmp-button]"},{exists:"#ConsentManagementPage"}]}],detectPopup:[{any:[{visible:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-cmp-button]"},{visible:"#ConsentManagementPage"}]}],optIn:[{if:{exists:"#gdpr-banner-container #gdpr-banner"},then:[{click:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-accept]"}],else:[{click:"#ConsentManagementPage .Button-primary"}]}],optOut:[{if:{exists:"#gdpr-banner-container #gdpr-banner"},then:[{click:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-cmp-button]"}],else:[{click:"#ConsentManagementPage .Button-secondary"}]}]},{name:"lightbox",prehideSelectors:[".darken-layer.open,.lightbox.lightbox--cookie-consent"],detectCmp:[{exists:"body.cookie-consent-is-active div.lightbox--cookie-consent > div.lightbox__content > div.cookie-consent[data-jsb]"}],detectPopup:[{visible:"body.cookie-consent-is-active div.lightbox--cookie-consent > div.lightbox__content > div.cookie-consent[data-jsb]"}],optOut:[{click:".cookie-consent__footer > button[type='submit']:not([data-button='selectAll'])"}],optIn:[{click:".cookie-consent__footer > button[type='submit'][data-button='selectAll']"}]},{name:"lineagrafica",vendorUrl:"https://addons.prestashop.com/en/legal/8734-eu-cookie-law-gdpr-banner-blocker.html",cosmetic:!0,prehideSelectors:["#lgcookieslaw_banner,#lgcookieslaw_modal,.lgcookieslaw-overlay"],detectCmp:[{exists:"#lgcookieslaw_banner,#lgcookieslaw_modal,.lgcookieslaw-overlay"}],detectPopup:[{exists:"#lgcookieslaw_banner,#lgcookieslaw_modal,.lgcookieslaw-overlay"}],optIn:[{waitForThenClick:"#lgcookieslaw_accept"}],optOut:[{hide:"#lgcookieslaw_banner,#lgcookieslaw_modal,.lgcookieslaw-overlay"}]},{name:"linkedin.com",prehideSelectors:[".artdeco-global-alert[type=COOKIE_CONSENT]"],detectCmp:[{exists:".artdeco-global-alert[type=COOKIE_CONSENT]"}],detectPopup:[{visible:".artdeco-global-alert[type=COOKIE_CONSENT]"}],optIn:[{waitForVisible:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=ACCEPT]"},{wait:500},{waitForThenClick:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=ACCEPT]"}],optOut:[{waitForVisible:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=DENY]"},{wait:500},{waitForThenClick:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=DENY]"}],test:[{waitForVisible:".artdeco-global-alert[type=COOKIE_CONSENT]",check:"none"}]},{name:"livejasmin",vendorUrl:"https://www.livejasmin.com/",runContext:{urlPattern:"^https://(m|www)\\.livejasmin\\.com/"},prehideSelectors:["#consent_modal"],detectCmp:[{exists:"#consent_modal"}],detectPopup:[{visible:"#consent_modal"}],optIn:[{waitForThenClick:"#consent_modal button[data-testid=ButtonStyledButton]:first-of-type"}],optOut:[{waitForThenClick:"#consent_modal button[data-testid=ButtonStyledButton]:nth-of-type(2)"},{waitForVisible:"[data-testid=PrivacyPreferenceCenterWithConsentCookieContent]"},{click:"[data-testid=PrivacyPreferenceCenterWithConsentCookieContent] input[data-testid=PrivacyPreferenceCenterWithConsentCookieSwitch]:checked",optional:!0,all:!0},{waitForThenClick:"[data-testid=PrivacyPreferenceCenterWithConsentCookieContent] button[data-testid=ButtonStyledButton]:last-child"}]},{name:"macpaw.com",cosmetic:!0,prehideSelectors:['div[data-banner="cookies"]'],detectCmp:[{exists:'div[data-banner="cookies"]'}],detectPopup:[{exists:'div[data-banner="cookies"]'}],optIn:[{click:'button[data-banner-close="cookies"]'}],optOut:[{hide:'div[data-banner="cookies"]'}]},{name:"marksandspencer.com",cosmetic:!0,detectCmp:[{exists:".navigation-cookiebbanner"}],detectPopup:[{visible:".navigation-cookiebbanner"}],optOut:[{hide:".navigation-cookiebbanner"}],optIn:[{click:".navigation-cookiebbanner__submit"}]},{name:"mediamarkt.de",prehideSelectors:["div[aria-labelledby=pwa-consent-layer-title]","div[class^=StyledConsentLayerWrapper-]"],detectCmp:[{exists:"div[aria-labelledby^=pwa-consent-layer-title]"}],detectPopup:[{exists:"div[aria-labelledby^=pwa-consent-layer-title]"}],optOut:[{click:"button[data-test^=pwa-consent-layer-deny-all]"}],optIn:[{click:"button[data-test^=pwa-consent-layer-accept-all"}]},{name:"Mediavine",prehideSelectors:['[data-name="mediavine-gdpr-cmp"]'],detectCmp:[{exists:'[data-name="mediavine-gdpr-cmp"]'}],detectPopup:[{wait:500},{visible:'[data-name="mediavine-gdpr-cmp"]'}],optIn:[{waitForThenClick:'[data-name="mediavine-gdpr-cmp"] [format="primary"]'}],optOut:[{waitForThenClick:'[data-name="mediavine-gdpr-cmp"] [data-view="manageSettings"]'},{waitFor:'[data-name="mediavine-gdpr-cmp"] input[type=checkbox]'},{eval:"EVAL_MEDIAVINE_0",optional:!0},{click:'[data-name="mediavine-gdpr-cmp"] [format="secondary"]'}]},{name:"medium",vendorUrl:"https://medium.com",cosmetic:!0,runContext:{main:!0,frame:!1,urlPattern:"^https://([a-z0-9-]+\\.)?medium\\.com/"},prehideSelectors:[],detectCmp:[{exists:'div:has(> div > div > div[role=alert] > a[href^="https://policy.medium.com/medium-privacy-policy-"])'}],detectPopup:[{visible:'div:has(> div > div > div[role=alert] > a[href^="https://policy.medium.com/medium-privacy-policy-"])'}],optIn:[{waitForThenClick:"[data-testid=close-button]"}],optOut:[{hide:'div:has(> div > div > div[role=alert] > a[href^="https://policy.medium.com/medium-privacy-policy-"])'}]},{name:"microsoft.com",prehideSelectors:["#wcpConsentBannerCtrl"],detectCmp:[{exists:"#wcpConsentBannerCtrl"}],detectPopup:[{exists:"#wcpConsentBannerCtrl"}],optOut:[{eval:"EVAL_MICROSOFT_0"}],optIn:[{eval:"EVAL_MICROSOFT_1"}],test:[{eval:"EVAL_MICROSOFT_2"}]},{name:"midway-usa",runContext:{urlPattern:"^https://www\\.midwayusa\\.com/"},cosmetic:!0,prehideSelectors:["#cookie-container"],detectCmp:[{exists:['div[aria-label="Cookie Policy Banner"]']}],detectPopup:[{visible:"#cookie-container"}],optIn:[{click:"button#cookie-btn"}],optOut:[{hide:'div[aria-label="Cookie Policy Banner"]'}]},{name:"moneysavingexpert.com",detectCmp:[{exists:"dialog[data-testid=accept-our-cookies-dialog]"}],detectPopup:[{visible:"dialog[data-testid=accept-our-cookies-dialog]"}],optIn:[{click:"#banner-accept"}],optOut:[{click:"#banner-manage"},{click:"#pc-confirm"}]},{name:"monzo.com",prehideSelectors:[".cookie-alert, cookie-alert__content"],detectCmp:[{exists:'div.cookie-alert[role="dialog"]'},{exists:'a[href*="monzo"]'}],detectPopup:[{visible:".cookie-alert__content"}],optIn:[{click:".js-accept-cookie-policy"}],optOut:[{click:".js-decline-cookie-policy"}]},{name:"Moove",prehideSelectors:["#moove_gdpr_cookie_info_bar"],detectCmp:[{exists:"#moove_gdpr_cookie_info_bar"}],detectPopup:[{visible:"#moove_gdpr_cookie_info_bar:not(.moove-gdpr-info-bar-hidden)"}],optIn:[{waitForThenClick:".moove-gdpr-infobar-allow-all"}],optOut:[{if:{exists:"#moove_gdpr_cookie_info_bar .change-settings-button"},then:[{click:"#moove_gdpr_cookie_info_bar .change-settings-button"},{waitForVisible:"#moove_gdpr_cookie_modal"},{eval:"EVAL_MOOVE_0"},{click:".moove-gdpr-modal-save-settings"}],else:[{hide:"#moove_gdpr_cookie_info_bar"}]}],test:[{visible:"#moove_gdpr_cookie_info_bar",check:"none"}]},{name:"national-lottery.co.uk",detectCmp:[{exists:".cuk_cookie_consent"}],detectPopup:[{visible:".cuk_cookie_consent",check:"any"}],optOut:[{click:".cuk_cookie_consent_manage_pref"},{click:".cuk_cookie_consent_save_pref"},{click:".cuk_cookie_consent_close"}],optIn:[{click:".cuk_cookie_consent_accept_all"}]},{name:"nba.com",runContext:{urlPattern:"^https://(www\\.)?nba.com/"},cosmetic:!0,prehideSelectors:["#onetrust-banner-sdk"],detectCmp:[{exists:"#onetrust-banner-sdk"}],detectPopup:[{visible:"#onetrust-banner-sdk"}],optIn:[{click:"#onetrust-accept-btn-handler"}],optOut:[{hide:"#onetrust-banner-sdk"}]},{name:"netbeat.de",runContext:{urlPattern:"^https://(www\\.)?netbeat\\.de/"},prehideSelectors:["div#cookieWarning"],detectCmp:[{exists:"div#cookieWarning"}],detectPopup:[{visible:"div#cookieWarning"}],optIn:[{waitForThenClick:"a#btnCookiesAcceptAll"}],optOut:[{waitForThenClick:"a#btnCookiesDenyAll"}]},{name:"netflix.de",detectCmp:[{exists:"#cookie-disclosure"}],detectPopup:[{visible:".cookie-disclosure-message",check:"any"}],optIn:[{click:".btn-accept"}],optOut:[{hide:"#cookie-disclosure"},{click:".btn-reject"}]},{name:"nhs.uk",prehideSelectors:["#nhsuk-cookie-banner"],detectCmp:[{exists:"#nhsuk-cookie-banner"}],detectPopup:[{exists:"#nhsuk-cookie-banner"}],optOut:[{click:"#nhsuk-cookie-banner__link_accept"}],optIn:[{click:"#nhsuk-cookie-banner__link_accept_analytics"}]},{name:"nike",vendorUrl:"https://nike.com",runContext:{urlPattern:"^https://(www\\.)?nike\\.com/"},prehideSelectors:[],detectCmp:[{exists:"[data-testid=cookie-dialog-root]"}],detectPopup:[{visible:"[data-testid=cookie-dialog-root]"}],optIn:[{waitForThenClick:"[data-testid=dialog-accept-button]"}],optOut:[{waitForThenClick:"input[type=radio][id$=-declineLabel]",all:!0},{waitForThenClick:"[data-testid=confirm-choice-button]"}]},{name:"notice-cookie",prehideSelectors:[".button--notice"],cosmetic:!0,detectCmp:[{exists:".notice--cookie"}],detectPopup:[{visible:".notice--cookie"}],optIn:[{click:".button--notice"}],optOut:[{hide:".notice--cookie"}]},{name:"nrk.no",cosmetic:!0,prehideSelectors:[".nrk-masthead__info-banner--cookie"],detectCmp:[{exists:".nrk-masthead__info-banner--cookie"}],detectPopup:[{exists:".nrk-masthead__info-banner--cookie"}],optIn:[{click:"div.nrk-masthead__info-banner--cookie button > span:has(+ svg.nrk-close)"}],optOut:[{hide:".nrk-masthead__info-banner--cookie"}]},{name:"obi.de",prehideSelectors:[".disc-cp--active"],detectCmp:[{exists:".disc-cp-modal__modal"}],detectPopup:[{visible:".disc-cp-modal__modal"}],optIn:[{click:".js-disc-cp-accept-all"}],optOut:[{click:".js-disc-cp-deny-all"}]},{name:"om",vendorUrl:"https://olli-machts.de/en/extension/cookie-manager",prehideSelectors:[".tx-om-cookie-consent"],detectCmp:[{exists:".tx-om-cookie-consent .active[data-omcookie-panel]"}],detectPopup:[{exists:".tx-om-cookie-consent .active[data-omcookie-panel]"}],optIn:[{waitForThenClick:"[data-omcookie-panel-save=all]"}],optOut:[{if:{exists:"[data-omcookie-panel-save=min]"},then:[{waitForThenClick:"[data-omcookie-panel-save=min]"}],else:[{click:"input[data-omcookie-panel-grp]:checked:not(:disabled)",all:!0,optional:!0},{waitForThenClick:"[data-omcookie-panel-save=save]"}]}]},{name:"onlyFans.com",runContext:{urlPattern:"^https://onlyfans\\.com/"},prehideSelectors:["div.b-cookies-informer"],detectCmp:[{exists:"div.b-cookies-informer"}],detectPopup:[{exists:"div.b-cookies-informer"}],optIn:[{click:"div.b-cookies-informer__nav > button:nth-child(2)"}],optOut:[{click:"div.b-cookies-informer__nav > button:nth-child(1)"},{if:{exists:"div.b-cookies-informer__switchers"},then:[{click:"div.b-cookies-informer__switchers input:not([disabled])",all:!0},{click:"div.b-cookies-informer__nav > button"}]}]},{name:"openai",vendorUrl:"https://platform.openai.com/",cosmetic:!1,runContext:{urlPattern:"^https://([a-z0-9-]+\\.)?openai\\.com/"},prehideSelectors:["[data-testid=cookie-consent-banner]"],detectCmp:[{exists:"[data-testid=cookie-consent-banner]"}],detectPopup:[{visible:"[data-testid=cookie-consent-banner]"}],optIn:[{waitForThenClick:"xpath///button[contains(., 'Accept all')]"}],optOut:[{waitForThenClick:"xpath///button[contains(., 'Reject all')]"}],test:[{wait:500},{eval:"EVAL_OPENAI_TEST"}]},{name:"openli",vendorUrl:"https://openli.com",prehideSelectors:[".legalmonster-cleanslate"],detectCmp:[{exists:".legalmonster-cleanslate"}],detectPopup:[{visible:".legalmonster-cleanslate #lm-cookie-wall-container",check:"any"}],optIn:[{waitForThenClick:"#lm-accept-all"}],optOut:[{waitForThenClick:"#lm-accept-necessary"}]},{name:"opera.com",vendorUrl:"https://unknown",cosmetic:!1,runContext:{main:!0,frame:!1},intermediate:!1,prehideSelectors:[],detectCmp:[{exists:"#cookie-consent .manage-cookies__btn"}],detectPopup:[{visible:"#cookie-consent .cookie-basic-consent__btn"}],optIn:[{waitForThenClick:"#cookie-consent .cookie-basic-consent__btn"}],optOut:[{waitForThenClick:"#cookie-consent .manage-cookies__btn"},{waitForThenClick:"#cookie-consent .active.marketing_option_switch.cookie-consent__switch",all:!0},{waitForThenClick:"#cookie-consent .cookie-selection__btn"}],test:[{eval:"EVAL_OPERA_0"}]},{name:"osano",prehideSelectors:[".osano-cm-window,.osano-cm-dialog"],detectCmp:[{exists:".osano-cm-window"}],detectPopup:[{visible:".osano-cm-dialog"}],optIn:[{click:".osano-cm-accept-all",optional:!0}],optOut:[{waitForThenClick:".osano-cm-denyAll"}]},{name:"otto.de",prehideSelectors:[".cookieBanner--visibility"],detectCmp:[{exists:".cookieBanner--visibility"}],detectPopup:[{visible:".cookieBanner__wrapper"}],optIn:[{click:".js_cookieBannerPermissionButton"}],optOut:[{click:".js_cookieBannerProhibitionButton"}]},{name:"ourworldindata",vendorUrl:"https://ourworldindata.org/",runContext:{urlPattern:"^https://ourworldindata\\.org/"},prehideSelectors:[".cookie-manager"],detectCmp:[{exists:".cookie-manager"}],detectPopup:[{visible:".cookie-manager .cookie-notice.open"}],optIn:[{waitForThenClick:".cookie-notice [data-test=accept]"}],optOut:[{waitForThenClick:".cookie-notice [data-test=reject]"}]},{name:"pabcogypsum",vendorUrl:"https://unknown",prehideSelectors:[".js-cookie-notice:has(#cookie_settings-form)"],detectCmp:[{exists:".js-cookie-notice #cookie_settings-form"}],detectPopup:[{visible:".js-cookie-notice #cookie_settings-form"}],optIn:[{waitForThenClick:".js-cookie-notice button[value=allow]"}],optOut:[{waitForThenClick:".js-cookie-notice button[value=disable]"}]},{name:"paypal-us",prehideSelectors:["#ccpaCookieContent_wrapper, article.ppvx_modal--overpanel"],detectCmp:[{exists:"#ccpaCookieBanner, .privacy-sheet-content"}],detectPopup:[{visible:"#ccpaCookieBanner, .privacy-sheet-content"}],optIn:[{click:"#acceptAllButton"}],optOut:[{if:{exists:"#bannerDeclineButton"},then:[{click:"#bannerDeclineButton"}],else:[{if:{exists:"a#manageCookiesLink"},then:[{click:"a#manageCookiesLink"}],else:[{waitForVisible:".privacy-sheet-content #formContent"},{click:"#formContent .cookiepref-11m2iee-checkbox_base input:checked",all:!0,optional:!0},{click:".cookieAction.saveCookie,.confirmCookie #submitCookiesBtn"}]}]}]},{name:"paypal.com",prehideSelectors:["#gdprCookieBanner"],detectCmp:[{exists:"#gdprCookieBanner"}],detectPopup:[{visible:"#gdprCookieContent_wrapper"}],optIn:[{click:"#acceptAllButton"}],optOut:[{wait:200},{click:".gdprCookieBanner_decline-button"}],test:[{wait:500},{eval:"EVAL_PAYPAL_0"}]},{name:"pinetools.com",cosmetic:!0,prehideSelectors:["#aviso_cookies"],detectCmp:[{exists:"#aviso_cookies"}],detectPopup:[{exists:".lang_en #aviso_cookies"}],optIn:[{click:"#aviso_cookies .a_boton_cerrar"}],optOut:[{hide:"#aviso_cookies"}]},{name:"pinterest-business",vendorUrl:"https://business.pinterest.com/",runContext:{urlPattern:"^https://.*\\.pinterest\\.com/"},prehideSelectors:[".BusinessCookieConsent"],detectCmp:[{exists:".BusinessCookieConsent"}],detectPopup:[{visible:".BusinessCookieConsent [data-id=cookie-consent-banner-buttons]"}],optIn:[{waitForThenClick:"[data-id=cookie-consent-banner-buttons] > div:nth-child(1) button"}],optOut:[{waitForThenClick:"[data-id=cookie-consent-banner-buttons] > div:nth-child(2) button"}]},{name:"pmc",cosmetic:!0,prehideSelectors:["#pmc-pp-tou--notice"],detectCmp:[{exists:"#pmc-pp-tou--notice"}],detectPopup:[{visible:"#pmc-pp-tou--notice"}],optIn:[{click:"span.pmc-pp-tou--notice-close-btn"}],optOut:[{hide:"#pmc-pp-tou--notice"}]},{name:"pornhub.com",runContext:{urlPattern:"^https://(www\\.)?pornhub\\.com/"},cosmetic:!1,prehideSelectors:["#cookieBanner #cookieBannerContent"],detectCmp:[{exists:"#cookieBanner #cookieBannerContent"}],detectPopup:[{visible:"#cookieBanner #cookieBannerContent"}],optIn:[{waitForThenClick:"#cookieBanner [data-label=accept_all]"}],optOut:[{waitForThenClick:"#cookieBanner [data-label=accept_essential]"}]},{name:"pornpics.com",cosmetic:!0,prehideSelectors:["#cookie-contract"],detectCmp:[{exists:"#cookie-contract"}],detectPopup:[{visible:"#cookie-contract"}],optIn:[{click:"#cookie-contract .icon-cross"}],optOut:[{hide:"#cookie-contract"}]},{name:"PrimeBox CookieBar",prehideSelectors:["#cookie-bar"],detectCmp:[{exists:"#cookie-bar .cb-enable,#cookie-bar .cb-disable,#cookie-bar .cb-policy"}],detectPopup:[{visible:"#cookie-bar .cb-enable,#cookie-bar .cb-disable,#cookie-bar .cb-policy",check:"any"}],optIn:[{waitForThenClick:"#cookie-bar .cb-enable"}],optOut:[{click:"#cookie-bar .cb-disable",optional:!0},{hide:"#cookie-bar"}],test:[{eval:"EVAL_PRIMEBOX_0"}]},{name:"privacymanager.io",prehideSelectors:["#gdpr-consent-tool-wrapper",'iframe[src^="https://cmp-consent-tool.privacymanager.io"]'],runContext:{urlPattern:"^https://cmp-consent-tool\\.privacymanager\\.io/",main:!1,frame:!0},detectCmp:[{exists:"button#save"}],detectPopup:[{visible:"button#save"}],optIn:[{click:"button#save"}],optOut:[{if:{exists:"#denyAll"},then:[{click:"#denyAll"},{waitForThenClick:".okButton"}],else:[{waitForThenClick:"#manageSettings"},{waitFor:".purposes-overview-list"},{waitFor:"button#saveAndExit"},{click:"span[role=checkbox][aria-checked=true]",all:!0,optional:!0},{click:"button#saveAndExit"}]}]},{name:"productz.com",vendorUrl:"https://productz.com/",runContext:{urlPattern:"^https://productz\\.com/"},prehideSelectors:[],detectCmp:[{exists:".c-modal.is-active"}],detectPopup:[{visible:".c-modal.is-active"}],optIn:[{waitForThenClick:".c-modal.is-active .is-accept"}],optOut:[{waitForThenClick:".c-modal.is-active .is-dismiss"}]},{name:"pubtech",prehideSelectors:["#pubtech-cmp"],detectCmp:[{exists:"#pubtech-cmp"}],detectPopup:[{visible:"#pubtech-cmp #pt-actions"}],optIn:[{if:{exists:"#pt-accept-all"},then:[{click:"#pubtech-cmp #pt-actions #pt-accept-all"}],else:[{click:"#pubtech-cmp #pt-actions button:nth-of-type(2)"}]}],optOut:[{click:"#pubtech-cmp #pt-close"}],test:[{eval:"EVAL_PUBTECH_0"}]},{name:"quantcast",prehideSelectors:["#qc-cmp2-main,#qc-cmp2-container"],detectCmp:[{exists:"#qc-cmp2-container"}],detectPopup:[{visible:"#qc-cmp2-ui"}],optOut:[{waitFor:'.qc-cmp2-summary-buttons > button[mode="secondary"]',timeout:2e3},{if:{exists:'.qc-cmp2-summary-buttons > button[mode="secondary"]:nth-of-type(2)'},then:[{click:'.qc-cmp2-summary-buttons > button[mode="secondary"]:nth-of-type(2)'}],else:[{click:'.qc-cmp2-summary-buttons > button[mode="secondary"]:nth-of-type(1)'},{waitFor:"#qc-cmp2-ui"},{click:'.qc-cmp2-toggle-switch > button[aria-checked="true"]',all:!0,optional:!0},{click:'.qc-cmp2-main button[aria-label="REJECT ALL"]',optional:!0},{waitForThenClick:'.qc-cmp2-main button[aria-label="SAVE & EXIT"],.qc-cmp2-buttons-desktop > button[mode="primary"]',timeout:5e3}]}],optIn:[{click:'.qc-cmp2-summary-buttons > button[mode="primary"]'}]},{name:"reddit.com",runContext:{urlPattern:"^https://www\\.reddit\\.com/"},prehideSelectors:["[bundlename=reddit_cookie_banner]"],detectCmp:[{exists:"reddit-cookie-banner"}],detectPopup:[{visible:"reddit-cookie-banner"}],optIn:[{waitForThenClick:["reddit-cookie-banner","#accept-all-cookies-button > button"]}],optOut:[{waitForThenClick:["reddit-cookie-banner","#reject-nonessential-cookies-button > button"]}],test:[{eval:"EVAL_REDDIT_0"}]},{name:"roblox",vendorUrl:"https://roblox.com",cosmetic:!1,runContext:{main:!0,frame:!1,urlPattern:"^https://(www\\.)?roblox\\.com/"},prehideSelectors:[],detectCmp:[{exists:".cookie-banner-wrapper"}],detectPopup:[{visible:".cookie-banner-wrapper .cookie-banner"}],optIn:[{waitForThenClick:".cookie-banner-wrapper button.btn-cta-lg"}],optOut:[{waitForThenClick:".cookie-banner-wrapper button.btn-secondary-lg"}],test:[{eval:"EVAL_ROBLOX_TEST"}]},{name:"rog-forum.asus.com",runContext:{urlPattern:"^https://rog-forum\\.asus\\.com/"},prehideSelectors:["#cookie-policy-info"],detectCmp:[{exists:"#cookie-policy-info"}],detectPopup:[{visible:"#cookie-policy-info"}],optIn:[{click:'div.cookie-btn-box > div[aria-label="Accept"]'}],optOut:[{click:'div.cookie-btn-box > div[aria-label="Reject"]'},{waitForThenClick:'.cookie-policy-lightbox-bottom > div[aria-label="Save Settings"]'}]},{name:"roofingmegastore.co.uk",runContext:{urlPattern:"^https://(www\\.)?roofingmegastore\\.co\\.uk"},prehideSelectors:["#m-cookienotice"],detectCmp:[{exists:"#m-cookienotice"}],detectPopup:[{visible:"#m-cookienotice"}],optIn:[{click:"#accept-cookies"}],optOut:[{click:"#manage-cookies"},{waitForThenClick:"#accept-selected"}]},{name:"samsung.com",runContext:{urlPattern:"^https://www\\.samsung\\.com/"},cosmetic:!0,prehideSelectors:["div.cookie-bar"],detectCmp:[{exists:"div.cookie-bar"}],detectPopup:[{visible:"div.cookie-bar"}],optIn:[{click:"div.cookie-bar__manage > a"}],optOut:[{hide:"div.cookie-bar"}]},{name:"setapp.com",vendorUrl:"https://setapp.com/",cosmetic:!0,runContext:{urlPattern:"^https://setapp\\.com/"},prehideSelectors:[],detectCmp:[{exists:".cookie-banner.js-cookie-banner"}],detectPopup:[{visible:".cookie-banner.js-cookie-banner"}],optIn:[{waitForThenClick:".cookie-banner.js-cookie-banner button"}],optOut:[{hide:".cookie-banner.js-cookie-banner"}]},{name:"sibbo",prehideSelectors:["sibbo-cmp-layout"],detectCmp:[{exists:"sibbo-cmp-layout"}],detectPopup:[{visible:"#rejectAllMain"}],optIn:[{click:"#acceptAllMain"}],optOut:[{click:"#rejectAllMain"}]},{name:"similarweb.com",cosmetic:!0,prehideSelectors:[".app-cookies-notification"],detectCmp:[{exists:".app-cookies-notification"}],detectPopup:[{exists:".app-layout .app-cookies-notification"}],optIn:[{click:"button.app-cookies-notification__dismiss"}],optOut:[{hide:".app-layout .app-cookies-notification"}]},{name:"Sirdata",cosmetic:!1,prehideSelectors:["#sd-cmp"],detectCmp:[{exists:"#sd-cmp"}],detectPopup:[{visible:"#sd-cmp"}],optIn:[{waitForThenClick:"#sd-cmp .sd-cmp-3cRQ2"}],optOut:[{waitForThenClick:["#sd-cmp","xpath///span[contains(., 'Do not accept') or contains(., 'Acceptera inte') or contains(., 'No aceptar') or contains(., 'Ikke acceptere') or contains(., 'Nicht akzeptieren') or contains(., 'Не приемам') or contains(., 'Να μην γίνει αποδοχή') or contains(., 'Niet accepteren') or contains(., 'Nepřijímat') or contains(., 'Nie akceptuj') or contains(., 'Nu acceptați') or contains(., 'Não aceitar') or contains(., 'Continuer sans accepter') or contains(., 'Non accettare') or contains(., 'Nem fogad el')]"]}]},{name:"skyscanner",vendorUrl:"https://skyscanner.com",cosmetic:!1,runContext:{main:!0,frame:!1,urlPattern:"^https://(www\\.)?skyscanner[\\.a-z]+/"},prehideSelectors:[".cookie-banner-wrapper"],detectCmp:[{exists:"#cookieBannerContent"}],detectPopup:[{visible:"#cookieBannerContent"}],optIn:[{waitForThenClick:"[data-tracking-element-id=cookie_banner_accept_all]"}],optOut:[{waitForThenClick:"[data-tracking-element-id=cookie_banner_essential_only]"},{waitForVisible:"#cookieBannerContent",check:"none"}],test:[{eval:"EVAL_SKYSCANNER_TEST"}]},{name:"snigel",detectCmp:[{exists:".snigel-cmp-framework"}],detectPopup:[{visible:".snigel-cmp-framework"}],optOut:[{click:"#sn-b-custom"},{click:"#sn-b-save"}],test:[{eval:"EVAL_SNIGEL_0"}],optIn:[{click:".snigel-cmp-framework #accept-choices"}]},{name:"steampowered.com",detectCmp:[{exists:".cookiepreferences_popup"},{visible:".cookiepreferences_popup"}],detectPopup:[{visible:".cookiepreferences_popup"}],optOut:[{click:"#rejectAllButton"}],optIn:[{click:"#acceptAllButton"}],test:[{wait:1e3},{eval:"EVAL_STEAMPOWERED_0"}]},{name:"strato.de",prehideSelectors:[".consent__wrapper"],runContext:{urlPattern:"^https://www\\.strato\\.de/"},detectCmp:[{exists:".consent"}],detectPopup:[{visible:".consent"}],optIn:[{click:"button.consentAgree"}],optOut:[{click:"button.consentSettings"},{waitForThenClick:"button#consentSubmit"}]},{name:"svt.se",vendorUrl:"https://www.svt.se/",runContext:{urlPattern:"^https://www\\.svt\\.se/"},prehideSelectors:["[class*=CookieConsent__root___]"],detectCmp:[{exists:"[class*=CookieConsent__root___]"}],detectPopup:[{visible:"[class*=CookieConsent__modal___]"}],optIn:[{waitForThenClick:"[class*=CookieConsent__modal___] > div > button[class*=primary]"}],optOut:[{waitForThenClick:"[class*=CookieConsent__modal___] > div > button[class*=secondary]:nth-child(2)"}],test:[{eval:"EVAL_SVT_TEST"}]},{name:"takealot.com",cosmetic:!0,prehideSelectors:['div[class^="cookies-banner-module_"]'],detectCmp:[{exists:'div[class^="cookies-banner-module_cookie-banner_"]'}],detectPopup:[{exists:'div[class^="cookies-banner-module_cookie-banner_"]'}],optIn:[{click:'button[class*="cookies-banner-module_dismiss-button_"]'}],optOut:[{hide:'div[class^="cookies-banner-module_"]'},{if:{exists:'div[class^="cookies-banner-module_small-cookie-banner_"]'},then:[{eval:"EVAL_TAKEALOT_0"}],else:[]}]},{name:"tarteaucitron.js",prehideSelectors:["#tarteaucitronRoot"],detectCmp:[{exists:"#tarteaucitronRoot"}],detectPopup:[{visible:"#tarteaucitronRoot #tarteaucitronAlertBig",check:"any"}],optIn:[{eval:"EVAL_TARTEAUCITRON_1"}],optOut:[{eval:"EVAL_TARTEAUCITRON_0"}],test:[{eval:"EVAL_TARTEAUCITRON_2",comment:"sometimes there are required categories, so we check that at least something is false"}]},{name:"taunton",vendorUrl:"https://www.taunton.com/",prehideSelectors:["#taunton-user-consent__overlay"],detectCmp:[{exists:"#taunton-user-consent__overlay"}],detectPopup:[{exists:"#taunton-user-consent__overlay:not([aria-hidden=true])"}],optIn:[{click:"#taunton-user-consent__toolbar input[type=checkbox]:not(:checked)"},{click:"#taunton-user-consent__toolbar button[type=submit]"}],optOut:[{click:"#taunton-user-consent__toolbar input[type=checkbox]:checked",optional:!0,all:!0},{click:"#taunton-user-consent__toolbar button[type=submit]"}],test:[{eval:"EVAL_TAUNTON_TEST"}]},{name:"Tealium",prehideSelectors:["#__tealiumGDPRecModal,#__tealiumGDPRcpPrefs,#__tealiumImplicitmodal,#consent-layer"],detectCmp:[{exists:"#__tealiumGDPRecModal *,#__tealiumGDPRcpPrefs *,#__tealiumImplicitmodal *"},{eval:"EVAL_TEALIUM_0"}],detectPopup:[{visible:"#__tealiumGDPRecModal *,#__tealiumGDPRcpPrefs *,#__tealiumImplicitmodal *",check:"any"}],optOut:[{eval:"EVAL_TEALIUM_1"},{eval:"EVAL_TEALIUM_DONOTSELL"},{hide:"#__tealiumGDPRecModal,#__tealiumGDPRcpPrefs,#__tealiumImplicitmodal"},{waitForThenClick:"#cm-acceptNone,.js-accept-essential-cookies,#continueWithoutAccepting",timeout:1e3,optional:!0}],optIn:[{hide:"#__tealiumGDPRecModal,#__tealiumGDPRcpPrefs"},{eval:"EVAL_TEALIUM_2"}],test:[{eval:"EVAL_TEALIUM_3"},{eval:"EVAL_TEALIUM_DONOTSELL_CHECK"},{visible:"#__tealiumGDPRecModal,#__tealiumGDPRcpPrefs",check:"none"}]},{name:"temu",vendorUrl:"https://temu.com",runContext:{urlPattern:"^https://([a-z0-9-]+\\.)?temu\\.com/"},prehideSelectors:[],detectCmp:[{exists:'div > div > div > div > span[href*="/cookie-and-similar-technologies-policy.html"]'}],detectPopup:[{visible:'div > div > div > div > span[href*="/cookie-and-similar-technologies-policy.html"]'}],optIn:[{waitForThenClick:'div > div > div:has(> div > span[href*="/cookie-and-similar-technologies-policy.html"]) > [role=button]:nth-child(3)'}],optOut:[{if:{exists:"xpath///span[contains(., 'Alle afwijzen') or contains(., 'Reject all') or contains(., 'Tümünü reddet') or contains(., 'Odrzuć wszystko')]"},then:[{waitForThenClick:"xpath///span[contains(., 'Alle afwijzen') or contains(., 'Reject all') or contains(., 'Tümünü reddet') or contains(., 'Odrzuć wszystko')]"}],else:[{waitForThenClick:'div > div > div:has(> div > span[href*="/cookie-and-similar-technologies-policy.html"]) > [role=button]:nth-child(2)'}]}]},{name:"Termly",prehideSelectors:["#termly-code-snippet-support"],detectCmp:[{exists:"#termly-code-snippet-support"}],detectPopup:[{visible:"#termly-code-snippet-support div"}],optIn:[{waitForThenClick:'[data-tid="banner-accept"]'}],optOut:[{if:{exists:'[data-tid="banner-decline"]'},then:[{click:'[data-tid="banner-decline"]'}],else:[{click:".t-preference-button"},{wait:500},{if:{exists:".t-declineAllButton"},then:[{click:".t-declineAllButton"}],else:[{waitForThenClick:".t-preference-modal input[type=checkbox][checked]:not([disabled])",all:!0},{waitForThenClick:".t-saveButton"}]}]}]},{name:"termsfeed",vendorUrl:"https://termsfeed.com",comment:"v4.x.x",prehideSelectors:[".termsfeed-com---nb"],detectCmp:[{exists:".termsfeed-com---nb"}],detectPopup:[{visible:".termsfeed-com---nb"}],optIn:[{waitForThenClick:".cc-nb-okagree"}],optOut:[{waitForThenClick:".cc-nb-reject"}]},{name:"termsfeed3",vendorUrl:"https://termsfeed.com",comment:"v3.x.x",prehideSelectors:[".cc_dialog.cc_css_reboot,.cc_overlay_lock"],detectCmp:[{exists:".cc_dialog.cc_css_reboot"}],detectPopup:[{visible:".cc_dialog.cc_css_reboot"}],optIn:[{waitForThenClick:".cc_dialog.cc_css_reboot .cc_b_ok"}],optOut:[{if:{exists:".cc_dialog.cc_css_reboot .cc_b_cp"},then:[{click:".cc_dialog.cc_css_reboot .cc_b_cp"},{waitForVisible:".cookie-consent-preferences-dialog .cc_cp_f_save button"},{waitForThenClick:".cookie-consent-preferences-dialog .cc_cp_f_save button"}],else:[{hide:".cc_dialog.cc_css_reboot,.cc_overlay_lock"}]}]},{name:"tesco",vendorUrl:"https://www.tesco.com",cosmetic:!1,runContext:{urlPattern:"^https://(www\\.)?tesco\\.com/"},prehideSelectors:["[class*=CookieBanner__Sizer]"],detectCmp:[{exists:"[aria-label=consent-banner]"}],detectPopup:[{visible:"[aria-label=consent-banner]"}],optIn:[{wait:1e3},{waitForThenClick:"xpath///button[contains(., 'Accept all')]"}],optOut:[{wait:1e3},{waitForThenClick:"xpath///button[contains(., 'Reject all')]"}]},{name:"tesla",vendorUrl:"https://tesla.com/",runContext:{main:!0,frame:!1,urlPattern:"^https://(www\\.)?tesla\\.com/"},prehideSelectors:[],detectCmp:[{exists:"#cookie_banner"}],detectPopup:[{visible:"#cookie_banner"}],optIn:[{waitForThenClick:"#tsla-accept-cookie"}],optOut:[{waitForThenClick:"#tsla-reject-cookie"}],test:[{eval:"EVAL_TESLA_TEST"}]},{name:"Test page cosmetic CMP",cosmetic:!0,prehideSelectors:["#privacy-test-page-cmp-test-prehide"],detectCmp:[{exists:"#privacy-test-page-cmp-test-banner"}],detectPopup:[{visible:"#privacy-test-page-cmp-test-banner"}],optIn:[{waitFor:"#accept-all"},{click:"#accept-all"}],optOut:[{hide:"#privacy-test-page-cmp-test-banner"}],test:[{wait:500},{eval:"EVAL_TESTCMP_COSMETIC_0"}]},{name:"Test page CMP",prehideSelectors:["#reject-all"],detectCmp:[{exists:"#privacy-test-page-cmp-test"}],detectPopup:[{visible:"#privacy-test-page-cmp-test"}],optIn:[{waitFor:"#accept-all"},{click:"#accept-all"}],optOut:[{waitFor:"#reject-all"},{eval:"EVAL_TESTCMP_STEP"},{click:"#reject-all"}],test:[{eval:"EVAL_TESTCMP_0"}]},{name:"thalia.de",prehideSelectors:[".consent-banner-box"],detectCmp:[{exists:"consent-banner[component=consent-banner]"}],detectPopup:[{visible:".consent-banner-box"}],optIn:[{click:".button-zustimmen"}],optOut:[{click:"button[data-consent=disagree]"}]},{name:"thefreedictionary.com",prehideSelectors:["#cmpBanner"],detectCmp:[{exists:"#cmpBanner"}],detectPopup:[{visible:"#cmpBanner"}],optIn:[{eval:"EVAL_THEFREEDICTIONARY_1"}],optOut:[{eval:"EVAL_THEFREEDICTIONARY_0"}]},{name:"theverge",runContext:{frame:!1,main:!0,urlPattern:"^https://(www)?\\.theverge\\.com"},intermediate:!1,prehideSelectors:[".duet--cta--cookie-banner"],detectCmp:[{exists:".duet--cta--cookie-banner"}],detectPopup:[{visible:".duet--cta--cookie-banner"}],optIn:[{click:".duet--cta--cookie-banner button.tracking-12",all:!1}],optOut:[{click:".duet--cta--cookie-banner button.tracking-12 > span"}],test:[{eval:"EVAL_THEVERGE_0"}]},{name:"tidbits-com",cosmetic:!0,prehideSelectors:["#eu_cookie_law_widget-2"],detectCmp:[{exists:"#eu_cookie_law_widget-2"}],detectPopup:[{visible:"#eu_cookie_law_widget-2"}],optIn:[{click:"#eu-cookie-law form > input.accept"}],optOut:[{hide:"#eu_cookie_law_widget-2"}]},{name:"tractor-supply",runContext:{urlPattern:"^https://www\\.tractorsupply\\.com/"},cosmetic:!0,prehideSelectors:[".tsc-cookie-banner"],detectCmp:[{exists:".tsc-cookie-banner"}],detectPopup:[{visible:".tsc-cookie-banner"}],optIn:[{click:"#cookie-banner-cancel"}],optOut:[{hide:".tsc-cookie-banner"}]},{name:"trader-joes-com",cosmetic:!0,prehideSelectors:['div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]'],detectCmp:[{exists:'div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]'}],detectPopup:[{visible:'div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]'}],optIn:[{click:'div[class^="CookiesAlert_cookiesAlert__container__"] button'}],optOut:[{hide:'div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]'}]},{name:"transcend",vendorUrl:"https://unknown",cosmetic:!0,prehideSelectors:["#transcend-consent-manager"],detectCmp:[{exists:"#transcend-consent-manager"}],detectPopup:[{visible:"#transcend-consent-manager"}],optIn:[{waitForThenClick:["#transcend-consent-manager","#consentManagerMainDialog .inner-container button"]}],optOut:[{hide:"#transcend-consent-manager"}]},{name:"transip-nl",runContext:{urlPattern:"^https://www\\.transip\\.nl/"},prehideSelectors:["#consent-modal"],detectCmp:[{any:[{exists:"#consent-modal"},{exists:"#privacy-settings-content"}]}],detectPopup:[{any:[{visible:"#consent-modal"},{visible:"#privacy-settings-content"}]}],optIn:[{click:'button[type="submit"]'}],optOut:[{if:{exists:"#privacy-settings-content"},then:[{click:'button[type="submit"]'}],else:[{click:"div.one-modal__action-footer-column--secondary > a"}]}]},{name:"tropicfeel-com",prehideSelectors:["#shopify-section-cookies-controller"],detectCmp:[{exists:"#shopify-section-cookies-controller"}],detectPopup:[{visible:"#shopify-section-cookies-controller #cookies-controller-main-pane",check:"any"}],optIn:[{waitForThenClick:"#cookies-controller-main-pane form[data-form-allow-all] button"}],optOut:[{click:"#cookies-controller-main-pane a[data-tab-target=manage-cookies]"},{waitFor:"#manage-cookies-pane.active"},{click:"#manage-cookies-pane.active input[type=checkbox][checked]:not([disabled])",all:!0},{click:"#manage-cookies-pane.active button[type=submit]"}],test:[]},{name:"true-car",runContext:{urlPattern:"^https://www\\.truecar\\.com/"},cosmetic:!0,prehideSelectors:[['div[aria-labelledby="cookie-banner-heading"]']],detectCmp:[{exists:'div[aria-labelledby="cookie-banner-heading"]'}],detectPopup:[{visible:'div[aria-labelledby="cookie-banner-heading"]'}],optIn:[{click:'div[aria-labelledby="cookie-banner-heading"] > button[aria-label="Close"]'}],optOut:[{hide:'div[aria-labelledby="cookie-banner-heading"]'}]},{name:"truyo",prehideSelectors:["#truyo-consent-module"],detectCmp:[{exists:"#truyo-cookieBarContent"}],detectPopup:[{visible:"#truyo-consent-module"}],optIn:[{click:"button#acceptAllCookieButton"}],optOut:[{click:"button#declineAllCookieButton"}]},{name:"twcc",vendorUrl:"https://unknown",cosmetic:!1,runContext:{main:!0,frame:!1,urlPattern:""},prehideSelectors:["#twcc__mechanism"],detectCmp:[{exists:"#twcc__mechanism .twcc__notice"}],detectPopup:[{visible:"#twcc__mechanism .twcc__notice"}],optIn:[{waitForThenClick:"#twcc__accept-button"}],optOut:[{waitForThenClick:"#twcc__decline-button"}],test:[{eval:"EVAL_TWCC_TEST"}]},{name:"twitch-mobile",vendorUrl:"https://m.twitch.tv/",cosmetic:!0,runContext:{urlPattern:"^https?://m\\.twitch\\.tv"},prehideSelectors:[],detectCmp:[{exists:'.ReactModal__Overlay [href="https://www.twitch.tv/p/cookie-policy"]'}],detectPopup:[{visible:'.ReactModal__Overlay [href="https://www.twitch.tv/p/cookie-policy"]'}],optIn:[{waitForThenClick:'.ReactModal__Overlay:has([href="https://www.twitch.tv/p/cookie-policy"]) button'}],optOut:[{hide:'.ReactModal__Overlay:has([href="https://www.twitch.tv/p/cookie-policy"])'}]},{name:"twitch.tv",runContext:{urlPattern:"^https?://(www\\.)?twitch\\.tv"},prehideSelectors:["div:has(> .consent-banner .consent-banner__content--gdpr-v2),.ReactModalPortal:has([data-a-target=consent-modal-save])"],detectCmp:[{exists:".consent-banner .consent-banner__content--gdpr-v2"}],detectPopup:[{visible:".consent-banner .consent-banner__content--gdpr-v2"}],optIn:[{click:'button[data-a-target="consent-banner-accept"]'}],optOut:[{hide:"div:has(> .consent-banner .consent-banner__content--gdpr-v2)"},{click:'button[data-a-target="consent-banner-manage-preferences"]'},{waitFor:"input[type=checkbox][data-a-target=tw-checkbox]"},{click:"input[type=checkbox][data-a-target=tw-checkbox][checked]:not([disabled])",all:!0,optional:!0},{waitForThenClick:"[data-a-target=consent-modal-save]"},{waitForVisible:".ReactModalPortal:has([data-a-target=consent-modal-save])",check:"none"}]},{name:"twitter",runContext:{urlPattern:"^https://([a-z0-9-]+\\.)?(twitter|x)\\.com/"},prehideSelectors:['[data-testid="BottomBar"]'],detectCmp:[{exists:'[data-testid="BottomBar"] div'}],detectPopup:[{visible:'[data-testid="BottomBar"] div'}],optIn:[{waitForThenClick:'[data-testid="BottomBar"] > div:has(>div:first-child>div:last-child>button[role=button]>span) > div:last-child > button[role=button]:first-child'}],optOut:[{waitForThenClick:'[data-testid="BottomBar"] > div:has(>div:first-child>div:last-child>button[role=button]>span) > div:last-child > button[role=button]:last-child'}],TODOtest:[{eval:"EVAL_document.cookie.includes('d_prefs=MjoxLGNvbnNlbnRfdmVyc2lvbjoy')"}]},{name:"ubuntu.com",prehideSelectors:["dialog.cookie-policy"],detectCmp:[{any:[{exists:"dialog.cookie-policy header"},{exists:'xpath///*[@id="modal"]/div/header'}]}],detectPopup:[{any:[{visible:"dialog header"},{visible:'xpath///*[@id="modal"]/div/header'}]}],optIn:[{any:[{waitForThenClick:"#cookie-policy-button-accept"},{waitForThenClick:'xpath///*[@id="cookie-policy-button-accept"]'}]}],optOut:[{any:[{waitForThenClick:"button.js-manage"},{waitForThenClick:'xpath///*[@id="cookie-policy-content"]/p[4]/button[2]'}]},{waitForThenClick:"dialog.cookie-policy .p-switch__input:checked",optional:!0,all:!0,timeout:500},{any:[{waitForThenClick:"dialog.cookie-policy .js-save-preferences"},{waitForThenClick:'xpath///*[@id="modal"]/div/button'}]}],test:[{eval:"EVAL_UBUNTU_COM_0"}]},{name:"UK Cookie Consent",prehideSelectors:["#catapult-cookie-bar"],cosmetic:!0,detectCmp:[{exists:"#catapult-cookie-bar"}],detectPopup:[{exists:".has-cookie-bar #catapult-cookie-bar"}],optIn:[{click:"#catapultCookie"}],optOut:[{hide:"#catapult-cookie-bar"}],test:[{eval:"EVAL_UK_COOKIE_CONSENT_0"}]},{name:"urbanarmorgear-com",cosmetic:!0,prehideSelectors:['div[class^="Layout__CookieBannerContainer-"]'],detectCmp:[{exists:'div[class^="Layout__CookieBannerContainer-"]'}],detectPopup:[{visible:'div[class^="Layout__CookieBannerContainer-"]'}],optIn:[{click:'button[class^="CookieBanner__AcceptButton"]'}],optOut:[{hide:'div[class^="Layout__CookieBannerContainer-"]'}]},{name:"usercentrics-api",detectCmp:[{exists:"#usercentrics-root,#usercentrics-cmp-ui"}],detectPopup:[{eval:"EVAL_USERCENTRICS_API_0"},{if:{exists:"#usercentrics-cmp-ui"},then:[{waitForVisible:"#usercentrics-cmp-ui",timeout:2e3}],else:[{exists:["#usercentrics-root","[data-testid=uc-container]"]},{waitForVisible:"#usercentrics-root",timeout:2e3}]}],optIn:[{eval:"EVAL_USERCENTRICS_API_3"},{eval:"EVAL_USERCENTRICS_API_1"},{eval:"EVAL_USERCENTRICS_API_5"}],optOut:[{eval:"EVAL_USERCENTRICS_API_1"},{eval:"EVAL_USERCENTRICS_API_2"}],test:[{eval:"EVAL_USERCENTRICS_API_6"}]},{name:"usercentrics-button",detectCmp:[{exists:"#usercentrics-button"}],detectPopup:[{visible:"#usercentrics-button #uc-btn-accept-banner"}],optIn:[{click:"#usercentrics-button #uc-btn-accept-banner"}],optOut:[{click:"#usercentrics-button #uc-btn-deny-banner"}],test:[{eval:"EVAL_USERCENTRICS_BUTTON_0"}]},{name:"uswitch.com",runContext:{main:!0,frame:!1,urlPattern:"^https://(www\\.)?uswitch\\.com/"},prehideSelectors:[".ucb"],detectCmp:[{exists:".ucb-banner"}],detectPopup:[{visible:".ucb-banner"}],optIn:[{waitForThenClick:".ucb-banner .ucb-btn-accept"}],optOut:[{waitForThenClick:".ucb-banner .ucb-btn-save"}]},{name:"vodafone.de",runContext:{urlPattern:"^https://www\\.vodafone\\.de/"},prehideSelectors:[".dip-consent,.dip-consent-container"],detectCmp:[{exists:".dip-consent-container"}],detectPopup:[{visible:".dip-consent-content"}],optOut:[{click:'.dip-consent-btn[tabindex="2"]'}],optIn:[{click:'.dip-consent-btn[tabindex="1"]'}]},{name:"waitrose.com",prehideSelectors:["div[aria-labelledby=CookieAlertModalHeading]","section[data-test=initial-waitrose-cookie-consent-banner]","section[data-test=cookie-consent-modal]"],detectCmp:[{exists:"section[data-test=initial-waitrose-cookie-consent-banner]"}],detectPopup:[{visible:"section[data-test=initial-waitrose-cookie-consent-banner]"}],optIn:[{click:"button[data-test=accept-all]"}],optOut:[{click:"button[data-test=manage-cookies]"},{wait:200},{eval:"EVAL_WAITROSE_0"},{click:"button[data-test=submit]"}],test:[{eval:"EVAL_WAITROSE_1"}]},{name:"webflow",vendorUrl:"https://webflow.com/",prehideSelectors:[".fs-cc-components"],detectCmp:[{exists:".fs-cc-components"}],detectPopup:[{visible:".fs-cc-components"},{visible:"[fs-cc=banner]"}],optIn:[{wait:500},{waitForThenClick:"[fs-cc=banner] [fs-cc=allow]"}],optOut:[{wait:500},{waitForThenClick:"[fs-cc=banner] [fs-cc=deny]"}]},{name:"wetransfer.com",detectCmp:[{exists:".welcome__cookie-notice"}],detectPopup:[{visible:".welcome__cookie-notice"}],optIn:[{click:".welcome__button--accept"}],optOut:[{click:".welcome__button--decline"}]},{name:"whitepages.com",runContext:{urlPattern:"^https://www\\.whitepages\\.com/"},cosmetic:!0,prehideSelectors:[".cookie-wrapper, .cookie-overlay"],detectCmp:[{exists:".cookie-wrapper"}],detectPopup:[{visible:".cookie-overlay"}],optIn:[{click:'button[aria-label="Got it"]'}],optOut:[{hide:".cookie-wrapper"}]},{name:"wolframalpha",vendorUrl:"https://www.wolframalpha.com",prehideSelectors:[],cosmetic:!0,runContext:{urlPattern:"^https://www\\.wolframalpha\\.com/"},detectCmp:[{exists:"section._a_yb"}],detectPopup:[{visible:"section._a_yb"}],optIn:[{waitForThenClick:"section._a_yb button"}],optOut:[{hide:"section._a_yb"}]},{name:"woo-commerce-com",prehideSelectors:[".wccom-comp-privacy-banner .wccom-privacy-banner"],detectCmp:[{exists:".wccom-comp-privacy-banner .wccom-privacy-banner"}],detectPopup:[{exists:".wccom-comp-privacy-banner .wccom-privacy-banner"}],optIn:[{click:".wccom-privacy-banner__content-buttons button.is-primary"}],optOut:[{click:".wccom-privacy-banner__content-buttons button.is-secondary"},{waitForThenClick:"input[type=checkbox][checked]:not([disabled])",all:!0},{click:"div.wccom-modal__footer > button"}]},{name:"WP Cookie Notice for GDPR",vendorUrl:"https://wordpress.org/plugins/gdpr-cookie-consent/",prehideSelectors:["#gdpr-cookie-consent-bar"],detectCmp:[{exists:"#gdpr-cookie-consent-bar"}],detectPopup:[{visible:"#gdpr-cookie-consent-bar"}],optIn:[{waitForThenClick:"#gdpr-cookie-consent-bar #cookie_action_accept"}],optOut:[{waitForThenClick:"#gdpr-cookie-consent-bar #cookie_action_reject"}],test:[{eval:"EVAL_WP_COOKIE_NOTICE_0"}]},{name:"wpcc",cosmetic:!0,prehideSelectors:[".wpcc-container"],detectCmp:[{exists:".wpcc-container"}],detectPopup:[{exists:".wpcc-container .wpcc-message"}],optIn:[{click:".wpcc-compliance .wpcc-btn"}],optOut:[{hide:".wpcc-container"}]},{name:"xe.com",vendorUrl:"https://www.xe.com/",runContext:{urlPattern:"^https://www\\.xe\\.com/"},prehideSelectors:["[class*=ConsentBanner]"],detectCmp:[{exists:"[class*=ConsentBanner]"}],detectPopup:[{visible:"[class*=ConsentBanner]"}],optIn:[{waitForThenClick:"[class*=ConsentBanner] .egnScw"}],optOut:[{wait:1e3},{waitForThenClick:"[class*=ConsentBanner] .frDWEu"},{waitForThenClick:"[class*=ConsentBanner] .hXIpFU"}],test:[{eval:"EVAL_XE_TEST"}]},{name:"xhamster-eu",prehideSelectors:[".cookies-modal"],detectCmp:[{exists:".cookies-modal"}],detectPopup:[{exists:".cookies-modal"}],optIn:[{click:"button.cmd-button-accept-all"}],optOut:[{click:"button.cmd-button-reject-all"}]},{name:"xhamster-us",runContext:{urlPattern:"^https://(www\\.)?xhamster\\d?\\.com"},cosmetic:!0,prehideSelectors:[".cookie-announce"],detectCmp:[{exists:".cookie-announce"}],detectPopup:[{visible:".cookie-announce .announce-text"}],optIn:[{click:".cookie-announce button.xh-button"}],optOut:[{hide:".cookie-announce"}]},{name:"xing.com",detectCmp:[{exists:"div[class^=cookie-consent-CookieConsent]"}],detectPopup:[{exists:"div[class^=cookie-consent-CookieConsent]"}],optIn:[{click:"#consent-accept-button"}],optOut:[{click:"#consent-settings-button"},{click:".consent-banner-button-accept-overlay"}],test:[{eval:"EVAL_XING_0"}]},{name:"xnxx-com",cosmetic:!0,prehideSelectors:["#cookies-use-alert"],detectCmp:[{exists:"#cookies-use-alert"}],detectPopup:[{visible:"#cookies-use-alert"}],optIn:[{click:"#cookies-use-alert .close"}],optOut:[{hide:"#cookies-use-alert"}]},{name:"xvideos",vendorUrl:"https://xvideos.com",runContext:{urlPattern:"^https://[^/]*xvideos\\.com/"},prehideSelectors:[],detectCmp:[{exists:".disclaimer-opened #disclaimer-cookies"}],detectPopup:[{visible:".disclaimer-opened #disclaimer-cookies"}],optIn:[{waitForThenClick:"#disclaimer-accept_cookies"}],optOut:[{waitForThenClick:"#disclaimer-reject_cookies"}]},{name:"Yahoo",runContext:{urlPattern:"^https://consent\\.yahoo\\.com/v2/"},prehideSelectors:["#reject-all"],detectCmp:[{exists:"#consent-page"}],detectPopup:[{visible:"#consent-page"}],optIn:[{waitForThenClick:"#consent-page button[value=agree]"}],optOut:[{waitForThenClick:"#consent-page button[value=reject]"}]},{name:"youporn.com",cosmetic:!0,prehideSelectors:[".euCookieModal, #js_euCookieModal"],detectCmp:[{exists:".euCookieModal"}],detectPopup:[{exists:".euCookieModal, #js_euCookieModal"}],optIn:[{click:'button[name="user_acceptCookie"]'}],optOut:[{hide:".euCookieModal"}]},{name:"youtube-desktop",prehideSelectors:["tp-yt-iron-overlay-backdrop.opened","ytd-consent-bump-v2-lightbox"],detectCmp:[{exists:"ytd-consent-bump-v2-lightbox tp-yt-paper-dialog"},{exists:'ytd-consent-bump-v2-lightbox tp-yt-paper-dialog a[href^="https://consent.youtube.com/"]'}],detectPopup:[{visible:"ytd-consent-bump-v2-lightbox tp-yt-paper-dialog"}],optIn:[{waitForThenClick:"ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:last-child #button,ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:last-child button"},{wait:500}],optOut:[{waitForThenClick:"ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:first-child #button,ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:first-child button"},{wait:500}],test:[{wait:500},{eval:"EVAL_YOUTUBE_DESKTOP_0"}]},{name:"youtube-mobile",prehideSelectors:[".consent-bump-v2-lightbox"],detectCmp:[{exists:"ytm-consent-bump-v2-renderer"}],detectPopup:[{visible:"ytm-consent-bump-v2-renderer"}],optIn:[{waitForThenClick:"ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons c3-material-button:first-child button, ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons ytm-button-renderer:first-child button"},{wait:500}],optOut:[{waitForThenClick:"ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons c3-material-button:nth-child(2) button, ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons ytm-button-renderer:nth-child(2) button"},{wait:500}],test:[{wait:500},{eval:"EVAL_YOUTUBE_MOBILE_0"}]},{name:"zdf",prehideSelectors:["#zdf-cmp-banner-sdk"],detectCmp:[{exists:"#zdf-cmp-banner-sdk"}],detectPopup:[{visible:"#zdf-cmp-main.zdf-cmp-show"}],optIn:[{waitForThenClick:"#zdf-cmp-main #zdf-cmp-accept-btn"}],optOut:[{waitForThenClick:"#zdf-cmp-main #zdf-cmp-deny-btn"}],test:[]},{name:"zentralruf-de",runContext:{urlPattern:"^https://(www\\.)?zentralruf\\.de"},prehideSelectors:["#cookie_modal_wrapper"],detectCmp:[{exists:"#cookie_modal_wrapper"}],detectPopup:[{visible:"#cookie_modal_wrapper"}],optIn:[{waitForThenClick:"#cookie_modal_wrapper #cookie_modal_button_consent_all"}],optOut:[{waitForThenClick:"#cookie_modal_wrapper #cookie_modal_button_choose"}]}],ye={"didomi.io":{detectors:[{presentMatcher:{target:{selector:"#didomi-host, #didomi-notice"},type:"css"},showingMatcher:{target:{selector:"body.didomi-popup-open, .didomi-notice-banner"},type:"css"}}],methods:[{action:{target:{selector:".didomi-popup-notice-buttons .didomi-button:not(.didomi-button-highlight), .didomi-notice-banner .didomi-learn-more-button"},type:"click"},name:"OPEN_OPTIONS"},{action:{actions:[{retries:50,target:{selector:"#didomi-purpose-cookies"},type:"waitcss",waitTime:50},{consents:[{description:"Share (everything) with others",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-share_whith_others]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-share_whith_others]:last-child"},type:"click"},type:"X"},{description:"Information storage and access",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-cookies]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-cookies]:last-child"},type:"click"},type:"D"},{description:"Content selection, offers and marketing",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-CL-T1Rgm7]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-CL-T1Rgm7]:last-child"},type:"click"},type:"E"},{description:"Analytics",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-analytics]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-analytics]:last-child"},type:"click"},type:"B"},{description:"Analytics",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-M9NRHJe3G]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-M9NRHJe3G]:last-child"},type:"click"},type:"B"},{description:"Ad and content selection",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-advertising_personalization]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-advertising_personalization]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection",falseAction:{parent:{childFilter:{target:{selector:"#didomi-purpose-pub-ciblee"}},selector:".didomi-consent-popup-data-processing, .didomi-components-accordion-label-container"},target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-pub-ciblee]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-pub-ciblee]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - basics",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-q4zlJqdcD]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-q4zlJqdcD]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - partners and subsidiaries",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-partenaire-cAsDe8jC]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-partenaire-cAsDe8jC]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - social networks",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-p4em9a8m]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-p4em9a8m]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - others",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-autres-pub]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-autres-pub]:last-child"},type:"click"},type:"F"},{description:"Social networks",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-reseauxsociaux]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-reseauxsociaux]:last-child"},type:"click"},type:"A"},{description:"Social networks",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-social_media]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-social_media]:last-child"},type:"click"},type:"A"},{description:"Content selection",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-content_personalization]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-content_personalization]:last-child"},type:"click"},type:"E"},{description:"Ad delivery",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-ad_delivery]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-ad_delivery]:last-child"},type:"click"},type:"F"}],type:"consent"},{action:{consents:[{matcher:{childFilter:{target:{selector:":not(.didomi-components-radio__option--selected)"}},type:"css"},trueAction:{target:{selector:":nth-child(2)"},type:"click"},falseAction:{target:{selector:":first-child"},type:"click"},type:"X"}],type:"consent"},target:{selector:".didomi-components-radio"},type:"foreach"}],type:"list"},name:"DO_CONSENT"},{action:{parent:{selector:".didomi-consent-popup-footer .didomi-consent-popup-actions"},target:{selector:".didomi-components-button:first-child"},type:"click"},name:"SAVE_CONSENT"}]},oil:{detectors:[{presentMatcher:{target:{selector:".as-oil-content-overlay"},type:"css"},showingMatcher:{target:{selector:".as-oil-content-overlay"},type:"css"}}],methods:[{action:{actions:[{target:{selector:".as-js-advanced-settings"},type:"click"},{retries:"10",target:{selector:".as-oil-cpc__purpose-container"},type:"waitcss",waitTime:"250"}],type:"list"},name:"OPEN_OPTIONS"},{action:{actions:[{consents:[{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Information storage and access","Opbevaring af og adgang til oplysninger på din enhed"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Information storage and access","Opbevaring af og adgang til oplysninger på din enhed"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"D"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personlige annoncer","Personalisation"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personlige annoncer","Personalisation"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"E"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Annoncevalg, levering og rapportering","Ad selection, delivery, reporting"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Annoncevalg, levering og rapportering","Ad selection, delivery, reporting"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"F"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personalisering af indhold","Content selection, delivery, reporting"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personalisering af indhold","Content selection, delivery, reporting"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"E"},{matcher:{parent:{childFilter:{target:{selector:".as-oil-cpc__purpose-header",textFilter:["Måling","Measurement"]}},selector:".as-oil-cpc__purpose-container"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{childFilter:{target:{selector:".as-oil-cpc__purpose-header",textFilter:["Måling","Measurement"]}},selector:".as-oil-cpc__purpose-container"},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"B"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:"Google"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:"Google"},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"F"}],type:"consent"}],type:"list"},name:"DO_CONSENT"},{action:{target:{selector:".as-oil__btn-optin"},type:"click"},name:"SAVE_CONSENT"},{action:{target:{selector:"div.as-oil"},type:"hide"},name:"HIDE_CMP"}]},optanon:{detectors:[{presentMatcher:{target:{selector:"#optanon-menu, .optanon-alert-box-wrapper"},type:"css"},showingMatcher:{target:{displayFilter:!0,selector:".optanon-alert-box-wrapper"},type:"css"}}],methods:[{action:{actions:[{target:{selector:".optanon-alert-box-wrapper .optanon-toggle-display, a[onclick*='OneTrust.ToggleInfoDisplay()'], a[onclick*='Optanon.ToggleInfoDisplay()']"},type:"click"}],type:"list"},name:"OPEN_OPTIONS"},{action:{actions:[{target:{selector:".preference-menu-item #Your-privacy"},type:"click"},{target:{selector:"#optanon-vendor-consent-text"},type:"click"},{action:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"X"}],type:"consent"},target:{selector:"#optanon-vendor-consent-list .vendor-item"},type:"foreach"},{target:{selector:".vendor-consent-back-link"},type:"click"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-performance"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-performance"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-functional"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-functional"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-advertising"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-advertising"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-social"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-social"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Social Media Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Social Media Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalisation"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalisation"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Site monitoring cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Site monitoring cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Third party privacy-enhanced content"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Third party privacy-enhanced content"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"X"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Performance & Advertising Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Performance & Advertising Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Information storage and access"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Information storage and access"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"D"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad selection, delivery, reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad selection, delivery, reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content selection, delivery, reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content selection, delivery, reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Measurement"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Measurement"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Recommended Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Recommended Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"X"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Unclassified Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Unclassified Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"X"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Analytical Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Analytical Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Marketing Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Marketing Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalization"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalization"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad Selection, Delivery & Reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad Selection, Delivery & Reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content Selection, Delivery & Reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content Selection, Delivery & Reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"}],type:"list"},name:"DO_CONSENT"},{action:{parent:{selector:".optanon-save-settings-button"},target:{selector:".optanon-white-button-middle"},type:"click"},name:"SAVE_CONSENT"},{action:{actions:[{target:{selector:"#optanon-popup-wrapper"},type:"hide"},{target:{selector:"#optanon-popup-bg"},type:"hide"},{target:{selector:".optanon-alert-box-wrapper"},type:"hide"}],type:"list"},name:"HIDE_CMP"}]},quantcast2:{detectors:[{presentMatcher:{target:{selector:"[data-tracking-opt-in-overlay]"},type:"css"},showingMatcher:{target:{selector:"[data-tracking-opt-in-overlay] [data-tracking-opt-in-learn-more]"},type:"css"}}],methods:[{action:{target:{selector:"[data-tracking-opt-in-overlay] [data-tracking-opt-in-learn-more]"},type:"click"},name:"OPEN_OPTIONS"},{action:{actions:[{type:"wait",waitTime:500},{action:{actions:[{target:{selector:"div",textFilter:["Information storage and access"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"D"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Personalization"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"F"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Ad selection, delivery, reporting"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"F"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Content selection, delivery, reporting"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"E"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Measurement"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"B"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Other Partners"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"X"}],type:"consent"},type:"ifcss"}],type:"list"},parent:{childFilter:{target:{selector:"input"}},selector:"[data-tracking-opt-in-overlay] > div > div"},target:{childFilter:{target:{selector:"input"}},selector:":scope > div"},type:"foreach"}],type:"list"},name:"DO_CONSENT"},{action:{target:{selector:"[data-tracking-opt-in-overlay] [data-tracking-opt-in-save]"},type:"click"},name:"SAVE_CONSENT"}]},springer:{detectors:[{presentMatcher:{parent:null,target:{selector:".cmp-app_gdpr"},type:"css"},showingMatcher:{parent:null,target:{displayFilter:!0,selector:".cmp-popup_popup"},type:"css"}}],methods:[{action:{actions:[{target:{selector:".cmp-intro_rejectAll"},type:"click"},{type:"wait",waitTime:250},{target:{selector:".cmp-purposes_purposeItem:not(.cmp-purposes_selectedPurpose)"},type:"click"}],type:"list"},name:"OPEN_OPTIONS"},{action:{consents:[{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Przechowywanie informacji na urządzeniu lub dostęp do nich",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Przechowywanie informacji na urządzeniu lub dostęp do nich",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"D"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór podstawowych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór podstawowych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"F"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"F"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"E"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"E"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"B"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"B"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"B"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Stosowanie badań rynkowych w celu generowania opinii odbiorców",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Stosowanie badań rynkowych w celu generowania opinii odbiorców",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"X"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Opracowywanie i ulepszanie produktów",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Opracowywanie i ulepszanie produktów",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"X"}],type:"consent"},name:"DO_CONSENT"},{action:{target:{selector:".cmp-details_save"},type:"click"},name:"SAVE_CONSENT"}]},wordpressgdpr:{detectors:[{presentMatcher:{parent:null,target:{selector:".wpgdprc-consent-bar"},type:"css"},showingMatcher:{parent:null,target:{displayFilter:!0,selector:".wpgdprc-consent-bar"},type:"css"}}],methods:[{action:{parent:null,target:{selector:".wpgdprc-consent-bar .wpgdprc-consent-bar__settings",textFilter:null},type:"click"},name:"OPEN_OPTIONS"},{action:{actions:[{target:{selector:".wpgdprc-consent-modal .wpgdprc-button",textFilter:"Eyeota"},type:"click"},{consents:[{description:"Eyeota Cookies",matcher:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Eyeota"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Eyeota"},target:{selector:"label"},type:"click"},type:"X"}],type:"consent"},{target:{selector:".wpgdprc-consent-modal .wpgdprc-button",textFilter:"Advertising"},type:"click"},{consents:[{description:"Advertising Cookies",matcher:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Advertising"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Advertising"},target:{selector:"label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},name:"DO_CONSENT"},{action:{parent:null,target:{selector:".wpgdprc-button",textFilter:"Save my settings"},type:"click"},name:"SAVE_CONSENT"}]}},we={autoconsent:_e,consentomatic:ye},Ce=Object.freeze({__proto__:null,autoconsent:_e,consentomatic:ye,default:we}); /*! Bundled license information: @ghostery/adblocker/dist/esm/codebooks/cosmetic-selector.js: @@ -442,4 +442,4 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. *) - */const wn=new class{constructor(e,t=null,o=null){if(this.id=c(),this.rules=[],this.foundCmp=null,this.state={cosmeticFiltersOn:!1,filterListReported:!1,lifecycle:"loading",prehideOn:!1,findCmpAttempts:0,detectedCmps:[],detectedPopups:[],selfTest:null},a.sendContentMessage=e,this.sendContentMessage=e,this.rules=[],this.updateState({lifecycle:"loading"}),this.addDynamicRules(),t)this.initialize(t,o);else{o&&this.parseDeclarativeRules(o);e({type:"init",url:window.location.href}),this.updateState({lifecycle:"waitingForInitResponse"})}this.domActions=new _(this)}initialize(e,t){const o=b(e);if(o.logs.lifecycle&&console.log("autoconsent init",window.location.href),this.config=o,o.enabled){if(t&&this.parseDeclarativeRules(t),e.enableFilterList){try{gn&&gn.length>0&&(this.filtersEngine=un.deserialize(gn))}catch(e){console.error("Error parsing filter list",e)}"loading"===document.readyState?window.addEventListener("DOMContentLoaded",(()=>{this.applyCosmeticFilters()})):this.applyCosmeticFilters()}if(this.rules=function(e,t){return e.filter((e=>(!t.disabledCmps||!t.disabledCmps.includes(e.name))&&(t.enableCosmeticRules||!e.isCosmetic)))}(this.rules,o),e.enablePrehide)if(document.documentElement)this.prehideElements();else{const e=()=>{window.removeEventListener("DOMContentLoaded",e),this.prehideElements()};window.addEventListener("DOMContentLoaded",e)}if("loading"===document.readyState){const e=()=>{window.removeEventListener("DOMContentLoaded",e),this.start()};window.addEventListener("DOMContentLoaded",e)}else this.start();this.updateState({lifecycle:"initialized"})}else o.logs.lifecycle&&console.log("autoconsent is disabled")}addDynamicRules(){v.forEach((e=>{this.rules.push(new e(this))}))}parseDeclarativeRules(e){e.consentomatic&&Object.keys(e.consentomatic).forEach((t=>{this.addConsentomaticCMP(t,e.consentomatic[t])})),e.autoconsent&&e.autoconsent.forEach((e=>{this.addDeclarativeCMP(e)}))}addDeclarativeCMP(e){this.rules.push(new u(e,this))}addConsentomaticCMP(e,t){this.rules.push(new h(`com_${e}`,t))}start(){!function(e,t=500){globalThis.requestIdleCallback?requestIdleCallback(e,{timeout:t}):setTimeout(e,0)}((()=>this._start()))}async _start(){const e=this.config.logs;e.lifecycle&&console.log(`Detecting CMPs on ${window.location.href}`),this.updateState({lifecycle:"started"});const t=await this.findCmp(this.config.detectRetries);if(this.updateState({detectedCmps:t.map((e=>e.name))}),0===t.length)return e.lifecycle&&console.log("no CMP found",location.href),this.config.enablePrehide&&this.undoPrehide(),this.filterListFallback();this.updateState({lifecycle:"cmpDetected"});const o=[],i=[];for(const e of t)e.isCosmetic?i.push(e):o.push(e);let n=!1,s=await this.detectPopups(o,(async e=>{n=await this.handlePopup(e)}));if(0===s.length&&(s=await this.detectPopups(i,(async e=>{n=await this.handlePopup(e)}))),0===s.length)return e.lifecycle&&console.log("no popup found"),this.config.enablePrehide&&this.undoPrehide(),!1;if(s.length>1){const t={msg:"Found multiple CMPs, check the detection rules.",cmps:s.map((e=>e.name))};e.errors&&console.warn(t.msg,t.cmps),this.sendContentMessage({type:"autoconsentError",details:t})}return n}async findCmp(e){const t=this.config.logs;this.updateState({findCmpAttempts:this.state.findCmpAttempts+1});const o=[];for(const e of this.rules)try{if(!e.checkRunContext())continue;await e.detectCmp()&&(t.lifecycle&&console.log(`Found CMP: ${e.name} ${window.location.href}`),this.sendContentMessage({type:"cmpDetected",url:location.href,cmp:e.name}),o.push(e))}catch(o){t.errors&&console.warn(`error detecting ${e.name}`,o)}return 0===o.length&&e>0?(await this.domActions.wait(500),this.findCmp(e-1)):o}async detectPopup(e){if(await this.waitForPopup(e).catch((t=>(this.config.logs.errors&&console.warn(`error waiting for a popup for ${e.name}`,t),!1))))return this.updateState({detectedPopups:this.state.detectedPopups.concat([e.name])}),this.sendContentMessage({type:"popupFound",cmp:e.name,url:location.href}),e;throw new Error("Popup is not shown")}async detectPopups(e,t){const o=e.map((e=>this.detectPopup(e)));await Promise.any(o).then((e=>{t(e)})).catch((()=>null));const i=await Promise.allSettled(o),n=[];for(const e of i)"fulfilled"===e.status&&n.push(e.value);return n}async handlePopup(e){return this.updateState({lifecycle:"openPopupDetected"}),this.config.enablePrehide&&!this.state.prehideOn&&this.prehideElements(),this.state.cosmeticFiltersOn&&this.undoCosmetics(),this.foundCmp=e,"optOut"===this.config.autoAction?await this.doOptOut():"optIn"===this.config.autoAction?await this.doOptIn():(this.config.logs.lifecycle&&console.log("waiting for opt-out signal...",location.href),!0)}async doOptOut(){const e=this.config.logs;let t;return this.updateState({lifecycle:"runningOptOut"}),this.foundCmp?(e.lifecycle&&console.log(`CMP ${this.foundCmp.name}: opt out on ${window.location.href}`),t=await this.foundCmp.optOut(),e.lifecycle&&console.log(`${this.foundCmp.name}: opt out result ${t}`)):(e.errors&&console.log("no CMP to opt out"),t=!1),this.config.enablePrehide&&this.undoPrehide(),this.sendContentMessage({type:"optOutResult",cmp:this.foundCmp?this.foundCmp.name:"none",result:t,scheduleSelfTest:this.foundCmp&&this.foundCmp.hasSelfTest,url:location.href}),t&&!this.foundCmp.isIntermediate?(this.sendContentMessage({type:"autoconsentDone",cmp:this.foundCmp.name,isCosmetic:this.foundCmp.isCosmetic,url:location.href}),this.updateState({lifecycle:"done"})):this.updateState({lifecycle:t?"optOutSucceeded":"optOutFailed"}),t}async doOptIn(){const e=this.config.logs;let t;return this.updateState({lifecycle:"runningOptIn"}),this.foundCmp?(e.lifecycle&&console.log(`CMP ${this.foundCmp.name}: opt in on ${window.location.href}`),t=await this.foundCmp.optIn(),e.lifecycle&&console.log(`${this.foundCmp.name}: opt in result ${t}`)):(e.errors&&console.log("no CMP to opt in"),t=!1),this.config.enablePrehide&&this.undoPrehide(),this.sendContentMessage({type:"optInResult",cmp:this.foundCmp?this.foundCmp.name:"none",result:t,scheduleSelfTest:!1,url:location.href}),t&&!this.foundCmp.isIntermediate?(this.sendContentMessage({type:"autoconsentDone",cmp:this.foundCmp.name,isCosmetic:this.foundCmp.isCosmetic,url:location.href}),this.updateState({lifecycle:"done"})):this.updateState({lifecycle:t?"optInSucceeded":"optInFailed"}),t}async doSelfTest(){const e=this.config.logs;let t;return this.foundCmp?(e.lifecycle&&console.log(`CMP ${this.foundCmp.name}: self-test on ${window.location.href}`),t=await this.foundCmp.test()):(e.errors&&console.log("no CMP to self test"),t=!1),this.sendContentMessage({type:"selfTestResult",cmp:this.foundCmp?this.foundCmp.name:"none",result:t,url:location.href}),this.updateState({selfTest:t}),t}async waitForPopup(e,t=5,o=500){const i=this.config.logs;i.lifecycle&&console.log("checking if popup is open...",e.name);const n=await e.detectPopup().catch((t=>(i.errors&&console.warn(`error detecting popup for ${e.name}`,t),!1)));return!n&&t>0?(await this.domActions.wait(o),this.waitForPopup(e,t-1,o)):(i.lifecycle&&console.log(e.name,"popup is "+(n?"open":"not open")),n)}prehideElements(){const e=this.config.logs,t=this.rules.filter((e=>e.prehideSelectors&&e.checkRunContext())).reduce(((e,t)=>[...e,...t.prehideSelectors]),["#didomi-popup,.didomi-popup-container,.didomi-popup-notice,.didomi-consent-popup-preferences,#didomi-notice,.didomi-popup-backdrop,.didomi-screen-medium"]);return this.updateState({prehideOn:!0}),setTimeout((()=>{this.config.enablePrehide&&this.state.prehideOn&&!["runningOptOut","runningOptIn"].includes(this.state.lifecycle)&&(e.lifecycle&&console.log("Process is taking too long, unhiding elements"),this.undoPrehide())}),this.config.prehideTimeout||2e3),this.domActions.prehide(t.join(","))}undoPrehide(){return this.updateState({prehideOn:!1}),this.domActions.undoPrehide()}async applyCosmeticFilters(e){if(!this.filtersEngine)return!1;const t=this.config?.logs;e||(e=mn(this.filtersEngine)),setTimeout((()=>{if(this.state.cosmeticFiltersOn&&!this.state.filterListReported){this.domActions.elementVisible(An(e),"any")?(t?.lifecycle&&console.log("Prehide cosmetic filters matched",location.href),this.reportFilterlist()):t?.lifecycle&&console.log("Prehide cosmetic filters didn't match",location.href)}}),1e3),this.updateState({cosmeticFiltersOn:!0});try{this.cosmeticStyleSheet=await this.domActions.createOrUpdateStyleSheet(e,this.cosmeticStyleSheet),t?.lifecycle&&console.log("[cosmetics]",this.cosmeticStyleSheet,location.href),document.adoptedStyleSheets.push(this.cosmeticStyleSheet)}catch(e){return this.config.logs&&console.error("Error applying cosmetic filters",e),!1}return!0}undoCosmetics(){this.updateState({cosmeticFiltersOn:!1}),this.config.logs.lifecycle&&console.log("[undocosmetics]",this.cosmeticStyleSheet,location.href),this.domActions.removeStyleSheet(this.cosmeticStyleSheet)}reportFilterlist(){this.sendContentMessage({type:"cmpDetected",url:location.href,cmp:"filterList"}),this.sendContentMessage({type:"popupFound",cmp:"filterList",url:location.href}),this.updateState({filterListReported:!0})}filterListFallback(){if(!this.filtersEngine)return this.updateState({lifecycle:"nothingDetected"}),!1;const e=mn(this.filtersEngine),t=this.domActions.elementVisible(An(e),"any"),o=this.config?.logs;return t?(this.applyCosmeticFilters(e),o?.lifecycle&&console.log("Keeping cosmetic filters",location.href),this.updateState({lifecycle:"cosmeticFiltersDetected"}),this.state.filterListReported||this.reportFilterlist(),this.sendContentMessage({type:"optOutResult",cmp:"filterList",result:!0,scheduleSelfTest:!1,url:location.href}),this.updateState({lifecycle:"done"}),this.sendContentMessage({type:"autoconsentDone",cmp:"filterList",isCosmetic:!0,url:location.href}),!0):(o?.lifecycle&&console.log("Cosmetic filters didn't work, removing them",location.href),this.undoCosmetics(),this.updateState({lifecycle:"nothingDetected"}),!1)}updateState(e){Object.assign(this.state,e),this.sendContentMessage({type:"report",instanceId:this.id,url:window.location.href,mainFrame:window.top===window.self,state:this.state})}async receiveMessageCallback(e){const t=this.config?.logs;switch(t?.messages&&console.log("received from background",e,window.location.href),e.type){case"initResp":this.initialize(e.config,e.rules);break;case"optIn":await this.doOptIn();break;case"optOut":await this.doOptOut();break;case"selfTest":await this.doSelfTest();break;case"evalResp":!function(e,t){const o=a.pending.get(e);o?(a.pending.delete(e),o.timer&&window.clearTimeout(o.timer),o.resolve(t)):console.warn("no eval #",e)}(e.id,e.result)}}}((e=>{window.webkit.messageHandlers[e.type]&&window.webkit.messageHandlers[e.type].postMessage(e).then((e=>{wn.receiveMessageCallback(e)}))}),null,yn);window.autoconsentMessageCallback=e=>{wn.receiveMessageCallback(e)}}(); + */const ve=new class{constructor(e,t=null,o=null){if(this.id=a(),this.rules=[],this.foundCmp=null,this.state={cosmeticFiltersOn:!1,filterListReported:!1,lifecycle:"loading",prehideOn:!1,findCmpAttempts:0,detectedCmps:[],detectedPopups:[],heuristicPatterns:[],heuristicSnippets:[],selfTest:null},r.sendContentMessage=e,this.sendContentMessage=e,this.rules=[],this.updateState({lifecycle:"loading"}),this.addDynamicRules(),t)this.initialize(t,o);else{o&&this.parseDeclarativeRules(o);e({type:"init",url:window.location.href}),this.updateState({lifecycle:"waitingForInitResponse"})}this.domActions=new C(this)}initialize(e,t){const o=g(e);if(o.logs.lifecycle&&console.log("autoconsent init",window.location.href),this.config=o,o.enabled){if(t&&this.parseDeclarativeRules(t),e.enableFilterList){try{0}catch(e){console.error("Error parsing filter list",e)}"loading"===document.readyState?window.addEventListener("DOMContentLoaded",(()=>{this.applyCosmeticFilters()})):this.applyCosmeticFilters()}if(this.rules=function(e,t){return e.filter((e=>(!t.disabledCmps||!t.disabledCmps.includes(e.name))&&(t.enableCosmeticRules||!e.isCosmetic)))}(this.rules,o),e.enablePrehide)if(document.documentElement)this.prehideElements();else{const e=()=>{window.removeEventListener("DOMContentLoaded",e),this.prehideElements()};window.addEventListener("DOMContentLoaded",e)}if("loading"===document.readyState){const e=()=>{window.removeEventListener("DOMContentLoaded",e),this.start()};window.addEventListener("DOMContentLoaded",e)}else this.start();this.updateState({lifecycle:"initialized"})}else o.logs.lifecycle&&console.log("autoconsent is disabled")}addDynamicRules(){w.forEach((e=>{this.rules.push(new e(this))}))}parseDeclarativeRules(e){e.consentomatic&&Object.keys(e.consentomatic).forEach((t=>{this.addConsentomaticCMP(t,e.consentomatic[t])})),e.autoconsent&&e.autoconsent.forEach((e=>{this.addDeclarativeCMP(e)}))}addDeclarativeCMP(e){this.rules.push(new u(e,this))}addConsentomaticCMP(e,t){this.rules.push(new m(`com_${e}`,t))}start(){!function(e,t=500){globalThis.requestIdleCallback?requestIdleCallback(e,{timeout:t}):setTimeout(e,0)}((()=>this._start()))}async _start(){const e=this.config.logs;e.lifecycle&&console.log(`Detecting CMPs on ${window.location.href}`),this.updateState({lifecycle:"started"});const t=await this.findCmp(this.config.detectRetries);if(this.updateState({detectedCmps:t.map((e=>e.name))}),0===t.length)return e.lifecycle&&console.log("no CMP found",location.href),this.config.enablePrehide&&this.undoPrehide(),this.filterListFallback();this.updateState({lifecycle:"cmpDetected"});const o=[],i=[];for(const e of t)e.isCosmetic?i.push(e):o.push(e);let c=!1,n=await this.detectPopups(o,(async e=>{c=await this.handlePopup(e)}));if(0===n.length&&(n=await this.detectPopups(i,(async e=>{c=await this.handlePopup(e)}))),0===n.length)return e.lifecycle&&console.log("no popup found"),this.config.enablePrehide&&this.undoPrehide(),!1;if(n.length>1){const t={msg:"Found multiple CMPs, check the detection rules.",cmps:n.map((e=>e.name))};e.errors&&console.warn(t.msg,t.cmps),this.sendContentMessage({type:"autoconsentError",details:t})}return c}async findCmp(e){const t=this.config.logs;this.updateState({findCmpAttempts:this.state.findCmpAttempts+1});const o=[];for(const e of this.rules)try{if(!e.checkRunContext())continue;await e.detectCmp()&&(t.lifecycle&&console.log(`Found CMP: ${e.name} ${window.location.href}`),this.sendContentMessage({type:"cmpDetected",url:location.href,cmp:e.name}),o.push(e))}catch(o){t.errors&&console.warn(`error detecting ${e.name}`,o)}return this.detectHeuristics(),0===o.length&&e>0?(await this.domActions.wait(500),this.findCmp(e-1)):o}detectHeuristics(){if(this.config.enableHeuristicDetection){const{patterns:e,snippets:t}=function(){const e=document.documentElement.innerText,t=[],o=[];for(const i of ge){const c=e.match(i);c&&(t.push(i.toString()),o.push(...c.map((e=>e.substring(0,200)))))}return{patterns:t,snippets:o}}();e.length>0&&(e.length!==this.state.heuristicPatterns.length||this.state.heuristicPatterns.some(((t,o)=>t!==e[o])))&&(this.config.logs.lifecycle&&console.log("Heuristic patterns found",e,t),this.updateState({heuristicPatterns:e,heuristicSnippets:t}))}}async detectPopup(e){if(await this.waitForPopup(e).catch((t=>(this.config.logs.errors&&console.warn(`error waiting for a popup for ${e.name}`,t),!1))))return this.updateState({detectedPopups:this.state.detectedPopups.concat([e.name])}),this.sendContentMessage({type:"popupFound",cmp:e.name,url:location.href}),e;throw new Error("Popup is not shown")}async detectPopups(e,t){const o=e.map((e=>this.detectPopup(e)));await Promise.any(o).then((e=>{this.detectHeuristics(),t(e)})).catch((()=>null));const i=await Promise.allSettled(o),c=[];for(const e of i)"fulfilled"===e.status&&c.push(e.value);return c}async handlePopup(e){return this.updateState({lifecycle:"openPopupDetected"}),this.config.enablePrehide&&!this.state.prehideOn&&this.prehideElements(),this.state.cosmeticFiltersOn&&this.undoCosmetics(),this.foundCmp=e,"optOut"===this.config.autoAction?await this.doOptOut():"optIn"===this.config.autoAction?await this.doOptIn():(this.config.logs.lifecycle&&console.log("waiting for opt-out signal...",location.href),!0)}async doOptOut(){const e=this.config.logs;let t;return this.updateState({lifecycle:"runningOptOut"}),this.foundCmp?(e.lifecycle&&console.log(`CMP ${this.foundCmp.name}: opt out on ${window.location.href}`),t=await this.foundCmp.optOut(),e.lifecycle&&console.log(`${this.foundCmp.name}: opt out result ${t}`)):(e.errors&&console.log("no CMP to opt out"),t=!1),this.config.enablePrehide&&this.undoPrehide(),this.sendContentMessage({type:"optOutResult",cmp:this.foundCmp?this.foundCmp.name:"none",result:t,scheduleSelfTest:this.foundCmp&&this.foundCmp.hasSelfTest,url:location.href}),t&&!this.foundCmp.isIntermediate?(this.sendContentMessage({type:"autoconsentDone",cmp:this.foundCmp.name,isCosmetic:this.foundCmp.isCosmetic,url:location.href}),this.updateState({lifecycle:"done"})):this.updateState({lifecycle:t?"optOutSucceeded":"optOutFailed"}),t}async doOptIn(){const e=this.config.logs;let t;return this.updateState({lifecycle:"runningOptIn"}),this.foundCmp?(e.lifecycle&&console.log(`CMP ${this.foundCmp.name}: opt in on ${window.location.href}`),t=await this.foundCmp.optIn(),e.lifecycle&&console.log(`${this.foundCmp.name}: opt in result ${t}`)):(e.errors&&console.log("no CMP to opt in"),t=!1),this.config.enablePrehide&&this.undoPrehide(),this.sendContentMessage({type:"optInResult",cmp:this.foundCmp?this.foundCmp.name:"none",result:t,scheduleSelfTest:!1,url:location.href}),t&&!this.foundCmp.isIntermediate?(this.sendContentMessage({type:"autoconsentDone",cmp:this.foundCmp.name,isCosmetic:this.foundCmp.isCosmetic,url:location.href}),this.updateState({lifecycle:"done"})):this.updateState({lifecycle:t?"optInSucceeded":"optInFailed"}),t}async doSelfTest(){const e=this.config.logs;let t;return this.foundCmp?(e.lifecycle&&console.log(`CMP ${this.foundCmp.name}: self-test on ${window.location.href}`),t=await this.foundCmp.test()):(e.errors&&console.log("no CMP to self test"),t=!1),this.sendContentMessage({type:"selfTestResult",cmp:this.foundCmp?this.foundCmp.name:"none",result:t,url:location.href}),this.updateState({selfTest:t}),t}async waitForPopup(e,t=5,o=500){const i=this.config.logs;i.lifecycle&&console.log("checking if popup is open...",e.name);const c=await e.detectPopup().catch((t=>(i.errors&&console.warn(`error detecting popup for ${e.name}`,t),!1)));return!c&&t>0?(await this.domActions.wait(o),this.waitForPopup(e,t-1,o)):(i.lifecycle&&console.log(e.name,"popup is "+(c?"open":"not open")),c)}prehideElements(){const e=this.config.logs,t=this.rules.filter((e=>e.prehideSelectors&&e.checkRunContext())).reduce(((e,t)=>[...e,...t.prehideSelectors]),["#didomi-popup,.didomi-popup-container,.didomi-popup-notice,.didomi-consent-popup-preferences,#didomi-notice,.didomi-popup-backdrop,.didomi-screen-medium"]);return this.updateState({prehideOn:!0}),setTimeout((()=>{this.config.enablePrehide&&this.state.prehideOn&&!["runningOptOut","runningOptIn"].includes(this.state.lifecycle)&&(e.lifecycle&&console.log("Process is taking too long, unhiding elements"),this.undoPrehide())}),this.config.prehideTimeout||2e3),this.domActions.prehide(t.join(","))}undoPrehide(){return this.updateState({prehideOn:!1}),this.domActions.undoPrehide()}async applyCosmeticFilters(e){if(!this.filtersEngine)return!1;const t=this.config?.logs;setTimeout((()=>{if(this.state.cosmeticFiltersOn&&!this.state.filterListReported){const o=this.domActions.elementVisible(function(e){if(e)return e.replace(/\s*{[^\\}]*}\s*/g,",").replace(/,$/,"");return""}(e),"any");o?(t?.lifecycle&&console.log("Prehide cosmetic filters matched",location.href),this.reportFilterlist()):t?.lifecycle&&console.log("Prehide cosmetic filters didn't match",location.href)}}),2e3),this.updateState({cosmeticFiltersOn:!0});try{this.cosmeticStyleSheet=await this.domActions.createOrUpdateStyleSheet(e,this.cosmeticStyleSheet),t?.lifecycle&&console.log("[cosmetics]",this.cosmeticStyleSheet,location.href),document.adoptedStyleSheets.push(this.cosmeticStyleSheet)}catch(e){return this.config.logs&&console.error("Error applying cosmetic filters",e),!1}return!0}undoCosmetics(){this.updateState({cosmeticFiltersOn:!1}),this.config.logs.lifecycle&&console.log("[undocosmetics]",this.cosmeticStyleSheet,location.href),this.domActions.removeStyleSheet(this.cosmeticStyleSheet)}reportFilterlist(){this.sendContentMessage({type:"cmpDetected",url:location.href,cmp:"filterList"}),this.sendContentMessage({type:"popupFound",cmp:"filterList",url:location.href}),this.updateState({filterListReported:!0})}filterListFallback(){return this.updateState({lifecycle:"nothingDetected"}),!1}updateState(e){Object.assign(this.state,e),this.sendContentMessage({type:"report",instanceId:this.id,url:window.location.href,mainFrame:window.top===window.self,state:this.state})}async receiveMessageCallback(e){const t=this.config?.logs;switch(t?.messages&&console.log("received from background",e,window.location.href),e.type){case"initResp":this.initialize(e.config,e.rules);break;case"optIn":await this.doOptIn();break;case"optOut":await this.doOptOut();break;case"selfTest":await this.doSelfTest();break;case"evalResp":!function(e,t){const o=r.pending.get(e);o?(r.pending.delete(e),o.timer&&window.clearTimeout(o.timer),o.resolve(t)):console.warn("no eval #",e)}(e.id,e.result)}}}((e=>{window.webkit.messageHandlers[e.type]&&window.webkit.messageHandlers[e.type].postMessage(e).then((e=>{ve.receiveMessageCallback(e)}))}),null,Ce);window.autoconsentMessageCallback=e=>{ve.receiveMessageCallback(e)}}(); diff --git a/DuckDuckGo/AutofillCredentialsDebugViewController.swift b/DuckDuckGo/AutofillCredentialsDebugViewController.swift index 097c4d5e94..555d8971b6 100644 --- a/DuckDuckGo/AutofillCredentialsDebugViewController.swift +++ b/DuckDuckGo/AutofillCredentialsDebugViewController.swift @@ -87,7 +87,8 @@ class AutofillCredentialsDebugViewController: UITableViewController { private let tld: TLD = AppDependencyProvider.shared.storageCache.tld private let autofillDomainNameUrlMatcher: AutofillDomainNameUrlMatcher = AutofillDomainNameUrlMatcher() private var credentials: [DisplayCredentials] = [] - private let authenticator = AutofillLoginListAuthenticator(reason: UserText.autofillLoginListAuthenticationReason) + private let authenticator = AutofillLoginListAuthenticator(reason: UserText.autofillLoginListAuthenticationReason, + cancelTitle: UserText.autofillLoginListAuthenticationCancelButton) override func viewDidLoad() { super.viewDidLoad() diff --git a/DuckDuckGo/AutofillListItemTableViewCell.swift b/DuckDuckGo/AutofillListItemTableViewCell.swift index 209ed903d8..c1e7f90086 100644 --- a/DuckDuckGo/AutofillListItemTableViewCell.swift +++ b/DuckDuckGo/AutofillListItemTableViewCell.swift @@ -20,6 +20,7 @@ import UIKit import SwiftUI import DuckUI +import Core class AutofillListItemTableViewCell: UITableViewCell { @@ -78,12 +79,12 @@ class AutofillListItemTableViewCell: UITableViewCell { fatalError("init(coder:) has not been implemented") } - var viewModel: AutofillLoginListItemViewModel? { + var item: AutofillLoginItem? { didSet { - guard let viewModel = viewModel else { + guard let item = item else { return } - setupContentView(with: viewModel) + setupContentView(with: item) } } @@ -119,7 +120,7 @@ class AutofillListItemTableViewCell: UITableViewCell { ]) } - private func setupContentView(with item: AutofillLoginListItemViewModel) { + private func setupContentView(with item: AutofillLoginItem) { titleLabel.text = item.title subtitleLabel.text = item.subtitle iconImageView.loadFavicon(forDomain: item.account.domain, usingCache: .fireproof, preferredFakeFaviconLetters: item.preferredFaviconLetters) diff --git a/DuckDuckGo/AutofillLoginDetailsViewController.swift b/DuckDuckGo/AutofillLoginDetailsViewController.swift index 1a08045aa1..ae03ae302e 100644 --- a/DuckDuckGo/AutofillLoginDetailsViewController.swift +++ b/DuckDuckGo/AutofillLoginDetailsViewController.swift @@ -38,7 +38,8 @@ class AutofillLoginDetailsViewController: UIViewController { weak var delegate: AutofillLoginDetailsViewControllerDelegate? private let viewModel: AutofillLoginDetailsViewModel private var cancellables: Set = [] - private var authenticator = AutofillLoginListAuthenticator(reason: UserText.autofillLoginListAuthenticationReason) + private var authenticator = AutofillLoginListAuthenticator(reason: UserText.autofillLoginListAuthenticationReason, + cancelTitle: UserText.autofillLoginListAuthenticationCancelButton) private let lockedView = AutofillItemsLockedView() private let noAuthAvailableView = AutofillNoAuthAvailableView() private var contentView: UIView? diff --git a/DuckDuckGo/AutofillLoginListViewModel.swift b/DuckDuckGo/AutofillLoginListViewModel.swift index c244f97752..3c267fda0d 100644 --- a/DuckDuckGo/AutofillLoginListViewModel.swift +++ b/DuckDuckGo/AutofillLoginListViewModel.swift @@ -27,28 +27,6 @@ import DDGSync import PrivacyDashboard import os.log -internal enum AutofillLoginListSectionType: Comparable { - case enableAutofill - case suggestions(title: String, items: [AutofillLoginListItemViewModel]) - case credentials(title: String, items: [AutofillLoginListItemViewModel]) - - static func < (lhs: AutofillLoginListSectionType, rhs: AutofillLoginListSectionType) -> Bool { - if case .credentials(let leftTitle, _) = lhs, - case .credentials(let rightTitle, _) = rhs { - if leftTitle == miscSectionHeading { - return false - } else if rightTitle == miscSectionHeading { - return true - } - - return leftTitle.localizedCaseInsensitiveCompare(rightTitle) == .orderedAscending - } - return true - } - - static let miscSectionHeading = "#" -} - internal enum EnableAutofillRows: Int, CaseIterable { case toggleAutofill case resetNeverPromptWebsites @@ -69,7 +47,8 @@ final class AutofillLoginListViewModel: ObservableObject { static let tabUid = "com.duckduckgo.autofill.tab-uid" } - let authenticator = AutofillLoginListAuthenticator(reason: UserText.autofillLoginListAuthenticationReason) + let authenticator = AutofillLoginListAuthenticator(reason: UserText.autofillLoginListAuthenticationReason, + cancelTitle: UserText.autofillLoginListAuthenticationCancelButton) var isSearching: Bool = false var isEditing: Bool = false { didSet { @@ -109,6 +88,8 @@ final class AutofillLoginListViewModel: ObservableObject { return settings["monitorIntervalDays"] as? Int ?? 42 }() + private lazy var credentialIdentityStoreManager: AutofillCredentialIdentityStoreManaging = AutofillCredentialIdentityStoreManager(vault: secureVault, tld: tld) + private lazy var syncPromoManager: SyncPromoManaging = SyncPromoManager(syncService: syncService) private lazy var autofillSurveyManager: AutofillSurveyManaging = AutofillSurveyManager() @@ -401,7 +382,13 @@ final class AutofillLoginListViewModel: ObservableObject { } do { - return try secureVault.accounts() + let accounts = try secureVault.accounts() + + Task { + await credentialIdentityStoreManager.replaceCredentialStore(with: accounts) + } + + return accounts } catch { Logger.autofill.error("Failed to fetch accounts \(error.localizedDescription, privacy: .public)") return [] @@ -437,22 +424,22 @@ final class AutofillLoginListViewModel: ObservableObject { } if !accountsToSuggest.isEmpty { - let accountItems = accountsToSuggest.map { AutofillLoginListItemViewModel(account: $0, - tld: tld, - autofillDomainNameUrlMatcher: autofillDomainNameUrlMatcher, - autofillDomainNameUrlSort: autofillDomainNameUrlSort) + let accountItems = accountsToSuggest.map { AutofillLoginItem(account: $0, + tld: tld, + autofillDomainNameUrlMatcher: autofillDomainNameUrlMatcher, + autofillDomainNameUrlSort: autofillDomainNameUrlSort) } newSections.append(.suggestions(title: UserText.autofillLoginListSuggested, items: accountItems)) } } - let viewModelsGroupedByFirstLetter = accounts.autofillLoginListItemViewModelsForAccountsGroupedByFirstLetter( + let viewModelsGroupedByFirstLetter = accounts.groupedByFirstLetter( tld: tld, autofillDomainNameUrlMatcher: autofillDomainNameUrlMatcher, autofillDomainNameUrlSort: autofillDomainNameUrlSort) - let accountSections = viewModelsGroupedByFirstLetter.autofillLoginListSectionsForViewModelsSortedByTitle(autofillDomainNameUrlSort, - tld: tld) - + let accountSections = viewModelsGroupedByFirstLetter.sortedIntoSections(autofillDomainNameUrlSort, + tld: tld) + newSections.append(contentsOf: accountSections) return newSections } @@ -584,51 +571,3 @@ final class AutofillLoginListViewModel: ObservableObject { return true } } - -extension AutofillLoginListItemViewModel: Comparable { - static func < (lhs: AutofillLoginListItemViewModel, rhs: AutofillLoginListItemViewModel) -> Bool { - lhs.title < rhs.title - } -} - -internal extension Array where Element == SecureVaultModels.WebsiteAccount { - - func autofillLoginListItemViewModelsForAccountsGroupedByFirstLetter(tld: TLD, - autofillDomainNameUrlMatcher: AutofillDomainNameUrlMatcher, - autofillDomainNameUrlSort: AutofillDomainNameUrlSort) - -> [String: [AutofillLoginListItemViewModel]] { - reduce(into: [String: [AutofillLoginListItemViewModel]]()) { result, account in - - // Unfortunetly, folding doesn't produce perfect results despite respecting the system locale - // E.g. Romainian should treat letters with diacritics as seperate letters, but folding doesn't - // Apple's own apps (e.g. contacts) seem to suffer from the same problem - let key: String - if let firstChar = autofillDomainNameUrlSort.firstCharacterForGrouping(account, tld: tld), - let deDistinctionedChar = String(firstChar).folding(options: [.diacriticInsensitive, .caseInsensitive], locale: nil).first, - deDistinctionedChar.isLetter { - - key = String(deDistinctionedChar) - } else { - key = AutofillLoginListSectionType.miscSectionHeading - } - - return result[key, default: []].append(AutofillLoginListItemViewModel(account: account, - tld: tld, - autofillDomainNameUrlMatcher: autofillDomainNameUrlMatcher, - autofillDomainNameUrlSort: autofillDomainNameUrlSort)) - } - } -} - -internal extension Dictionary where Key == String, Value == [AutofillLoginListItemViewModel] { - - func autofillLoginListSectionsForViewModelsSortedByTitle(_ autofillDomainNameUrlSort: AutofillDomainNameUrlSort, tld: TLD) -> [AutofillLoginListSectionType] { - map { dictionaryItem -> AutofillLoginListSectionType in - let sortedGroup = dictionaryItem.value.sorted(by: { - autofillDomainNameUrlSort.compareAccountsForSortingAutofill(lhs: $0.account, rhs: $1.account, tld: tld) == .orderedAscending - }) - return AutofillLoginListSectionType.credentials(title: dictionaryItem.key, - items: sortedGroup) - }.sorted() - } -} diff --git a/DuckDuckGo/AutofillLoginPromptViewModel.swift b/DuckDuckGo/AutofillLoginPromptViewModel.swift index da3a3e551a..4193ba26af 100644 --- a/DuckDuckGo/AutofillLoginPromptViewModel.swift +++ b/DuckDuckGo/AutofillLoginPromptViewModel.swift @@ -20,6 +20,7 @@ import Foundation import UIKit import BrowserServicesKit +import Core protocol AutofillLoginPromptViewModelDelegate: AnyObject { func autofillLoginPromptViewModel(_ viewModel: AutofillLoginPromptViewModel, didSelectAccount account: SecureVaultModels.WebsiteAccount) diff --git a/DuckDuckGo/AutofillLoginSettingsListViewController.swift b/DuckDuckGo/AutofillLoginSettingsListViewController.swift index 49884b187b..2c0723b1dd 100644 --- a/DuckDuckGo/AutofillLoginSettingsListViewController.swift +++ b/DuckDuckGo/AutofillLoginSettingsListViewController.swift @@ -785,9 +785,9 @@ final class AutofillLoginSettingsListViewController: UIViewController { // MARK: Cell Methods - private func credentialCell(for tableView: UITableView, item: AutofillLoginListItemViewModel, indexPath: IndexPath) -> AutofillListItemTableViewCell { + private func credentialCell(for tableView: UITableView, item: AutofillLoginItem, indexPath: IndexPath) -> AutofillListItemTableViewCell { let cell = tableView.dequeueCell(ofType: AutofillListItemTableViewCell.self, for: indexPath) - cell.viewModel = item + cell.item = item cell.accessoryType = .disclosureIndicator cell.backgroundColor = UIColor(designSystemColor: .surface) return cell diff --git a/DuckDuckGo/AutofillUsageMonitor.swift b/DuckDuckGo/AutofillUsageMonitor.swift index 4302aec266..6123c43180 100644 --- a/DuckDuckGo/AutofillUsageMonitor.swift +++ b/DuckDuckGo/AutofillUsageMonitor.swift @@ -18,13 +18,43 @@ // import Core +import AuthenticationServices +import BrowserServicesKit final class AutofillUsageMonitor { + private lazy var credentialIdentityStoreManager: AutofillCredentialIdentityStoreManager? = { + guard let vault = try? AutofillSecureVaultFactory.makeVault(reporter: SecureVaultReporter()) else { + return nil + } + + return AutofillCredentialIdentityStoreManager(vault: vault, + tld: AppDependencyProvider.shared.storageCache.tld) + }() + init() { NotificationCenter.default.addObserver(self, selector: #selector(didReceiveSaveEvent), name: .autofillSaveEvent, object: nil) + + ASCredentialIdentityStore.shared.getState({ [weak self] state in + if state.isEnabled { + if self?.autofillExtensionEnabled == nil { + Task { + await self?.credentialIdentityStoreManager?.populateCredentialStore() + } + } + self?.autofillExtensionEnabled = true + } else { + if self?.autofillExtensionEnabled != nil { + Pixel.fire(pixel: .autofillExtensionDisabled) + self?.autofillExtensionEnabled = false + } + } + }) } - + + @UserDefaultsWrapper(key: .autofillExtensionEnabled, defaultValue: nil) + var autofillExtensionEnabled: Bool? + @UserDefaultsWrapper(key: .autofillFirstTimeUser, defaultValue: true) private var autofillFirstTimeUser: Bool diff --git a/DuckDuckGo/Base.lproj/OmniBar.xib b/DuckDuckGo/Base.lproj/OmniBar.xib index 0cd7d0a6c2..96af3fcd2b 100644 --- a/DuckDuckGo/Base.lproj/OmniBar.xib +++ b/DuckDuckGo/Base.lproj/OmniBar.xib @@ -1,9 +1,9 @@ - + - + @@ -340,7 +340,7 @@ - + - + - + @@ -39,43 +36,44 @@ - + - + - - + + - - + + + - + - + - + - + - + @@ -87,7 +85,7 @@ - + @@ -133,20 +131,20 @@ - + - + - + - + @@ -196,18 +194,20 @@ + - + - + + - + - - + + diff --git a/DuckDuckGo/BrowsingMenu/BrowsingMenuViewController.swift b/DuckDuckGo/BrowsingMenu/BrowsingMenuViewController.swift index 6b7f6b9abe..cc3d04d61b 100644 --- a/DuckDuckGo/BrowsingMenu/BrowsingMenuViewController.swift +++ b/DuckDuckGo/BrowsingMenu/BrowsingMenuViewController.swift @@ -249,8 +249,9 @@ extension BrowsingMenuViewController: UITableViewDelegate { switch menuEntries[indexPath.row] { case .regular(_, _, _, _, let action): - dismiss(animated: true) - action() + dismiss(animated: true) { + action() + } case .separator: break } diff --git a/DuckDuckGo/DaxDialogs.swift b/DuckDuckGo/DaxDialogs.swift index d1d30b9e3a..f8b4181f93 100644 --- a/DuckDuckGo/DaxDialogs.swift +++ b/DuckDuckGo/DaxDialogs.swift @@ -31,7 +31,6 @@ protocol EntityProviding { } protocol NewTabDialogSpecProvider { - func nextHomeScreenMessage() -> DaxDialogs.HomeScreenSpec? func nextHomeScreenMessageNew() -> DaxDialogs.HomeScreenSpec? func dismiss() } @@ -50,7 +49,6 @@ protocol ContextualOnboardingLogic { func setPrivacyButtonPulseSeen() func setDaxDialogDismiss() - func canEnableAddFavoriteFlow() -> Bool // Temporary during Contextual Onboarding Experiment func enableAddFavoriteFlow() } @@ -144,14 +142,14 @@ final class DaxDialogs: NewTabDialogSpecProvider, ContextualOnboardingLogic { highlightAddressBar: false, pixelName: .daxDialogsSiteOwnedByMajorUnique, type: .siteOwnedByMajorTracker) - static let withOneTracker = BrowsingSpec(message: UserText.daxDialogBrowsingWithOneTracker, + static let withOneTracker = BrowsingSpec(message: UserText.Onboarding.ContextualOnboarding.daxDialogBrowsingWithOneTracker, cta: UserText.daxDialogBrowsingWithOneTrackerCTA, - highlightAddressBar: true, + highlightAddressBar: false, pixelName: .daxDialogsWithTrackersUnique, type: .withOneTracker) - static let withMultipleTrackers = BrowsingSpec(message: UserText.daxDialogBrowsingWithMultipleTrackers, + static let withMultipleTrackers = BrowsingSpec(message: UserText.Onboarding.ContextualOnboarding.daxDialogBrowsingWithMultipleTrackers, cta: UserText.daxDialogBrowsingWithMultipleTrackersCTA, - highlightAddressBar: true, + highlightAddressBar: false, pixelName: .daxDialogsWithTrackersUnique, type: .withMultipleTrackers) // Message and CTA empty on purpose as for this case we use only pixelName and type @@ -233,10 +231,6 @@ final class DaxDialogs: NewTabDialogSpecProvider, ContextualOnboardingLogic { self.addToDockManager = onboardingManager } - private var isNewOnboarding: Bool { - variantManager.isContextualDaxDialogsEnabled - } - private var firstBrowsingMessageSeen: Bool { return settings.browsingAfterSearchShown || settings.browsingWithTrackersShown @@ -272,17 +266,14 @@ final class DaxDialogs: NewTabDialogSpecProvider, ContextualOnboardingLogic { } var isShowingSearchSuggestions: Bool { - guard isNewOnboarding else { return false } return currentHomeSpec == .initial } var isShowingSitesSuggestions: Bool { - guard isNewOnboarding else { return false } return lastShownDaxDialogType.flatMap(BrowsingSpec.SpecType.init(rawValue:)) == .visitWebsite || currentHomeSpec == .subsequent } var isShowingAddToDockDialog: Bool { - guard isNewOnboarding else { return false } return currentHomeSpec == .final && addToDockManager.addToDockEnabledState == .contextual } @@ -293,7 +284,7 @@ final class DaxDialogs: NewTabDialogSpecProvider, ContextualOnboardingLogic { } var isShowingFireDialog: Bool { - guard isNewOnboarding, let lastShownDaxDialogType else { return false } + guard let lastShownDaxDialogType else { return false } return BrowsingSpec.SpecType(rawValue: lastShownDaxDialogType) == .fire } @@ -302,25 +293,16 @@ final class DaxDialogs: NewTabDialogSpecProvider, ContextualOnboardingLogic { } var shouldShowFireButtonPulse: Bool { - if isNewOnboarding { - // Show fire the user hasn't seen the fire education dialog or the fire button has not animated before. - nonDDGBrowsingMessageSeen && (!settings.fireMessageExperimentShown && settings.fireButtonPulseDateShown == nil) && isEnabled - } else { - nonDDGBrowsingMessageSeen && !settings.fireButtonEducationShownOrExpired && isEnabled - } + // Show fire the user hasn't seen the fire education dialog or the fire button has not animated before. + nonDDGBrowsingMessageSeen && (!settings.fireMessageExperimentShown && settings.fireButtonPulseDateShown == nil) && isEnabled } var shouldShowPrivacyButtonPulse: Bool { - guard isNewOnboarding else { return false } return settings.browsingWithTrackersShown && !settings.privacyButtonPulseShown && fireButtonPulseTimer == nil && isEnabled } func isStillOnboarding() -> Bool { - if isNewOnboarding { - if peekNextHomeScreenMessageExperiment() != nil { - return true - } - } else if peekNextHomeScreenMessage() != nil { + if peekNextHomeScreenMessageExperiment() != nil { return true } return false @@ -336,16 +318,8 @@ final class DaxDialogs: NewTabDialogSpecProvider, ContextualOnboardingLogic { settings.isDismissed = false } - func canEnableAddFavoriteFlow() -> Bool { - !isNewOnboarding - } - func enableAddFavoriteFlow() { - guard canEnableAddFavoriteFlow() else { return } - nextHomeScreenMessageOverride = .addFavorite - // Progress to next home screen message, but don't re-show the second dax dialog if it's already been shown - settings.homeScreenMessagesSeen = max(settings.homeScreenMessagesSeen, 1) } func resumeRegularFlow() { @@ -360,22 +334,19 @@ final class DaxDialogs: NewTabDialogSpecProvider, ContextualOnboardingLogic { private static let timeToFireButtonExpire: TimeInterval = 1 * 60 * 60 private var lastVisitedOnboardingWebsiteURLPath: String? { - guard isNewOnboarding else { return nil } return settings.lastVisitedOnboardingWebsiteURLPath } private func saveLastVisitedOnboardingWebsite(url: URL?) { - guard isNewOnboarding, let url = url else { return } + guard let url = url else { return } settings.lastVisitedOnboardingWebsiteURLPath = url.absoluteString } private func removeLastVisitedOnboardingWebsite() { - guard isNewOnboarding else { return } settings.lastVisitedOnboardingWebsiteURLPath = nil } private var lastShownDaxDialogType: String? { - guard isNewOnboarding else { return nil } return settings.lastShownContextualOnboardingDialogType } @@ -384,12 +355,10 @@ final class DaxDialogs: NewTabDialogSpecProvider, ContextualOnboardingLogic { } private func saveLastShownDaxDialog(specType: BrowsingSpec.SpecType) { - guard isNewOnboarding else { return } settings.lastShownContextualOnboardingDialogType = specType.rawValue } private func removeLastShownDaxDialog() { - guard isNewOnboarding else { return } settings.lastShownContextualOnboardingDialogType = nil } @@ -404,13 +373,13 @@ final class DaxDialogs: NewTabDialogSpecProvider, ContextualOnboardingLogic { return BrowsingSpec.withoutTrackers case BrowsingSpec.SpecType.siteIsMajorTracker.rawValue: guard let host = privacyInfo.domain else { return nil } - return majorTrackerMessage(host) + return majorTrackerMessage(host, isReloadingDialog: true) case BrowsingSpec.SpecType.siteOwnedByMajorTracker.rawValue: guard let host = privacyInfo.domain, let owner = isOwnedByFacebookOrGoogle(host) else { return nil } - return majorTrackerOwnerMessage(host, owner) + return majorTrackerOwnerMessage(host, owner, isReloadingDialog: true) case BrowsingSpec.SpecType.withOneTracker.rawValue, BrowsingSpec.SpecType.withMultipleTrackers.rawValue: guard let entityNames = blockedEntityNames(privacyInfo.trackerInfo) else { return nil } - return trackersBlockedMessage(entityNames) + return trackersBlockedMessage(entityNames, isReloadingDialog: true) case BrowsingSpec.SpecType.fire.rawValue: return .fire case BrowsingSpec.SpecType.final.rawValue: @@ -439,20 +408,12 @@ final class DaxDialogs: NewTabDialogSpecProvider, ContextualOnboardingLogic { fireButtonPulseTimer?.invalidate() settings.fireButtonEducationShownOrExpired = true } - - func fireButtonEducationMessage() -> ActionSheetSpec? { - guard shouldShowFireButtonPulse else { return nil } - settings.fireButtonEducationShownOrExpired = true - return ActionSheetSpec.fireButtonEducation - } func setSearchMessageSeen() { - guard isNewOnboarding else { return } saveLastShownDaxDialog(specType: .visitWebsite) } func setFireEducationMessageSeen() { - guard isNewOnboarding else { return } // Set also privacy button pulse seen as we don't have to show anymore if we saw the fire educational message. settings.privacyButtonPulseShown = true settings.fireMessageExperimentShown = true @@ -460,35 +421,24 @@ final class DaxDialogs: NewTabDialogSpecProvider, ContextualOnboardingLogic { } func clearedBrowserData() { - guard isNewOnboarding else { return } setDaxDialogDismiss() } func setPrivacyButtonPulseSeen() { - guard isNewOnboarding else { return } settings.privacyButtonPulseShown = true } func setDaxDialogDismiss() { - guard isNewOnboarding else { return } clearOnboardingBrowsingData() } func setFinalOnboardingDialogSeen() { - guard isNewOnboarding else { return } settings.browsingFinalDialogShown = true } func nextBrowsingMessageIfShouldShow(for privacyInfo: PrivacyInfo) -> BrowsingSpec? { - var message: BrowsingSpec? - if isNewOnboarding { - message = nextBrowsingMessageExperiment(privacyInfo: privacyInfo) - } else { - guard privacyInfo.url != lastURLDaxDialogReturnedFor else { return nil } - message = nextBrowsingMessage(privacyInfo: privacyInfo) - } - + let message = nextBrowsingMessageExperiment(privacyInfo: privacyInfo) if message != nil { lastURLDaxDialogReturnedFor = privacyInfo.url } @@ -496,32 +446,6 @@ final class DaxDialogs: NewTabDialogSpecProvider, ContextualOnboardingLogic { return message } - private func nextBrowsingMessage(privacyInfo: PrivacyInfo) -> BrowsingSpec? { - guard isEnabled, nextHomeScreenMessageOverride == nil else { return nil } - guard let host = privacyInfo.domain else { return nil } - - if privacyInfo.url.isDuckDuckGoSearch { - return searchMessage() - } - - // won't be shown if owned by major tracker message has already been shown - if isFacebookOrGoogle(privacyInfo.url) { - return majorTrackerMessage(host) - } - - // won't be shown if major tracker message has already been shown - if let owner = isOwnedByFacebookOrGoogle(host) { - return majorTrackerOwnerMessage(host, owner) - } - - if let entityNames = blockedEntityNames(privacyInfo.trackerInfo) { - return trackersBlockedMessage(entityNames) - } - - // only shown if first time on a non-ddg page and none of the non-ddg messages shown - return noTrackersMessage() - } - private func nextBrowsingMessageExperiment(privacyInfo: PrivacyInfo) -> BrowsingSpec? { func hasTrackers(host: String) -> Bool { @@ -546,12 +470,12 @@ final class DaxDialogs: NewTabDialogSpecProvider, ContextualOnboardingLogic { spec = searchMessage() } else if isFacebookOrGoogle(privacyInfo.url) && shouldShowNetworkTrackerDialog { // won't be shown if owned by major tracker message has already been shown - spec = majorTrackerMessage(host) + spec = majorTrackerMessage(host, isReloadingDialog: false) } else if let owner = isOwnedByFacebookOrGoogle(host), shouldShowNetworkTrackerDialog { // won't be shown if major tracker message has already been shown - spec = majorTrackerOwnerMessage(host, owner) + spec = majorTrackerOwnerMessage(host, owner, isReloadingDialog: false) } else if let entityNames = blockedEntityNames(privacyInfo.trackerInfo), !settings.browsingWithTrackersShown { - spec = trackersBlockedMessage(entityNames) + spec = trackersBlockedMessage(entityNames, isReloadingDialog: false) } else if !settings.browsingWithoutTrackersShown && !privacyInfo.url.isDuckDuckGoSearch && !hasTrackers(host: host) { // if non duck duck go search and no trackers found and no tracker message already shown, show no trackers message spec = noTrackersMessage() @@ -570,16 +494,6 @@ final class DaxDialogs: NewTabDialogSpecProvider, ContextualOnboardingLogic { return spec } - func nextHomeScreenMessage() -> HomeScreenSpec? { - guard let homeScreenSpec = peekNextHomeScreenMessage() else { return nil } - - if homeScreenSpec != nextHomeScreenMessageOverride { - settings.homeScreenMessagesSeen += 1 - } - - return homeScreenSpec - } - func nextHomeScreenMessageNew() -> HomeScreenSpec? { // Reset the last browsing information when opening a new tab so loading the previous website won't show again the Dax dialog clearedBrowserData() @@ -592,25 +506,6 @@ final class DaxDialogs: NewTabDialogSpecProvider, ContextualOnboardingLogic { return homeScreenSpec } - private func peekNextHomeScreenMessage() -> HomeScreenSpec? { - if nextHomeScreenMessageOverride != nil { - return nextHomeScreenMessageOverride - } - - guard isEnabled else { return nil } - guard settings.homeScreenMessagesSeen < Constants.homeScreenMessagesSeenMaxCeiling else { return nil } - - if settings.homeScreenMessagesSeen == 0 { - return .initial - } - - if firstBrowsingMessageSeen { - return .final - } - - return nil - } - private func peekNextHomeScreenMessageExperiment() -> HomeScreenSpec? { if nextHomeScreenMessageOverride != nil { return nextHomeScreenMessageOverride @@ -645,9 +540,9 @@ final class DaxDialogs: NewTabDialogSpecProvider, ContextualOnboardingLogic { return nil } - func majorTrackerOwnerMessage(_ host: String, _ majorTrackerEntity: Entity) -> DaxDialogs.BrowsingSpec? { - if !isNewOnboarding && settings.browsingMajorTrackingSiteShown { return nil } - + func majorTrackerOwnerMessage(_ host: String, _ majorTrackerEntity: Entity, isReloadingDialog: Bool) -> DaxDialogs.BrowsingSpec? { + if !isReloadingDialog && settings.browsingMajorTrackingSiteShown { return nil } + guard let entityName = majorTrackerEntity.displayName, let entityPrevalence = majorTrackerEntity.prevalence else { return nil } settings.browsingMajorTrackingSiteShown = true @@ -657,8 +552,8 @@ final class DaxDialogs: NewTabDialogSpecProvider, ContextualOnboardingLogic { entityPrevalence) } - private func majorTrackerMessage(_ host: String) -> DaxDialogs.BrowsingSpec? { - if !isNewOnboarding && settings.browsingMajorTrackingSiteShown { return nil } + private func majorTrackerMessage(_ host: String, isReloadingDialog: Bool) -> DaxDialogs.BrowsingSpec? { + if !isReloadingDialog && settings.browsingMajorTrackingSiteShown { return nil } guard let entityName = entityProviding.entity(forHost: host)?.displayName else { return nil } settings.browsingMajorTrackingSiteShown = true @@ -677,8 +572,8 @@ final class DaxDialogs: NewTabDialogSpecProvider, ContextualOnboardingLogic { return BrowsingSpec.final } - private func trackersBlockedMessage(_ entitiesBlocked: [String]) -> BrowsingSpec? { - if !isNewOnboarding && settings.browsingWithTrackersShown { return nil } + private func trackersBlockedMessage(_ entitiesBlocked: [String], isReloadingDialog: Bool) -> BrowsingSpec? { + if !isReloadingDialog && settings.browsingWithTrackersShown { return nil } var spec: BrowsingSpec? switch entitiesBlocked.count { @@ -689,24 +584,12 @@ final class DaxDialogs: NewTabDialogSpecProvider, ContextualOnboardingLogic { case 1: settings.browsingWithTrackersShown = true let args = entitiesBlocked[0] - spec = if isNewOnboarding { - BrowsingSpec.withOneTracker.format(message: UserText.DaxOnboardingExperiment.ContextualOnboarding.daxDialogBrowsingWithOneTracker, args: args) - } else { - BrowsingSpec.withOneTracker.format(args: args) - } + spec = BrowsingSpec.withOneTracker.format(message: UserText.Onboarding.ContextualOnboarding.daxDialogBrowsingWithOneTracker, args: args) default: settings.browsingWithTrackersShown = true let args: [CVarArg] = [entitiesBlocked.count - 2, entitiesBlocked[0], entitiesBlocked[1]] - spec = if isNewOnboarding { - BrowsingSpec.withMultipleTrackers.format(message: UserText.DaxOnboardingExperiment.ContextualOnboarding.daxDialogBrowsingWithMultipleTrackers, args: args) - } else { - BrowsingSpec.withMultipleTrackers.format(args: args) - } - } - // New Contextual onboarding doesn't highlight the address bar. This checks prevents to cancel the lottie animation. - if isNewOnboarding { - spec?.highlightAddressBar = false + spec = BrowsingSpec.withMultipleTrackers.format(message: UserText.Onboarding.ContextualOnboarding.daxDialogBrowsingWithMultipleTrackers, args: args) } return spec } diff --git a/DuckDuckGo/DaxDialogsSettings.swift b/DuckDuckGo/DaxDialogsSettings.swift index 960b63013a..63be0c9973 100644 --- a/DuckDuckGo/DaxDialogsSettings.swift +++ b/DuckDuckGo/DaxDialogsSettings.swift @@ -23,8 +23,6 @@ protocol DaxDialogsSettings { var isDismissed: Bool { get set } - var homeScreenMessagesSeen: Int { get set } - var browsingAfterSearchShown: Bool { get set } var browsingWithTrackersShown: Bool { get set } @@ -96,8 +94,6 @@ class InMemoryDaxDialogsSettings: DaxDialogsSettings { var isDismissed: Bool = false - var homeScreenMessagesSeen: Int = 0 - var browsingAfterSearchShown: Bool = false var browsingWithTrackersShown: Bool = false diff --git a/DuckDuckGo/DaxOnboarding.xcassets/OnboardingGradientLight.imageset/Contents.json b/DuckDuckGo/DaxOnboarding.xcassets/OnboardingGradientLight.imageset/Contents.json new file mode 100644 index 0000000000..5a553e162d --- /dev/null +++ b/DuckDuckGo/DaxOnboarding.xcassets/OnboardingGradientLight.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "OnboardingBackgroundLight.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/DuckDuckGo/DaxOnboarding.xcassets/OnboardingGradientLight.imageset/OnboardingBackgroundLight.pdf b/DuckDuckGo/DaxOnboarding.xcassets/OnboardingGradientLight.imageset/OnboardingBackgroundLight.pdf new file mode 100644 index 0000000000..24684a0c47 Binary files /dev/null and b/DuckDuckGo/DaxOnboarding.xcassets/OnboardingGradientLight.imageset/OnboardingBackgroundLight.pdf differ diff --git a/DuckDuckGo/DuckDuckGo.entitlements b/DuckDuckGo/DuckDuckGo.entitlements index cfeb809f1b..ee77dff2bf 100644 --- a/DuckDuckGo/DuckDuckGo.entitlements +++ b/DuckDuckGo/DuckDuckGo.entitlements @@ -2,6 +2,8 @@ + com.apple.developer.authentication-services.autofill-credential-provider + com.apple.developer.browser.app-installation com.apple.developer.networking.networkextension @@ -18,9 +20,11 @@ $(GROUP_ID_PREFIX).netp $(GROUP_ID_PREFIX).statistics group.com.duckduckgo.app-configuration + $(GROUP_ID_PREFIX).vault keychain-access-groups + $(AppIdentifierPrefix)$(VAULT_APP_GROUP) $(AppIdentifierPrefix)$(APP_ID) $(AppIdentifierPrefix)$(SUBSCRIPTION_APP_GROUP) diff --git a/DuckDuckGo/DuckDuckGoAlpha Debug.entitlements b/DuckDuckGo/DuckDuckGoAlpha Debug.entitlements new file mode 100644 index 0000000000..446452b1fb --- /dev/null +++ b/DuckDuckGo/DuckDuckGoAlpha Debug.entitlements @@ -0,0 +1,30 @@ + + + + + com.apple.developer.authentication-services.autofill-credential-provider + + com.apple.developer.networking.networkextension + + packet-tunnel-provider + + com.apple.developer.web-browser + + com.apple.security.application-groups + + $(GROUP_ID_PREFIX).app-configuration + $(GROUP_ID_PREFIX).bookmarks + $(GROUP_ID_PREFIX).contentblocker + $(GROUP_ID_PREFIX).database + $(GROUP_ID_PREFIX).netp + $(GROUP_ID_PREFIX).statistics + $(GROUP_ID_PREFIX).vault + + keychain-access-groups + + $(AppIdentifierPrefix)$(APP_ID) + $(AppIdentifierPrefix)$(SUBSCRIPTION_APP_GROUP) + $(AppIdentifierPrefix)$(VAULT_APP_GROUP) + + + diff --git a/DuckDuckGo/DuckDuckGoAlpha.entitlements b/DuckDuckGo/DuckDuckGoAlpha.entitlements index b8debe8f31..60338d9de6 100644 --- a/DuckDuckGo/DuckDuckGoAlpha.entitlements +++ b/DuckDuckGo/DuckDuckGoAlpha.entitlements @@ -2,6 +2,8 @@ + com.apple.developer.authentication-services.autofill-credential-provider + com.apple.developer.networking.networkextension packet-tunnel-provider @@ -10,17 +12,19 @@ com.apple.security.application-groups + group.com.duckduckgo.alpha.app-configuration group.com.duckduckgo.alpha.bookmarks group.com.duckduckgo.alpha.contentblocker group.com.duckduckgo.alpha.database group.com.duckduckgo.alpha.netp group.com.duckduckgo.alpha.statistics - group.com.duckduckgo.alpha.app-configuration + group.com.duckduckgo.alpha.vault keychain-access-groups $(AppIdentifierPrefix)$(APP_ID) $(AppIdentifierPrefix)$(SUBSCRIPTION_APP_GROUP) + $(AppIdentifierPrefix)$(VAULT_APP_GROUP) diff --git a/DuckDuckGo/DuckPlayer/DuckPlayerNavigationHandler.swift b/DuckDuckGo/DuckPlayer/DuckPlayerNavigationHandler.swift index 609b1d3af8..8ea855cfca 100644 --- a/DuckDuckGo/DuckPlayer/DuckPlayerNavigationHandler.swift +++ b/DuckDuckGo/DuckPlayer/DuckPlayerNavigationHandler.swift @@ -25,6 +25,7 @@ import Common import BrowserServicesKit import DuckPlayer import os.log +import Combine /// Handles navigation and interactions related to Duck Player within the app. final class DuckPlayerNavigationHandler: NSObject { @@ -75,6 +76,9 @@ final class DuckPlayerNavigationHandler: NSObject { /// Delegate for handling tab navigation events. weak var tabNavigationHandler: DuckPlayerTabNavigationHandling? + /// Cancellable for observing DuckPlayer Mode changes + private var duckPlayerModeCancellable: AnyCancellable? + private struct Constants { static let SERPURL = "duckduckgo.com/" static let refererHeader = "Referer" @@ -125,6 +129,8 @@ final class DuckPlayerNavigationHandler: NSObject { self.dailyPixelFiring = dailyPixelFiring self.tabNavigationHandler = tabNavigationHandler self.duckPlayerOverlayUsagePixels = duckPlayerOverlayUsagePixels + + super.init() } /// Returns the file path for the Duck Player HTML template. @@ -552,6 +558,14 @@ final class DuckPlayerNavigationHandler: NSObject { return false } + /// Register a DuckPlayer mode Observe to handle events when the mode changes + private func setupPlayerModeObserver() { + duckPlayerModeCancellable = duckPlayer.settings.duckPlayerSettingsPublisher + .sink { [weak self] in + self?.duckPlayerOverlayUsagePixels?.duckPlayerMode = self?.duckPlayer.settings.mode ?? .disabled + } + } + /// // Handle "open in YouTube" links (duck://player/openInYoutube) /// /// - Parameter url: The `URL` used to determine the tab type. @@ -577,6 +591,11 @@ final class DuckPlayerNavigationHandler: NSObject { } } + deinit { + duckPlayerModeCancellable?.cancel() + duckPlayerModeCancellable = nil + } + } extension DuckPlayerNavigationHandler: DuckPlayerNavigationHandling { @@ -636,7 +655,6 @@ extension DuckPlayerNavigationHandler: DuckPlayerNavigationHandling { // Before performing the simulated request DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) { self.performRequest(request: newRequest, webView: webView) - self.duckPlayerOverlayUsagePixels?.handleNavigationAndFirePixels(url: url, duckPlayerMode: self.duckPlayerMode) self.fireDuckPlayerPixels(webView: webView) } @@ -680,12 +698,6 @@ extension DuckPlayerNavigationHandler: DuckPlayerNavigationHandling { return .notHandled(.duplicateNavigation) } - // Overlay Usage Pixel handling - if let url = webView.url { - duckPlayerOverlayUsagePixels?.handleNavigationAndFirePixels(url: url, duckPlayerMode: duckPlayerMode) - lastURLChangeHandling = Date() - } - // Check if DuckPlayer feature is enabled guard isDuckPlayerFeatureEnabled else { return .notHandled(.featureOff) @@ -786,6 +798,9 @@ extension DuckPlayerNavigationHandler: DuckPlayerNavigationHandling { guard let url = webView.url else { return } + + // Fire Reload Pixel + duckPlayerOverlayUsagePixels?.fireReloadPixelIfNeeded(url: url) if url.isDuckPlayer, duckPlayerMode != .disabled { redirectToDuckPlayerVideo(url: url, webView: webView, disableNewTab: true) @@ -809,6 +824,11 @@ extension DuckPlayerNavigationHandler: DuckPlayerNavigationHandling { // Reset referrer and initial settings referrer = .other + + // Attach WebView to OverlayPixels + duckPlayerOverlayUsagePixels?.webView = webView + duckPlayerOverlayUsagePixels?.duckPlayerMode = duckPlayer.settings.mode + setupPlayerModeObserver() // Ensure feature and mode are enabled guard isDuckPlayerFeatureEnabled, @@ -825,6 +845,7 @@ extension DuckPlayerNavigationHandler: DuckPlayerNavigationHandling { referrer = parameters.referrer redirectToDuckPlayerVideo(url: url, webView: webView, disableNewTab: true) } + } /// Updates the referrer after the web view finishes loading a page. @@ -836,16 +857,6 @@ extension DuckPlayerNavigationHandler: DuckPlayerNavigationHandling { // Reset allowFirstVideo duckPlayer.settings.allowFirstVideo = false - // Overlay Usage Pixel handling for Direct Navigation - if let url = webView.url, !url.isYoutube { - duckPlayerOverlayUsagePixels?.handleNavigationAndFirePixels(url: url, duckPlayerMode: duckPlayerMode) - } - // Reset Overlay Last Fired pixel after the page is loaded - // A delay is required as Youtube sometimes performs an extra redirect on load - DispatchQueue.main.asyncAfter(deadline: .now() + 3) { - self.duckPlayerOverlayUsagePixels?.lastFiredPixel = nil - } - } @@ -925,6 +936,16 @@ extension DuckPlayerNavigationHandler: DuckPlayerNavigationHandling { return false } + // Allow Youtube's internal navigation when DuckPlayer is enabled and user is watching on Youtube + // This is to prevent DuckPlayer from interfering with Youtube's internal navigation for search and settings + // https://app.asana.com/0/1204099484721401/1208930843675395/f + if let (destinationVideoID, _) = url.youtubeVideoParams, + let (originVideoID, _) = webView.url?.youtubeVideoParams, + destinationVideoID == originVideoID, + duckPlayerMode == .enabled { + return false + } + // Redirect to Duck Player if enabled if url.isYoutubeWatch && duckPlayerMode == .enabled && !isDuckPlayerRedirect(url: url) { redirectToDuckPlayerVideo(url: url, webView: webView) @@ -933,7 +954,7 @@ extension DuckPlayerNavigationHandler: DuckPlayerNavigationHandling { // Redirect to Youtube + DuckPlayer Overlay if Ask Mode if url.isYoutubeWatch && duckPlayerMode == .alwaysAsk && !isDuckPlayerRedirect(url: url) { - redirectToYouTubeVideo(url: url, webView: webView, allowFirstVideo: false) + redirectToYouTubeVideo(url: url, webView: webView, allowFirstVideo: false, disableNewTab: true) return true } diff --git a/DuckDuckGo/DuckPlayer/DuckPlayerOverlayUsagePixels.swift b/DuckDuckGo/DuckPlayer/DuckPlayerOverlayUsagePixels.swift index 6f3339dd72..c4b3f4afe2 100644 --- a/DuckDuckGo/DuckPlayer/DuckPlayerOverlayUsagePixels.swift +++ b/DuckDuckGo/DuckPlayer/DuckPlayerOverlayUsagePixels.swift @@ -17,91 +17,110 @@ // limitations under the License. // +import WebKit import Core protocol DuckPlayerOverlayPixelFiring { - + var pixelFiring: PixelFiring.Type { get set } - var navigationHistory: [URL] { get set } - var lastFiredPixel: Pixel.Event? { get set } - - func handleNavigationAndFirePixels(url: URL?, duckPlayerMode: DuckPlayerMode) + var webView: WKWebView? { get set } + var duckPlayerMode: DuckPlayerMode { get set } + func fireNavigationPixelsIfNeeded(webView: WKWebView) + func fireReloadPixelIfNeeded(url: URL) } -final class DuckPlayerOverlayUsagePixels: DuckPlayerOverlayPixelFiring { +final class DuckPlayerOverlayUsagePixels: NSObject, DuckPlayerOverlayPixelFiring { var pixelFiring: PixelFiring.Type - var navigationHistory: [URL] = [] - var lastFiredPixel: Pixel.Event? + var duckPlayerMode: DuckPlayerMode = .disabled + private var isObserving = false + + weak var webView: WKWebView? { + didSet { + if let webView { + addObservers(to: webView) + } + } + } - private var idleTimer: Timer? - private var idleTimeInterval: TimeInterval + private var lastVisitedURL: URL? // Tracks the last known URL - init(pixelFiring: PixelFiring.Type = Pixel.self, - navigationHistory: [URL] = [], - timeoutInterval: TimeInterval = 30.0) { + init(pixelFiring: PixelFiring.Type = Pixel.self) { self.pixelFiring = pixelFiring - self.idleTimeInterval = timeoutInterval } - func handleNavigationAndFirePixels(url: URL?, duckPlayerMode: DuckPlayerMode) { - guard let url = url else { return } - let comparisonURL = url.forComparison() - - // Only append the URL if it's different from the last entry in normalized form - navigationHistory.append(comparisonURL) - - // DuckPlayer is in Ask Mode, there's navigation history, and last URL is a YouTube Watch Video - guard duckPlayerMode == .alwaysAsk, - navigationHistory.count > 1, - let currentURL = navigationHistory.last, - let previousURL = navigationHistory.dropLast().last, - previousURL.isYoutubeWatch else { return } - - var isReload = false - // Check for a reload condition: when current videoID is the same as Previous - if let currentVideoID = currentURL.youtubeVideoParams?.videoID, - let previousVideoID = previousURL.youtubeVideoParams?.videoID, - !previousURL.isDuckPlayer, !currentURL.isDuckPlayer { - isReload = currentVideoID == previousVideoID + deinit { + if let webView { + removeObservers(from: webView) } + } + + func fireNavigationPixelsIfNeeded(webView: WKWebView) { - // Fire the reload pixel if this is a reload navigation - if isReload { - firePixel(.duckPlayerYouTubeOverlayNavigationRefresh) - } else { - // Determine if it’s a back navigation by looking further back in history - let isBackNavigation = navigationHistory.count > 2 && - navigationHistory[navigationHistory.count - 3].forComparison() == currentURL.forComparison() - - // Fire the appropriate pixel based on navigation type - if isBackNavigation { - firePixel(.duckPlayerYouTubeOverlayNavigationBack) - } else if previousURL.isYoutubeWatch && currentURL.isYoutube { - // Forward navigation within YouTube (including non-video URLs) - firePixel(.duckPlayerYouTubeNavigationWithinYouTube) - } else if previousURL.isYoutubeWatch && !currentURL.isYoutube && !currentURL.isDuckPlayer { - // Navigation outside YouTube - firePixel(.duckPlayerYouTubeOverlayNavigationOutsideYoutube) - navigationHistory.removeAll() - } + guard let currentURL = webView.url else { + return } + + let backItemURL = webView.backForwardList.backItem?.url - // Truncation logic: Remove all URLs up to the last occurrence of the current URL in normalized form - if navigationHistory.count > 0 { - if let lastOccurrenceIndex = (0.. Bool { + // If user saw Add to Dock instruction during onboarding do not show the reminder. + guard !(variantManager.isSupported(feature: .addToDockIntro) || variantManager.isSupported(feature: .addToDockContextual)) else { return false } + guard !hasShownBefore() else { return false } guard hasReminderTimeElapsed else { return false } return true diff --git a/DuckDuckGo/Info.plist b/DuckDuckGo/Info.plist index 6d3941b0ca..5e88ebbac6 100644 --- a/DuckDuckGo/Info.plist +++ b/DuckDuckGo/Info.plist @@ -21,49 +21,39 @@ CFBundleAlternateIcons - black + AppIcon-black CFBundleIconFiles - AppIconBlack29x29 - AppIconBlack40x40 - AppIconBlack60x60 + AppIcon-black - blue + AppIcon-blue CFBundleIconFiles - AppIconBlue29x29 - AppIconBlue40x40 - AppIconBlue60x60 + AppIcon-blue - green + AppIcon-green CFBundleIconFiles - AppIconGreen29x29 - AppIconGreen40x40 - AppIconGreen60x60 + AppIcon-green - purple + AppIcon-purple CFBundleIconFiles - AppIconPurple29x29 - AppIconPurple40x40 - AppIconPurple60x60 + AppIcon-purple - yellow + AppIcon-yellow CFBundleIconFiles - AppIconYellow29x29 - AppIconYellow40x40 - AppIconYellow60x60 + AppIcon-yellow @@ -81,44 +71,39 @@ CFBundleAlternateIcons - black + AppIcon-black CFBundleIconFiles - AppIconBlack76x76 - AppIconBlack83.5x83.5 + AppIcon-black - blue + AppIcon-green CFBundleIconFiles - AppIconBlue76x76 - AppIconBlue83.5x83.5 + AppIcon-blue - green + AppIcon-green CFBundleIconFiles - AppIconGreen76x76 - AppIconGreen83.5x83.5 + AppIcon-green - purple + AppIcon-purple CFBundleIconFiles - AppIconPurple76x76 - AppIconPurple83.5x83.5 + AppIcon-purple - yellow + AppIcon-yellow CFBundleIconFiles - AppIconYellow76x76 - AppIconYellow83.5x83.5 + AppIcon-yellow @@ -157,6 +142,7 @@ ddgFavorites ddgQuickLink ddgAddFavorite + ddgOpenPasswords http https @@ -244,5 +230,7 @@ https://duckduckgo.com UIViewControllerBasedStatusBarAppearance + VAULT_APP_GROUP + $(AppIdentifierPrefix)$(VAULT_APP_GROUP) diff --git a/DuckDuckGo/LargeOmniBarState.swift b/DuckDuckGo/LargeOmniBarState.swift index f80578a679..9b7a98edab 100644 --- a/DuckDuckGo/LargeOmniBarState.swift +++ b/DuckDuckGo/LargeOmniBarState.swift @@ -27,7 +27,7 @@ struct LargeOmniBarState { let showBackButton: Bool = true let showForwardButton: Bool = true let showBookmarksButton: Bool = true - let showShareButton: Bool = false + let showAccessoryButton: Bool = false let clearTextOnStart = true let allowsTrackersAnimation = false let showPrivacyIcon = false @@ -68,7 +68,7 @@ struct LargeOmniBarState { let showBackButton: Bool = true let showForwardButton: Bool = true let showBookmarksButton: Bool = true - let showShareButton: Bool = false + let showAccessoryButton: Bool = false let clearTextOnStart = false let allowsTrackersAnimation = false let showPrivacyIcon = false @@ -101,7 +101,7 @@ struct LargeOmniBarState { let showBackButton: Bool = true let showForwardButton: Bool = true let showBookmarksButton: Bool = true - let showShareButton: Bool = false + let showAccessoryButton: Bool = false let clearTextOnStart = true let allowsTrackersAnimation = false let showSearchLoupe = true @@ -134,7 +134,7 @@ struct LargeOmniBarState { let showBackButton: Bool = true let showForwardButton: Bool = true let showBookmarksButton: Bool = true - let showShareButton: Bool = true + let showAccessoryButton: Bool = true let clearTextOnStart = true let allowsTrackersAnimation = false let showPrivacyIcon = false @@ -167,7 +167,7 @@ struct LargeOmniBarState { let showBackButton: Bool = true let showForwardButton: Bool = true let showBookmarksButton: Bool = true - let showShareButton: Bool = true + let showAccessoryButton: Bool = true let clearTextOnStart = false let allowsTrackersAnimation = false let showPrivacyIcon = false @@ -200,7 +200,7 @@ struct LargeOmniBarState { let showBackButton: Bool = true let showForwardButton: Bool = true let showBookmarksButton: Bool = true - let showShareButton: Bool = true + let showAccessoryButton: Bool = true let clearTextOnStart = false let allowsTrackersAnimation = true let showSearchLoupe = false diff --git a/DuckDuckGo/MainViewController+AddFavoriteFlow.swift b/DuckDuckGo/MainViewController+AddFavoriteFlow.swift index 78e47a03c6..8231cc52bc 100644 --- a/DuckDuckGo/MainViewController+AddFavoriteFlow.swift +++ b/DuckDuckGo/MainViewController+AddFavoriteFlow.swift @@ -48,6 +48,7 @@ extension MainViewController { guard canDisplayAddFavoriteVisualIndicator, let window = view.window, presentedViewController == nil else { return } DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { + guard self.canDisplayAddFavoriteVisualIndicator else { return } ViewHighlighter.hideAll() ViewHighlighter.showIn(window, focussedOnView: self.presentedMenuButton) } diff --git a/DuckDuckGo/MainViewController+Segues.swift b/DuckDuckGo/MainViewController+Segues.swift index 5eafe2cdea..2ca6573a8a 100644 --- a/DuckDuckGo/MainViewController+Segues.swift +++ b/DuckDuckGo/MainViewController+Segues.swift @@ -98,25 +98,6 @@ extension MainViewController { } } - func segueToActionSheetDaxDialogWithSpec(_ spec: DaxDialogs.ActionSheetSpec) { - Logger.lifecycle.debug(#function) - hideAllHighlightsIfNeeded() - - if spec == DaxDialogs.ActionSheetSpec.fireButtonEducation { - ViewHighlighter.hideAll() - } - - let storyboard = UIStoryboard(name: "DaxOnboarding", bundle: nil) - let controller = storyboard.instantiateViewController(identifier: "ActionSheetDaxDialog", creator: { coder in - ActionSheetDaxDialogViewController(coder: coder) - }) - controller.spec = spec - controller.delegate = self - controller.modalTransitionStyle = .crossDissolve - controller.modalPresentationStyle = .overFullScreen - present(controller, animated: true) - } - func segueToReportBrokenSite(entryPoint: PrivacyDashboardEntryPoint = .report) { Logger.lifecycle.debug(#function) hideAllHighlightsIfNeeded() @@ -296,6 +277,9 @@ extension MainViewController { fireproofing: fireproofing, websiteDataManager: websiteDataManager) + let aiChatSettings = AIChatSettings(privacyConfigurationManager: ContentBlocking.shared.privacyConfigurationManager, + internalUserDecider: AppDependencyProvider.shared.internalUserDecider) + let settingsViewModel = SettingsViewModel(legacyViewProvider: legacyViewProvider, subscriptionManager: AppDependencyProvider.shared.subscriptionManager, subscriptionFeatureAvailability: subscriptionFeatureAvailability, @@ -304,7 +288,8 @@ extension MainViewController { historyManager: historyManager, syncPausedStateManager: syncPausedStateManager, privacyProDataReporter: privacyProDataReporter, - textZoomCoordinator: textZoomCoordinator) + textZoomCoordinator: textZoomCoordinator, + aiChatSettings: aiChatSettings) Pixel.fire(pixel: .settingsPresented) if let navigationController = self.presentedViewController as? UINavigationController, diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index 506727e17f..35c07f3865 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -38,33 +38,34 @@ import Onboarding import os.log import PageRefreshMonitor import BrokenSitePrompt +import AIChat class MainViewController: UIViewController { - + override var preferredStatusBarStyle: UIStatusBarStyle { return ThemeManager.shared.currentTheme.statusBarStyle } - + override var preferredScreenEdgesDeferringSystemGestures: UIRectEdge { let isIPad = UIDevice.current.userInterfaceIdiom == .pad - + return isIPad ? [.left, .right] : [] } - + weak var findInPageView: FindInPageView! weak var findInPageHeightLayoutConstraint: NSLayoutConstraint! weak var findInPageBottomLayoutConstraint: NSLayoutConstraint! - + weak var notificationView: UIView? var chromeManager: BrowserChromeManager! - + var allowContentUnderflow = false { didSet { viewCoordinator.constraints.contentContainerTop.constant = allowContentUnderflow ? contentUnderflow : 0 } } - + var contentUnderflow: CGFloat { return 3 + (allowContentUnderflow ? -viewCoordinator.navigationBarContainer.frame.size.height : 0) } @@ -83,7 +84,7 @@ class MainViewController: UIViewController { var newTabPageViewController: NewTabPageViewController? var tabsBarController: TabsBarViewController? var suggestionTrayController: SuggestionTrayViewController? - + let homePageConfiguration: HomePageConfiguration let homeTabManager: NewTabPageManager let tabManager: TabManager @@ -123,6 +124,7 @@ class MainViewController: UIViewController { private let tunnelDefaults = UserDefaults.networkProtectionGroupDefaults private var vpnCancellables = Set() private var feedbackCancellable: AnyCancellable? + private var aiChatCancellables = Set() let subscriptionFeatureAvailability: SubscriptionFeatureAvailability private let subscriptionCookieManager: SubscriptionCookieManaging @@ -137,7 +139,7 @@ class MainViewController: UIViewController { viewModel.favoritesDisplayMode = appSettings.favoritesDisplayMode return viewModel }() - + weak var tabSwitcherController: TabSwitcherViewController? var tabSwitcherButton: TabSwitcherButton! @@ -146,26 +148,26 @@ class MainViewController: UIViewController { var presentedMenuButton: MenuButton { AppWidthObserver.shared.isLargeWidth ? viewCoordinator.omniBar.menuButtonContent : menuButton } - + let gestureBookmarksButton = GestureToolbarButton() - + private lazy var fireButtonAnimator: FireButtonAnimator = FireButtonAnimator(appSettings: appSettings) - + let bookmarksCachingSearch: BookmarksCachingSearch - + lazy var tabSwitcherTransition = TabSwitcherTransitionDelegate() var currentTab: TabViewController? { return tabManager.current(createIfNeeded: false) } - + var searchBarRect: CGRect { let view = UIApplication.shared.firstKeyWindow?.rootViewController?.view return viewCoordinator.omniBar.searchContainer.convert(viewCoordinator.omniBar.searchContainer.bounds, to: view) } - + var keyModifierFlags: UIKeyModifierFlags? var showKeyboardAfterFireButton: DispatchWorkItem? - + // Skip SERP flow (focusing on autocomplete logic) and prepare for new navigation when selecting search bar private var skipSERPFlow = true @@ -176,7 +178,7 @@ class MainViewController: UIViewController { required init?(coder: NSCoder) { fatalError("Use init?(code:") } - + let fireproofing: Fireproofing let websiteDataManager: WebsiteDataManaging let textZoomCoordinator: TextZoomCoordinating @@ -186,6 +188,22 @@ class MainViewController: UIViewController { var appDidFinishLaunchingStartTime: CFAbsoluteTime? + private lazy var aiChatViewController: AIChatViewController = { + let settings = AIChatSettings(privacyConfigurationManager: ContentBlocking.shared.privacyConfigurationManager, + internalUserDecider: AppDependencyProvider.shared.internalUserDecider) + let aiChatViewController = AIChatViewController(settings: settings, + webViewConfiguration: WKWebViewConfiguration.persistent()) + aiChatViewController.delegate = self + return aiChatViewController + }() + + private var omnibarAccessoryHandler: OmnibarAccessoryHandler = { + let settings = AIChatSettings(privacyConfigurationManager: ContentBlocking.shared.privacyConfigurationManager, + internalUserDecider: AppDependencyProvider.shared.internalUserDecider) + + return OmnibarAccessoryHandler(settings: settings) + }() + init( bookmarksDatabase: CoreDataDatabase, bookmarksDatabaseCleaner: BookmarkDatabaseCleaner, @@ -319,6 +337,7 @@ class MainViewController: UIViewController { subscribeToSettingsDeeplinkNotifications() subscribeToNetworkProtectionEvents() subscribeToUnifiedFeedbackNotifications() + subscribeToAIChatSettingsEvents() findInPageView.delegate = self findInPageBottomLayoutConstraint.constant = 0 @@ -351,6 +370,7 @@ class MainViewController: UIViewController { let launchTime = CFAbsoluteTimeGetCurrent() - appDidFinishLaunchingStartTime Pixel.fire(pixel: .appDidShowUITime(time: Pixel.Event.BucketAggregation(number: launchTime)), withAdditionalParameters: [PixelParameters.time: String(launchTime)]) + self.appDidFinishLaunchingStartTime = nil /// We only want this pixel to be fired once } } @@ -380,7 +400,8 @@ class MainViewController: UIViewController { swipeTabsCoordinator = SwipeTabsCoordinator(coordinator: viewCoordinator, tabPreviewsSource: previewsSource, appSettings: appSettings, - voiceSearchHelper: voiceSearchHelper) { [weak self] in + voiceSearchHelper: voiceSearchHelper, + omnibarAccessoryHandler: omnibarAccessoryHandler) { [weak self] in guard $0 != self?.tabManager.model.currentIndex else { return } @@ -465,12 +486,6 @@ class MainViewController: UIViewController { } func startAddFavoriteFlow() { - // Disable add favourite flow when new onboarding experiment is running and open a new tab. - guard contextualOnboardingLogic.canEnableAddFavoriteFlow() else { - newTab() - return - } - contextualOnboardingLogic.enableAddFavoriteFlow() if tutorialSettings.hasSeenOnboarding { newTab() @@ -509,6 +524,7 @@ class MainViewController: UIViewController { var keyboardShowing = false + private var didSendGestureDismissPixel: Bool = false @objc private func keyboardDidShow() { @@ -517,14 +533,16 @@ class MainViewController: UIViewController { @objc private func keyboardWillHide() { - if newTabPageViewController?.isDragging == true, keyboardShowing { + if !didSendGestureDismissPixel, newTabPageViewController?.isDragging == true, keyboardShowing { Pixel.fire(pixel: .addressBarGestureDismiss) + didSendGestureDismissPixel = true } } @objc private func keyboardDidHide() { keyboardShowing = false + didSendGestureDismissPixel = false } private func registerForPageRefreshPatterns() { @@ -865,18 +883,10 @@ class MainViewController: UIViewController { hideNotificationBarIfBrokenSitePromptShown() wakeLazyFireButtonAnimator() - if variantManager.isContextualDaxDialogsEnabled { - // Dismiss dax dialog and pulse animation when the user taps on the Fire Button. - currentTab?.dismissContextualDaxFireDialog() - ViewHighlighter.hideAll() - showClearDataAlert() - } else { - if let spec = DaxDialogs.shared.fireButtonEducationMessage() { - segueToActionSheetDaxDialogWithSpec(spec) - } else { - showClearDataAlert() - } - } + // Dismiss dax dialog and pulse animation when the user taps on the Fire Button. + currentTab?.dismissContextualDaxFireDialog() + ViewHighlighter.hideAll() + showClearDataAlert() performCancel() } @@ -1121,6 +1131,8 @@ class MainViewController: UIViewController { viewCoordinator.omniBar.resetPrivacyIcon(for: tab.url) } + viewCoordinator.omniBar.accessoryType = omnibarAccessoryHandler.omnibarAccessory(for: tab.url) + viewCoordinator.omniBar.startBrowsing() } @@ -1532,6 +1544,15 @@ class MainViewController: UIViewController { .store(in: &settingsDeepLinkcancellables) } + private func subscribeToAIChatSettingsEvents() { + NotificationCenter.default.publisher(for: .aiChatSettingsChanged) + .receive(on: DispatchQueue.main) + .sink { [weak self] _ in + self?.refreshOmniBar() + } + .store(in: &aiChatCancellables) + } + private func subscribeToNetworkProtectionEvents() { NotificationCenter.default.publisher(for: .accountDidSignIn) .receive(on: DispatchQueue.main) @@ -1689,7 +1710,19 @@ class MainViewController: UIViewController { Pixel.fire(pixel: pixel, withAdditionalParameters: pixelParameters, includedParameters: [.atb]) } - + + private func openAIChat() { + let logoImage = UIImage(named: "Logo") + let title = UserText.aiChatTitle + + let roundedPageSheet = RoundedPageSheetContainerViewController( + contentViewController: aiChatViewController, + logoImage: logoImage, + title: title, + allowedOrientation: .portrait) + + present(roundedPageSheet, animated: true, completion: nil) + } } extension MainViewController: FindInPageDelegate { @@ -2048,17 +2081,25 @@ extension MainViewController: OmniBarDelegate { hideNotificationBarIfBrokenSitePromptShown(afterRefresh: true) } - func onSharePressed() { + func onAccessoryPressed(accessoryType: OmniBar.AccessoryType) { hideSuggestionTray() guard let link = currentTab?.link else { return } - currentTab?.onShareAction(forLink: link, fromView: viewCoordinator.omniBar.shareButton) + + switch accessoryType { + case .chat: + openAIChat() + Pixel.fire(pixel: .openAIChatFromAddressBar) + case .share: + Pixel.fire(pixel: .addressBarShare) + currentTab?.onShareAction(forLink: link, fromView: viewCoordinator.omniBar.accessoryButton) + } } - func onShareLongPressed() { + func onAccessoryLongPressed(accessoryType: OmniBar.AccessoryType) { if featureFlagger.isFeatureOn(.debugMenu) || isDebugBuild { segueToDebugSettings() } else { - onSharePressed() + onAccessoryPressed(accessoryType: accessoryType) } } @@ -2348,6 +2389,10 @@ extension MainViewController: TabDelegate { segueToReportBrokenSite(entryPoint: .toggleReport(completionHandler: completionHandler)) } + func tabDidRequestAIChat(tab: TabViewController) { + openAIChat() + } + func tabDidRequestBookmarks(tab: TabViewController) { Pixel.fire(pixel: .bookmarksButtonPressed, withAdditionalParameters: [PixelParameters.originatedFromMenu: "1"]) @@ -2565,8 +2610,15 @@ extension MainViewController: TabSwitcherButtonDelegate { } func showTabSwitcher(_ button: TabSwitcherButton) { - Pixel.fire(pixel: .tabBarTabSwitcherPressed) - DailyPixel.fireDaily(.tabSwitcherOpenDaily, withAdditionalParameters: TabSwitcherOpenDailyPixel().parameters(with: tabManager.model.tabs)) + Pixel.fire(pixel: .tabBarTabSwitcherOpened) + DailyPixel.fireDaily(.tabSwitcherOpenedDaily, withAdditionalParameters: TabSwitcherOpenDailyPixel().parameters(with: tabManager.model.tabs)) + if currentTab?.url?.isDuckDuckGoSearch == true { + Pixel.fire(pixel: .tabSwitcherOpenedFromSerp) + } else if currentTab?.url != nil { + Pixel.fire(pixel: .tabSwitcherOpenedFromWebsite) + } else { + Pixel.fire(pixel: .tabSwitcherOpenedFromNewTabPage) + } performCancel() showTabSwitcher() @@ -2734,9 +2786,8 @@ extension MainViewController: AutoClearWorker { self.showKeyboardAfterFireButton = showKeyboardAfterFireButton } - if self.variantManager.isContextualDaxDialogsEnabled { - DaxDialogs.shared.clearedBrowserData() - } + DaxDialogs.shared.clearedBrowserData() + } } @@ -2932,3 +2983,11 @@ extension MainViewController: AutofillLoginSettingsListViewControllerDelegate { controller.dismiss(animated: true) } } + +// MARK: - AIChatViewControllerDelegate +extension MainViewController: AIChatViewControllerDelegate { + func aiChatViewController(_ viewController: AIChatViewController, didRequestToLoad url: URL) { + loadUrlInNewTab(url, inheritedAttribution: nil) + viewController.dismiss(animated: true) + } +} diff --git a/DuckDuckGo/NewTabPageView.swift b/DuckDuckGo/NewTabPageView.swift index 219ddd9c27..0add43c1c9 100644 --- a/DuckDuckGo/NewTabPageView.swift +++ b/DuckDuckGo/NewTabPageView.swift @@ -76,7 +76,7 @@ struct NewTabPageView: View { .simultaneousGesture( DragGesture() .onChanged({ value in - if value.translation.height > 0 { + if value.translation.height != 0 { viewModel.beginDragging() } }) @@ -154,6 +154,8 @@ private extension NewTabPageView { EmptyView() } } + // Prevent recreating geomery reader when keyboard is shown/hidden. + .ignoresSafeArea(.keyboard) } @ViewBuilder diff --git a/DuckDuckGo/NewTabPageViewController.swift b/DuckDuckGo/NewTabPageViewController.swift index 02c0009e75..a9ebe988a0 100644 --- a/DuckDuckGo/NewTabPageViewController.swift +++ b/DuckDuckGo/NewTabPageViewController.swift @@ -210,11 +210,6 @@ final class NewTabPageViewController: UIHostingController, NewTabPage { self.launchNewSearch() } } - - if !variantManager.isContextualDaxDialogsEnabled { - // In the new onboarding this gets called twice (viewDidAppear in Tab) which then reset the spec to nil. - presentNextDaxDialog() - } } func dismiss() { @@ -239,11 +234,7 @@ final class NewTabPageViewController: UIHostingController, NewTabPage { // MARK: - Onboarding private func presentNextDaxDialog() { - if variantManager.isContextualDaxDialogsEnabled { - showNextDaxDialogNew(dialogProvider: newTabDialogTypeProvider, factory: newTabDialogFactory) - } else { - showNextDaxDialog(dialogProvider: newTabDialogTypeProvider) - } + showNextDaxDialogNew(dialogProvider: newTabDialogTypeProvider, factory: newTabDialogFactory) } // MARK: - Private @@ -280,37 +271,6 @@ extension NewTabPageViewController: HomeScreenTransitionSource { extension NewTabPageViewController { - func showNextDaxDialog(dialogProvider: NewTabDialogSpecProvider) { - guard let spec = dialogProvider.nextHomeScreenMessage() else { return } - guard !isDaxDialogVisible else { return } - guard let daxDialogViewController = daxDialogViewController else { return } - - newTabPageViewModel.startOnboarding() - - daxDialogViewController.view.isHidden = false - daxDialogViewController.view.alpha = 0.0 - - daxDialogViewController.loadViewIfNeeded() - daxDialogViewController.message = spec.message - daxDialogViewController.accessibleMessage = spec.accessibilityLabel - - if spec == .initial { - UniquePixel.fire(pixel: .onboardingContextualTryVisitSiteUnique, includedParameters: [.appVersion, .atb]) - } - - view.addGestureRecognizer(daxDialogViewController.tapToCompleteGestureRecognizer) - - daxDialogHeightConstraint?.constant = daxDialogViewController.calculateHeight() - - DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { - UIView.animate(withDuration: 0.4, animations: { - daxDialogViewController.view.alpha = 1.0 - }, completion: { _ in - daxDialogViewController.start() - }) - } - } - func showNextDaxDialogNew(dialogProvider: NewTabDialogSpecProvider, factory: any NewTabDaxDialogProvider) { dismissHostingController(didFinishNTPOnboarding: false) diff --git a/DuckDuckGo/NewTabPageViewModel.swift b/DuckDuckGo/NewTabPageViewModel.swift index 6cb387b402..5f76ce83b9 100644 --- a/DuckDuckGo/NewTabPageViewModel.swift +++ b/DuckDuckGo/NewTabPageViewModel.swift @@ -19,6 +19,7 @@ import Foundation import Core +import BrowserServicesKit final class NewTabPageViewModel: ObservableObject { @@ -39,6 +40,9 @@ final class NewTabPageViewModel: ObservableObject { isIntroMessageVisible = introDataStorage.newTabPageIntroMessageEnabled ?? false isOnboarding = false isShowingSettings = false + + // This is just temporarily here to run an A/A test to check the new experiment framework works as expected + _ = AppDependencyProvider.shared.featureFlagger.getCohortIfEnabled(for: CredentialsSavingFlag()) } func introMessageDisplayed() { @@ -78,3 +82,19 @@ final class NewTabPageViewModel: ObservableObject { isDragging = false } } + +// This is just temporarily here to run an A/A test to check the new experiment framework works as expected +public struct CredentialsSavingFlag: FeatureFlagExperimentDescribing { + public init() {} + + public typealias CohortType = Cohort + + public var rawValue = "credentialSaving" + + public var source: FeatureFlagSource = .remoteReleasable(.subfeature(ExperimentTestSubfeatures.experimentTestAA)) + + public enum Cohort: String, FlagCohort { + case control + case blue + } +} diff --git a/DuckDuckGo/OmniBar.swift b/DuckDuckGo/OmniBar.swift index fda9757981..6f20b39507 100644 --- a/DuckDuckGo/OmniBar.swift +++ b/DuckDuckGo/OmniBar.swift @@ -33,6 +33,11 @@ public enum OmniBarIcon: String { class OmniBar: UIView { + enum AccessoryType { + case share + case chat + } + public static let didLayoutNotification = Notification.Name("com.duckduckgo.app.OmniBarDidLayout") @IBOutlet weak var searchLoupe: UIView! @@ -55,8 +60,8 @@ class OmniBar: UIView { @IBOutlet weak var bookmarksButton: UIButton! @IBOutlet weak var backButton: UIButton! @IBOutlet weak var forwardButton: UIButton! - @IBOutlet weak var shareButton: UIButton! - + @IBOutlet weak var accessoryButton: UIButton! + private(set) var menuButtonContent = MenuButton() // Don't use weak because adding/removing them causes them to go away @@ -71,6 +76,16 @@ class OmniBar: UIView { weak var omniDelegate: OmniBarDelegate? fileprivate var state: OmniBarState! + var accessoryType: AccessoryType = .share { + didSet { + switch accessoryType { + case .chat: + accessoryButton.setImage(UIImage(named: "AIChat-24"), for: .normal) + case .share: + accessoryButton.setImage(UIImage(named: "Share-24"), for: .normal) + } + } + } private var privacyIconAndTrackersAnimator = PrivacyIconAndTrackersAnimator() private var notificationAnimator = OmniBarNotificationAnimator() @@ -104,7 +119,7 @@ class OmniBar: UIView { configureSettingsLongPressButton() configureShareLongPressButton() registerNotifications() - + configureSeparator() configureEditingMenu() enableInteractionsWithPointer() @@ -123,7 +138,7 @@ class OmniBar: UIView { private func configureShareLongPressButton() { let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(handleShareLongPress(_:))) longPressGesture.minimumPressDuration = 0.7 - shareButton.addGestureRecognizer(longPressGesture) + accessoryButton.addGestureRecognizer(longPressGesture) } @objc private func handleSettingsLongPress(_ gesture: UILongPressGestureRecognizer) { @@ -134,7 +149,7 @@ class OmniBar: UIView { @objc private func handleShareLongPress(_ gesture: UILongPressGestureRecognizer) { if gesture.state == .began { - omniDelegate?.onShareLongPressed() + omniDelegate?.onAccessoryLongPressed(accessoryType: accessoryType) } } @@ -156,7 +171,7 @@ class OmniBar: UIView { settingsButton.isPointerInteractionEnabled = true cancelButton.isPointerInteractionEnabled = true bookmarksButton.isPointerInteractionEnabled = true - shareButton.isPointerInteractionEnabled = true + accessoryButton.isPointerInteractionEnabled = true menuButton.isPointerInteractionEnabled = true refreshButton.isPointerInteractionEnabled = true @@ -265,7 +280,7 @@ class OmniBar: UIView { public func hidePrivacyIcon() { privacyInfoContainer.privacyIcon.isHidden = true } - + public func resetPrivacyIcon(for url: URL?) { cancelAllAnimations() privacyInfoContainer.privacyIcon.isHidden = false @@ -388,7 +403,7 @@ class OmniBar: UIView { setVisibility(backButton, hidden: !state.showBackButton) setVisibility(forwardButton, hidden: !state.showForwardButton) setVisibility(bookmarksButton, hidden: !state.showBookmarksButton) - setVisibility(shareButton, hidden: !state.showShareButton) + setVisibility(accessoryButton, hidden: !state.showAccessoryButton) searchContainerCenterConstraint.isActive = state.hasLargeWidth searchContainerMaxWidthConstraint.isActive = state.hasLargeWidth @@ -529,10 +544,9 @@ class OmniBar: UIView { withAdditionalParameters: [PixelParameters.originatedFromMenu: "0"]) omniDelegate?.onBookmarksPressed() } - - @IBAction func onSharePressed(_ sender: Any) { - Pixel.fire(pixel: .addressBarShare) - omniDelegate?.onSharePressed() + + @IBAction func onAccessoryPressed(_ sender: Any) { + omniDelegate?.onAccessoryPressed(accessoryType: accessoryType) } func enterPhoneState() { diff --git a/DuckDuckGo/OmniBarDelegate.swift b/DuckDuckGo/OmniBarDelegate.swift index d5122cac32..b7e2daf9ed 100644 --- a/DuckDuckGo/OmniBarDelegate.swift +++ b/DuckDuckGo/OmniBarDelegate.swift @@ -59,9 +59,9 @@ protocol OmniBarDelegate: AnyObject { func onForwardPressed() - func onSharePressed() + func onAccessoryPressed(accessoryType: OmniBar.AccessoryType) - func onShareLongPressed() + func onAccessoryLongPressed(accessoryType: OmniBar.AccessoryType) func onTextFieldWillBeginEditing(_ omniBar: OmniBar, tapped: Bool) @@ -92,8 +92,8 @@ extension OmniBarDelegate { } - func onShareLongPressed() { - + func onAccessoryLongPressed(accessoryType: OmniBar.AccessoryType) { + } func onBookmarksPressed() { @@ -124,9 +124,9 @@ extension OmniBarDelegate { } - func onSharePressed() { + func onAccessoryPressed(accessoryType: OmniBar.AccessoryType) { } - + func onBackPressed() { } diff --git a/DuckDuckGo/OmniBarState.swift b/DuckDuckGo/OmniBarState.swift index 6dbba113b3..a8aac646fe 100644 --- a/DuckDuckGo/OmniBarState.swift +++ b/DuckDuckGo/OmniBarState.swift @@ -28,7 +28,7 @@ protocol OmniBarState: CustomStringConvertible { var showBackButton: Bool { get } var showForwardButton: Bool { get } var showBookmarksButton: Bool { get } - var showShareButton: Bool { get } + var showAccessoryButton: Bool { get } var clearTextOnStart: Bool { get } var allowsTrackersAnimation: Bool { get } diff --git a/DuckDuckGo/OnboardingDebugView.swift b/DuckDuckGo/OnboardingDebugView.swift index f6528aea25..93c4ad3ba2 100644 --- a/DuckDuckGo/OnboardingDebugView.swift +++ b/DuckDuckGo/OnboardingDebugView.swift @@ -32,19 +32,6 @@ struct OnboardingDebugView: View { var body: some View { List { - Section { - Toggle( - isOn: $viewModel.isOnboardingHighlightsLocalFlagEnabled, - label: { - Text(verbatim: "Onboarding Highlights local setting enabled") - } - ) - } header: { - Text(verbatim: "Onboarding Higlights settings") - } footer: { - Text(verbatim: "Requires internal user flag set to have an effect.") - } - Section { Picker( selection: $viewModel.onboardingAddToDockLocalFlagState, @@ -57,10 +44,11 @@ struct OnboardingDebugView: View { Text(verbatim: "Onboarding Add to Dock local setting enabled") } ) + .disabled(!viewModel.isIphone) } header: { Text(verbatim: "Onboarding Add to Dock settings") } footer: { - Text(verbatim: "Requires internal user flag set to have an effect.") + Text(verbatim: viewModel.isIphone ? "Requires internal user flag set to have an effect." : "Requires internal user flag set to have an effect. iPhone only feature.") } Section { @@ -77,8 +65,7 @@ struct OnboardingDebugView: View { Section { Button(action: newOnboardingIntroStartAction, label: { - let onboardingType = viewModel.isOnboardingHighlightsLocalFlagEnabled ? "Highlights" : "" - Text(verbatim: "Preview New Onboarding Intro \(onboardingType)") + Text(verbatim: "Preview Onboarding Intro") }) } } @@ -86,11 +73,6 @@ struct OnboardingDebugView: View { } final class OnboardingDebugViewModel: ObservableObject { - @Published var isOnboardingHighlightsLocalFlagEnabled: Bool { - didSet { - manager.isOnboardingHighlightsLocalFlagEnabled = isOnboardingHighlightsLocalFlagEnabled - } - } @Published var onboardingAddToDockLocalFlagState: OnboardingAddToDockState { didSet { @@ -98,19 +80,23 @@ final class OnboardingDebugViewModel: ObservableObject { } } - private let manager: OnboardingHighlightsDebugging & OnboardingAddToDockDebugging + private let manager: OnboardingAddToDockDebugging private var settings: DaxDialogsSettings + let isIphone: Bool - init(manager: OnboardingHighlightsDebugging & OnboardingAddToDockDebugging = OnboardingManager(), settings: DaxDialogsSettings = DefaultDaxDialogsSettings()) { + init( + manager: OnboardingAddToDockDebugging = OnboardingManager(), + settings: DaxDialogsSettings = DefaultDaxDialogsSettings(), + isIphone: Bool = UIDevice.current.userInterfaceIdiom == .phone + ) { self.manager = manager self.settings = settings - isOnboardingHighlightsLocalFlagEnabled = manager.isOnboardingHighlightsLocalFlagEnabled + self.isIphone = isIphone onboardingAddToDockLocalFlagState = manager.addToDockLocalFlagState } func resetDaxDialogs() { settings.isDismissed = false - settings.homeScreenMessagesSeen = 0 settings.browsingAfterSearchShown = false settings.browsingWithTrackersShown = false settings.browsingWithoutTrackersShown = false diff --git a/DuckDuckGo/OnboardingExperiment/AddToDock/OnboardingView+AddToDockContent.swift b/DuckDuckGo/OnboardingExperiment/AddToDock/OnboardingView+AddToDockContent.swift index c1311dec7f..af74e72b42 100644 --- a/DuckDuckGo/OnboardingExperiment/AddToDock/OnboardingView+AddToDockContent.swift +++ b/DuckDuckGo/OnboardingExperiment/AddToDock/OnboardingView+AddToDockContent.swift @@ -22,32 +22,17 @@ import Onboarding extension OnboardingView { - struct AddToDockPromoContentState { - var animateTitle = true - var animateMessage = false - var showContent = false - } - struct AddToDockPromoContent: View { @State private var showAddToDockTutorial = false - private var animateTitle: Binding - private var animateMessage: Binding - private var showContent: Binding private let showTutorialAction: () -> Void private let dismissAction: (_ fromAddToDock: Bool) -> Void init( - animateTitle: Binding = .constant(true), - animateMessage: Binding = .constant(true), - showContent: Binding = .constant(false), showTutorialAction: @escaping () -> Void, dismissAction: @escaping (_ fromAddToDock: Bool) -> Void ) { - self.animateTitle = animateTitle - self.animateMessage = animateMessage - self.showContent = showContent self.showTutorialAction = showTutorialAction self.dismissAction = dismissAction } diff --git a/DuckDuckGo/OnboardingExperiment/AddToDock/VideoPlayer/VideoPlayerView.swift b/DuckDuckGo/OnboardingExperiment/AddToDock/VideoPlayer/VideoPlayerView.swift index c840bb7555..ea8fccf72a 100644 --- a/DuckDuckGo/OnboardingExperiment/AddToDock/VideoPlayer/VideoPlayerView.swift +++ b/DuckDuckGo/OnboardingExperiment/AddToDock/VideoPlayer/VideoPlayerView.swift @@ -24,7 +24,7 @@ struct VideoPlayerView: View { @ObservedObject private var model: VideoPlayerViewModel - private var isPlaying: Binding + private var isPlaying: Binding init(model: VideoPlayerViewModel, isPlaying: Binding = .constant(true)) { self.model = model @@ -41,6 +41,12 @@ struct VideoPlayerView: View { model.pause() } } + .onReceive(NotificationCenter.default.publisher(for: UIApplication.didEnterBackgroundNotification), perform: { _ in + isPlaying.wrappedValue = false + }) + .onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)) { _ in + isPlaying.wrappedValue = true + } } } diff --git a/DuckDuckGo/OnboardingExperiment/AddressBarPositionPicker/OnboardingAddressBarPositionPickerViewModel.swift b/DuckDuckGo/OnboardingExperiment/AddressBarPositionPicker/OnboardingAddressBarPositionPickerViewModel.swift index 595fb5c061..5bfe88fcae 100644 --- a/DuckDuckGo/OnboardingExperiment/AddressBarPositionPicker/OnboardingAddressBarPositionPickerViewModel.swift +++ b/DuckDuckGo/OnboardingExperiment/AddressBarPositionPicker/OnboardingAddressBarPositionPickerViewModel.swift @@ -74,22 +74,22 @@ private extension AddressBarPosition { var titleAndMessage: (title: NSAttributedString, message: String) { switch self { case .top: - let firstPart = NSAttributedString(string: UserText.HighlightsOnboardingExperiment.AddressBarPosition.topTitle) + let firstPart = NSAttributedString(string: UserText.Onboarding.AddressBarPosition.topTitle) .withFont(UIFont.daxBodyBold()) .withTextColor(UIColor.label) - let secondPart = NSAttributedString(string: UserText.HighlightsOnboardingExperiment.AddressBarPosition.defaultOption) + let secondPart = NSAttributedString(string: UserText.Onboarding.AddressBarPosition.defaultOption) .withFont(UIFont.daxBodyRegular()) .withTextColor(UIColor.secondaryLabel) return ( firstPart + " " + secondPart, - UserText.HighlightsOnboardingExperiment.AddressBarPosition.topMessage + UserText.Onboarding.AddressBarPosition.topMessage ) case .bottom: return ( - NSAttributedString(string: UserText.HighlightsOnboardingExperiment.AddressBarPosition.bottomTitle) + NSAttributedString(string: UserText.Onboarding.AddressBarPosition.bottomTitle) .withFont(UIFont.daxBodyBold()), - UserText.HighlightsOnboardingExperiment.AddressBarPosition.bottomMessage + UserText.Onboarding.AddressBarPosition.bottomMessage ) } } diff --git a/DuckDuckGo/OnboardingExperiment/AppIconPicker/AppIconPicker.swift b/DuckDuckGo/OnboardingExperiment/AppIconPicker/AppIconPicker.swift index 2d1dd5ea4d..6fbb17708a 100644 --- a/DuckDuckGo/OnboardingExperiment/AppIconPicker/AppIconPicker.swift +++ b/DuckDuckGo/OnboardingExperiment/AppIconPicker/AppIconPicker.swift @@ -30,8 +30,6 @@ private enum Metrics { } struct AppIconPicker: View { - @Environment(\.colorScheme) private var color - @StateObject private var viewModel = AppIconPickerViewModel() let layout = [GridItem(.adaptive(minimum: Metrics.iconSize), spacing: Metrics.spacing, alignment: .leading)] diff --git a/DuckDuckGo/OnboardingExperiment/Background/OnboardingBackground.swift b/DuckDuckGo/OnboardingExperiment/Background/OnboardingBackground.swift index 7e5b6fb531..b1dcdc5ddd 100644 --- a/DuckDuckGo/OnboardingExperiment/Background/OnboardingBackground.swift +++ b/DuckDuckGo/OnboardingExperiment/Background/OnboardingBackground.swift @@ -20,7 +20,6 @@ import SwiftUI struct OnboardingBackground: View { - @Environment(\.onboardingGradientType) private var gradientType @Environment(\.verticalSizeClass) private var vSizeClass @Environment(\.horizontalSizeClass) private var hSizeClass @Environment(\.colorScheme) private var colorScheme @@ -35,7 +34,7 @@ struct OnboardingBackground: View { .opacity(colorScheme == .light ? 0.5 : 0.3) .frame(width: proxy.size.width, height: proxy.size.height, alignment: alignment) .background( - OnboardingGradientView(type: gradientType) + OnboardingGradientView() .ignoresSafeArea() ) } @@ -48,24 +47,10 @@ private enum Metrics { #Preview("Light Mode") { OnboardingBackground() - .onboardingGradient(.default) - .preferredColorScheme(.light) -} - -#Preview("Dark Mode") { - OnboardingBackground() - .onboardingGradient(.default) - .preferredColorScheme(.dark) -} - -#Preview("Light Mode - Highlights") { - OnboardingBackground() - .onboardingGradient(.highlights) .preferredColorScheme(.light) } #Preview("Dark Mode - Highlights") { OnboardingBackground() - .onboardingGradient(.highlights) .preferredColorScheme(.dark) } diff --git a/DuckDuckGo/OnboardingExperiment/Background/OnboardingGradient.swift b/DuckDuckGo/OnboardingExperiment/Background/OnboardingGradient.swift index c5b6fff5a6..d32dfa3202 100644 --- a/DuckDuckGo/OnboardingExperiment/Background/OnboardingGradient.swift +++ b/DuckDuckGo/OnboardingExperiment/Background/OnboardingGradient.swift @@ -23,57 +23,22 @@ import Onboarding struct OnboardingGradientView: View { @Environment(\.colorScheme) private var colorScheme - private let type: OnboardingGradientType - - init(type: OnboardingGradientType) { - self.type = type - } - var body: some View { - switch (type, colorScheme) { - case (.default, .light): - linearLightGradient - case (.default, .dark): - linearDarkGradient - case (.highlights, _): - // If highlights experiment use new common gradient for iOS and macOS + switch colorScheme { + case .dark: OnboardingGradient() + case .light: + // iOS 15 doesn't render properly the light EllipticalGradient while the Dark gradient is rendered correctly + // https://app.asana.com/0/1206329551987282/1208839072951158/f + if #available(iOS 16, *) { + OnboardingGradient() + } else { + Image(.onboardingGradientLight) + .resizable() + } @unknown default: - linearLightGradient + OnboardingGradient() } } - private var linearLightGradient: some View { - gradient(colorStops: [ - .init(color: Color(red: 1, green: 0.9, blue: 0.87), location: 0.00), - .init(color: Color(red: 0.99, green: 0.89, blue: 0.87), location: 0.28), - .init(color: Color(red: 0.99, green: 0.89, blue: 0.87), location: 0.46), - .init(color: Color(red: 0.96, green: 0.87, blue: 0.87), location: 0.72), - .init(color: Color(red: 0.9, green: 0.84, blue: 0.92), location: 1.00), - ]) - } - - private var linearDarkGradient: some View { - gradient(colorStops: [ - .init(color: Color(red: 0.29, green: 0.19, blue: 0.25), location: 0.00), - .init(color: Color(red: 0.35, green: 0.23, blue: 0.32), location: 0.28), - .init(color: Color(red: 0.37, green: 0.25, blue: 0.38), location: 0.46), - .init(color: Color(red: 0.2, green: 0.15, blue: 0.32), location: 0.72), - .init(color: Color(red: 0.16, green: 0.15, blue: 0.34), location: 1.00), - ]) - } - - private func gradient(colorStops: [SwiftUI.Gradient.Stop]) -> some View { - LinearGradient( - stops: colorStops, - startPoint: UnitPoint(x: 0.5, y: 0), - endPoint: UnitPoint(x: 0.5, y: 1) - ) - } - -} - -enum OnboardingGradientType { - case `default` - case highlights } diff --git a/DuckDuckGo/OnboardingExperiment/BrowsersComparison/BrowsersComparisonModel.swift b/DuckDuckGo/OnboardingExperiment/BrowsersComparison/BrowsersComparisonModel.swift index 6f4f855868..c7011b0b1f 100644 --- a/DuckDuckGo/OnboardingExperiment/BrowsersComparison/BrowsersComparisonModel.swift +++ b/DuckDuckGo/OnboardingExperiment/BrowsersComparison/BrowsersComparisonModel.swift @@ -117,29 +117,18 @@ extension BrowsersComparisonModel.PrivacyFeature { case blockCreepyAds case eraseBrowsingData - // Remove it once Highlights experiment finishes - static var onboardingManager: OnboardingHighlightsManaging = OnboardingManager() - var title: String { switch self { case .privateSearch: - UserText.DaxOnboardingExperiment.BrowsersComparison.Features.privateSearch + UserText.Onboarding.BrowsersComparison.Features.privateSearch case .blockThirdPartyTrackers: - Self.onboardingManager.isOnboardingHighlightsEnabled ? - UserText.HighlightsOnboardingExperiment.BrowsersComparison.Features.trackerBlockers : - UserText.DaxOnboardingExperiment.BrowsersComparison.Features.trackerBlockers + UserText.Onboarding.BrowsersComparison.Features.trackerBlockers case .blockCookiePopups: - Self.onboardingManager.isOnboardingHighlightsEnabled ? - UserText.HighlightsOnboardingExperiment.BrowsersComparison.Features.cookiePopups: - UserText.DaxOnboardingExperiment.BrowsersComparison.Features.cookiePopups + UserText.Onboarding.BrowsersComparison.Features.cookiePopups case .blockCreepyAds: - Self.onboardingManager.isOnboardingHighlightsEnabled ? - UserText.HighlightsOnboardingExperiment.BrowsersComparison.Features.creepyAds : - UserText.DaxOnboardingExperiment.BrowsersComparison.Features.creepyAds + UserText.Onboarding.BrowsersComparison.Features.creepyAds case .eraseBrowsingData: - Self.onboardingManager.isOnboardingHighlightsEnabled ? - UserText.HighlightsOnboardingExperiment.BrowsersComparison.Features.eraseBrowsingData: - UserText.DaxOnboardingExperiment.BrowsersComparison.Features.eraseBrowsingData + UserText.Onboarding.BrowsersComparison.Features.eraseBrowsingData } } } diff --git a/DuckDuckGo/OnboardingExperiment/ContextualDaxDialogs/ContextualOnboardingDialogs.swift b/DuckDuckGo/OnboardingExperiment/ContextualDaxDialogs/ContextualOnboardingDialogs.swift index b3e61bea54..ed28c452ef 100644 --- a/DuckDuckGo/OnboardingExperiment/ContextualDaxDialogs/ContextualOnboardingDialogs.swift +++ b/DuckDuckGo/OnboardingExperiment/ContextualDaxDialogs/ContextualOnboardingDialogs.swift @@ -23,7 +23,7 @@ import Onboarding import DuckUI struct OnboardingTrySearchDialog: View { - let title = UserText.DaxOnboardingExperiment.ContextualOnboarding.onboardingTryASearchTitle + let title = UserText.Onboarding.ContextualOnboarding.onboardingTryASearchTitle let message: String let viewModel: OnboardingSearchSuggestionsViewModel @@ -34,6 +34,7 @@ struct OnboardingTrySearchDialog: View { title: title, titleFont: Font(UIFont.daxTitle3()), message: NSAttributedString(string: message), + messageFont: Font.system(size: 16), list: viewModel.itemsList, listAction: viewModel.listItemPressed ) @@ -58,7 +59,7 @@ struct OnboardingTryVisitingSiteDialog: View { } struct OnboardingTryVisitingSiteDialogContent: View { - let message = NSAttributedString(string: UserText.DaxOnboardingExperiment.ContextualOnboarding.onboardingTryASiteMessage) + let message = NSAttributedString(string: UserText.Onboarding.ContextualOnboarding.onboardingTryASiteMessage) let viewModel: OnboardingSiteSuggestionsViewModel @@ -67,6 +68,7 @@ struct OnboardingTryVisitingSiteDialogContent: View { title: viewModel.title, titleFont: Font(UIFont.daxTitle3()), message: message, + messageFont: Font.system(size: 16), list: viewModel.itemsList, listAction: viewModel.listItemPressed) } @@ -74,7 +76,7 @@ struct OnboardingTryVisitingSiteDialogContent: View { struct OnboardingFireButtonDialogContent: View { private let attributedMessage: NSAttributedString = { - let firstString = UserText.DaxOnboardingExperiment.ContextualOnboarding.onboardingTryFireButtonMessage + let firstString = UserText.Onboarding.ContextualOnboarding.onboardingTryFireButtonMessage let boldString = "Fire Button." let attributedString = NSMutableAttributedString(string: firstString) let boldFontAttribute: [NSAttributedString.Key: Any] = [ @@ -90,12 +92,14 @@ struct OnboardingFireButtonDialogContent: View { var body: some View { ContextualDaxDialogContent( - message: attributedMessage) + message: attributedMessage, + messageFont: Font.system(size: 16) + ) } } struct OnboardingFirstSearchDoneDialog: View { - let cta = UserText.DaxOnboardingExperiment.ContextualOnboarding.onboardingGotItButton + let cta = UserText.Onboarding.ContextualOnboarding.onboardingGotItButton let message: NSAttributedString @State private var showNextScreen: Bool = false @@ -113,6 +117,7 @@ struct OnboardingFirstSearchDoneDialog: View { } else { ContextualDaxDialogContent( message: message, + messageFont: Font.system(size: 16), customActionView: AnyView( OnboardingCTAButton(title: cta) { gotItAction() @@ -147,7 +152,7 @@ struct OnboardingFireDialog: View { } struct OnboardingTrackersDoneDialog: View { - let cta = UserText.DaxOnboardingExperiment.ContextualOnboarding.onboardingGotItButton + let cta = UserText.Onboarding.ContextualOnboarding.onboardingGotItButton @State private var showNextScreen: Bool = false @@ -164,6 +169,7 @@ struct OnboardingTrackersDoneDialog: View { } else { ContextualDaxDialogContent( message: message, + messageFont: Font.system(size: 16), customActionView: AnyView( OnboardingCTAButton(title: cta) { blockedTrackersCTAAction() @@ -202,7 +208,7 @@ struct OnboardingFinalDialog: View { } } else { ContextualDaxDialogContent( - title: canShowAddToDockTutorial ? UserText.AddToDockOnboarding.Promo.title : UserText.DaxOnboardingExperiment.ContextualOnboarding.onboardingFinalScreenTitle, + title: canShowAddToDockTutorial ? UserText.AddToDockOnboarding.Promo.title : UserText.Onboarding.ContextualOnboarding.onboardingFinalScreenTitle, titleFont: Font(UIFont.daxTitle3()), message: NSAttributedString(string: message), messageFont: Font.system(size: 16), @@ -295,17 +301,17 @@ struct OnboardingAddToDockTutorialContent: View { // MARK: - Preview #Preview("Try Search") { - OnboardingTrySearchDialog(message: UserText.DaxOnboardingExperiment.ContextualOnboarding.onboardingTryASearchMessage, viewModel: OnboardingSearchSuggestionsViewModel(suggestedSearchesProvider: OnboardingSuggestedSearchesProvider(), pixelReporter: OnboardingPixelReporter())) + OnboardingTrySearchDialog(message: UserText.Onboarding.ContextualOnboarding.onboardingTryASearchMessage, viewModel: OnboardingSearchSuggestionsViewModel(suggestedSearchesProvider: OnboardingSuggestedSearchesProvider(), pixelReporter: OnboardingPixelReporter())) .padding() } #Preview("Try Site Top") { - OnboardingTryVisitingSiteDialog(logoPosition: .top, viewModel: OnboardingSiteSuggestionsViewModel(title: UserText.DaxOnboardingExperiment.ContextualOnboarding.onboardingTryASiteTitle, suggestedSitesProvider: OnboardingSuggestedSitesProvider(surpriseItemTitle: UserText.DaxOnboardingExperiment.ContextualOnboarding.tryASearchOptionSurpriseMeTitle), pixelReporter: OnboardingPixelReporter())) + OnboardingTryVisitingSiteDialog(logoPosition: .top, viewModel: OnboardingSiteSuggestionsViewModel(title: UserText.Onboarding.ContextualOnboarding.onboardingTryASiteTitle, suggestedSitesProvider: OnboardingSuggestedSitesProvider(surpriseItemTitle: UserText.Onboarding.ContextualOnboarding.tryASearchOptionSurpriseMeTitle), pixelReporter: OnboardingPixelReporter())) .padding() } #Preview("Try Site Left") { - OnboardingTryVisitingSiteDialog(logoPosition: .left, viewModel: OnboardingSiteSuggestionsViewModel(title: UserText.DaxOnboardingExperiment.ContextualOnboarding.onboardingTryASiteTitle, suggestedSitesProvider: OnboardingSuggestedSitesProvider(surpriseItemTitle: UserText.DaxOnboardingExperiment.ContextualOnboarding.tryASearchOptionSurpriseMeTitle), pixelReporter: OnboardingPixelReporter())) + OnboardingTryVisitingSiteDialog(logoPosition: .left, viewModel: OnboardingSiteSuggestionsViewModel(title: UserText.Onboarding.ContextualOnboarding.onboardingTryASiteTitle, suggestedSitesProvider: OnboardingSuggestedSitesProvider(surpriseItemTitle: UserText.Onboarding.ContextualOnboarding.tryASearchOptionSurpriseMeTitle), pixelReporter: OnboardingPixelReporter())) .padding() } @@ -317,15 +323,21 @@ struct OnboardingAddToDockTutorialContent: View { } #Preview("First Search Dialog") { - OnboardingFirstSearchDoneDialog(message: NSAttributedString(string: UserText.DaxOnboardingExperiment.ContextualOnboarding.onboardingFirstSearchDoneMessage), shouldFollowUp: true, viewModel: OnboardingSiteSuggestionsViewModel(title: UserText.DaxOnboardingExperiment.ContextualOnboarding.onboardingTryASiteTitle, suggestedSitesProvider: OnboardingSuggestedSitesProvider(surpriseItemTitle: UserText.DaxOnboardingExperiment.ContextualOnboarding.tryASearchOptionSurpriseMeTitle), pixelReporter: OnboardingPixelReporter()), gotItAction: {}) + let attributedMessage = { + let message = UserText.Onboarding.ContextualOnboarding.onboardingFirstSearchDoneMessage + let boldRange = message.range(of: "DuckDuckGo Search") + return message.attributed.with(attribute: .font, value: UIFont.daxBodyBold(), in: boldRange) + }() + + return OnboardingFirstSearchDoneDialog(message: attributedMessage, shouldFollowUp: true, viewModel: OnboardingSiteSuggestionsViewModel(title: UserText.Onboarding.ContextualOnboarding.onboardingTryASiteTitle, suggestedSitesProvider: OnboardingSuggestedSitesProvider(surpriseItemTitle: UserText.Onboarding.ContextualOnboarding.tryASearchOptionSurpriseMeTitle), pixelReporter: OnboardingPixelReporter()), gotItAction: {}) .padding() } #Preview("Final Dialog - No Add to Dock Tutorial") { OnboardingFinalDialog( logoPosition: .top, - message: UserText.DaxOnboardingExperiment.ContextualOnboarding.onboardingFinalScreenMessage, - cta: UserText.DaxOnboardingExperiment.ContextualOnboarding.onboardingFinalScreenButton, + message: UserText.Onboarding.ContextualOnboarding.onboardingFinalScreenMessage, + cta: UserText.Onboarding.ContextualOnboarding.onboardingFinalScreenButton, canShowAddToDockTutorial: false, showAddToDockTutorialAction: {}, dismissAction: { _ in } diff --git a/DuckDuckGo/OnboardingExperiment/ContextualDaxDialogs/NewTabDaxDialogFactory.swift b/DuckDuckGo/OnboardingExperiment/ContextualDaxDialogs/NewTabDaxDialogFactory.swift index 1dce0528fd..00d70662ae 100644 --- a/DuckDuckGo/OnboardingExperiment/ContextualDaxDialogs/NewTabDaxDialogFactory.swift +++ b/DuckDuckGo/OnboardingExperiment/ContextualDaxDialogs/NewTabDaxDialogFactory.swift @@ -30,17 +30,13 @@ final class NewTabDaxDialogFactory: NewTabDaxDialogProvider { private var delegate: OnboardingNavigationDelegate? private let contextualOnboardingLogic: ContextualOnboardingLogic private let onboardingPixelReporter: OnboardingPixelReporting - private let onboardingManager: OnboardingHighlightsManaging & OnboardingAddToDockManaging - - private var gradientType: OnboardingGradientType { - onboardingManager.isOnboardingHighlightsEnabled ? .highlights : .default - } + private let onboardingManager: OnboardingAddToDockManaging init( delegate: OnboardingNavigationDelegate?, contextualOnboardingLogic: ContextualOnboardingLogic, onboardingPixelReporter: OnboardingPixelReporting, - onboardingManager: OnboardingHighlightsManaging & OnboardingAddToDockManaging = OnboardingManager() + onboardingManager: OnboardingAddToDockManaging = OnboardingManager() ) { self.delegate = delegate self.contextualOnboardingLogic = contextualOnboardingLogic @@ -66,24 +62,24 @@ final class NewTabDaxDialogFactory: NewTabDaxDialogProvider { private func createInitialDialog() -> some View { let viewModel = OnboardingSearchSuggestionsViewModel(suggestedSearchesProvider: OnboardingSuggestedSearchesProvider(), delegate: delegate, pixelReporter: onboardingPixelReporter) - let message = onboardingManager.isOnboardingHighlightsEnabled ? UserText.HighlightsOnboardingExperiment.ContextualOnboarding.onboardingTryASearchMessage : UserText.DaxOnboardingExperiment.ContextualOnboarding.onboardingTryASearchMessage + let message = UserText.Onboarding.ContextualOnboarding.onboardingTryASearchMessage return FadeInView { OnboardingTrySearchDialog(message: message, viewModel: viewModel) .onboardingDaxDialogStyle() } - .onboardingContextualBackgroundStyle(background: .illustratedGradient(gradientType)) + .onboardingContextualBackgroundStyle(background: .illustratedGradient) .onFirstAppear { [weak self] in self?.onboardingPixelReporter.trackScreenImpression(event: .onboardingContextualTrySearchUnique) } } private func createSubsequentDialog() -> some View { - let viewModel = OnboardingSiteSuggestionsViewModel(title: UserText.DaxOnboardingExperiment.ContextualOnboarding.onboardingTryASiteNTPTitle, suggestedSitesProvider: OnboardingSuggestedSitesProvider(surpriseItemTitle: UserText.DaxOnboardingExperiment.ContextualOnboarding.tryASearchOptionSurpriseMeTitle), delegate: delegate, pixelReporter: onboardingPixelReporter) + let viewModel = OnboardingSiteSuggestionsViewModel(title: UserText.Onboarding.ContextualOnboarding.onboardingTryASiteNTPTitle, suggestedSitesProvider: OnboardingSuggestedSitesProvider(surpriseItemTitle: UserText.Onboarding.ContextualOnboarding.tryASearchOptionSurpriseMeTitle), delegate: delegate, pixelReporter: onboardingPixelReporter) return FadeInView { OnboardingTryVisitingSiteDialog(logoPosition: .top, viewModel: viewModel) .onboardingDaxDialogStyle() } - .onboardingContextualBackgroundStyle(background: .illustratedGradient(gradientType)) + .onboardingContextualBackgroundStyle(background: .illustratedGradient) .onFirstAppear { [weak self] in self?.onboardingPixelReporter.trackScreenImpression(event: .onboardingContextualTryVisitSiteUnique) } @@ -91,11 +87,14 @@ final class NewTabDaxDialogFactory: NewTabDaxDialogProvider { private func createAddFavoriteDialog(message: String) -> some View { FadeInView { - DaxDialogView(logoPosition: .top) { - ContextualDaxDialogContent(message: NSAttributedString(string: message)) + ScrollView(.vertical) { + DaxDialogView(logoPosition: .top) { + ContextualDaxDialogContent(message: NSAttributedString(string: message), messageFont: Font.system(size: 16)) + } + .padding() } - .padding() } + .onboardingContextualBackgroundStyle(background: .illustratedGradient) } private func createFinalDialog(onDismiss: @escaping () -> Void) -> some View { @@ -105,8 +104,8 @@ final class NewTabDaxDialogFactory: NewTabDaxDialogProvider { (UserText.AddToDockOnboarding.Promo.contextualMessage, UserText.AddToDockOnboarding.Buttons.startBrowsing) } else { ( - onboardingManager.isOnboardingHighlightsEnabled ? UserText.HighlightsOnboardingExperiment.ContextualOnboarding.onboardingFinalScreenMessage : UserText.DaxOnboardingExperiment.ContextualOnboarding.onboardingFinalScreenMessage, - UserText.DaxOnboardingExperiment.ContextualOnboarding.onboardingFinalScreenButton + UserText.Onboarding.ContextualOnboarding.onboardingFinalScreenMessage, + UserText.Onboarding.ContextualOnboarding.onboardingFinalScreenButton ) } @@ -136,7 +135,7 @@ final class NewTabDaxDialogFactory: NewTabDaxDialogProvider { dismissAction: dismissAction ) } - .onboardingContextualBackgroundStyle(background: .illustratedGradient(gradientType)) + .onboardingContextualBackgroundStyle(background: .illustratedGradient) .onFirstAppear { [weak self] in self?.contextualOnboardingLogic.setFinalOnboardingDialogSeen() self?.onboardingPixelReporter.trackScreenImpression(event: .daxDialogsEndOfJourneyNewTabUnique) diff --git a/DuckDuckGo/OnboardingExperiment/ContextualOnboarding/ContextualDaxDialogsFactory.swift b/DuckDuckGo/OnboardingExperiment/ContextualOnboarding/ContextualDaxDialogsFactory.swift index 25f7b7542c..5ccdb1a462 100644 --- a/DuckDuckGo/OnboardingExperiment/ContextualOnboarding/ContextualDaxDialogsFactory.swift +++ b/DuckDuckGo/OnboardingExperiment/ContextualOnboarding/ContextualDaxDialogsFactory.swift @@ -48,18 +48,14 @@ final class ExperimentContextualDaxDialogsFactory: ContextualDaxDialogsFactory { private let contextualOnboardingSettings: ContextualOnboardingSettings private let contextualOnboardingPixelReporter: OnboardingPixelReporting private let contextualOnboardingSiteSuggestionsProvider: OnboardingSuggestionsItemsProviding - private let onboardingManager: OnboardingHighlightsManaging & OnboardingAddToDockManaging - - private var gradientType: OnboardingGradientType { - onboardingManager.isOnboardingHighlightsEnabled ? .highlights : .default - } + private let onboardingManager: OnboardingAddToDockManaging init( contextualOnboardingLogic: ContextualOnboardingLogic, contextualOnboardingSettings: ContextualOnboardingSettings = DefaultDaxDialogsSettings(), contextualOnboardingPixelReporter: OnboardingPixelReporting, - contextualOnboardingSiteSuggestionsProvider: OnboardingSuggestionsItemsProviding = OnboardingSuggestedSitesProvider(surpriseItemTitle: UserText.DaxOnboardingExperiment.ContextualOnboarding.tryASearchOptionSurpriseMeTitle), - onboardingManager: OnboardingHighlightsManaging & OnboardingAddToDockManaging = OnboardingManager() + contextualOnboardingSiteSuggestionsProvider: OnboardingSuggestionsItemsProviding = OnboardingSuggestedSitesProvider(surpriseItemTitle: UserText.Onboarding.ContextualOnboarding.tryASearchOptionSurpriseMeTitle), + onboardingManager: OnboardingAddToDockManaging = OnboardingManager() ) { self.contextualOnboardingSettings = contextualOnboardingSettings self.contextualOnboardingLogic = contextualOnboardingLogic @@ -99,7 +95,7 @@ final class ExperimentContextualDaxDialogsFactory: ContextualDaxDialogsFactory { let viewWithBackground = rootView .onboardingDaxDialogStyle() - .onboardingContextualBackgroundStyle(background: .gradientOnly(gradientType)) + .onboardingContextualBackgroundStyle(background: .gradientOnly) let hostingController = UIHostingController(rootView: AnyView(viewWithBackground)) if #available(iOS 16.0, *) { hostingController.sizingOptions = [.intrinsicContentSize] @@ -116,16 +112,12 @@ final class ExperimentContextualDaxDialogsFactory: ContextualDaxDialogsFactory { ) -> some View { func dialogMessage() -> NSAttributedString { - if onboardingManager.isOnboardingHighlightsEnabled { - let message = UserText.HighlightsOnboardingExperiment.ContextualOnboarding.onboardingFirstSearchDoneMessage - let boldRange = message.range(of: "DuckDuckGo Search") - return message.attributed.with(attribute: .font, value: UIFont.daxBodyBold(), in: boldRange) - } else { - return UserText.DaxOnboardingExperiment.ContextualOnboarding.onboardingFirstSearchDoneMessage.attributed - } + let message = UserText.Onboarding.ContextualOnboarding.onboardingFirstSearchDoneMessage + let boldRange = message.range(of: "DuckDuckGo Search") + return message.attributed.with(attribute: .font, value: UIFont.daxBodyBold(), in: boldRange) } - let viewModel = OnboardingSiteSuggestionsViewModel(title: UserText.DaxOnboardingExperiment.ContextualOnboarding.onboardingTryASiteTitle, suggestedSitesProvider: contextualOnboardingSiteSuggestionsProvider, delegate: delegate, pixelReporter: contextualOnboardingPixelReporter) + let viewModel = OnboardingSiteSuggestionsViewModel(title: UserText.Onboarding.ContextualOnboarding.onboardingTryASiteTitle, suggestedSitesProvider: contextualOnboardingSiteSuggestionsProvider, delegate: delegate, pixelReporter: contextualOnboardingPixelReporter) // If should not show websites search after searching inform the delegate that the user dimissed the dialog, otherwise let the dialog handle it. let gotItAction: () -> Void = if shouldFollowUpToWebsiteSearch { @@ -147,7 +139,7 @@ final class ExperimentContextualDaxDialogsFactory: ContextualDaxDialogsFactory { } private func tryVisitingSiteDialog(delegate: ContextualOnboardingDelegate) -> some View { - let viewModel = OnboardingSiteSuggestionsViewModel(title: UserText.DaxOnboardingExperiment.ContextualOnboarding.onboardingTryASiteTitle, suggestedSitesProvider: contextualOnboardingSiteSuggestionsProvider, delegate: delegate, pixelReporter: contextualOnboardingPixelReporter) + let viewModel = OnboardingSiteSuggestionsViewModel(title: UserText.Onboarding.ContextualOnboarding.onboardingTryASiteTitle, suggestedSitesProvider: contextualOnboardingSiteSuggestionsProvider, delegate: delegate, pixelReporter: contextualOnboardingPixelReporter) return OnboardingTryVisitingSiteDialog(logoPosition: .left, viewModel: viewModel) .onFirstAppear { [weak self] in self?.contextualOnboardingPixelReporter.trackScreenImpression(event: .onboardingContextualTryVisitSiteUnique) @@ -188,8 +180,8 @@ final class ExperimentContextualDaxDialogsFactory: ContextualDaxDialogsFactory { (UserText.AddToDockOnboarding.Promo.contextualMessage, UserText.AddToDockOnboarding.Buttons.startBrowsing) } else { ( - onboardingManager.isOnboardingHighlightsEnabled ? UserText.HighlightsOnboardingExperiment.ContextualOnboarding.onboardingFinalScreenMessage : UserText.DaxOnboardingExperiment.ContextualOnboarding.onboardingFinalScreenMessage, - UserText.DaxOnboardingExperiment.ContextualOnboarding.onboardingFinalScreenButton + UserText.Onboarding.ContextualOnboarding.onboardingFinalScreenMessage, + UserText.Onboarding.ContextualOnboarding.onboardingFinalScreenButton ) } diff --git a/DuckDuckGo/OnboardingExperiment/ContextualOnboarding/ContextualOnboardingPresenter.swift b/DuckDuckGo/OnboardingExperiment/ContextualOnboarding/ContextualOnboardingPresenter.swift index 3aef1552a0..91b863d63a 100644 --- a/DuckDuckGo/OnboardingExperiment/ContextualOnboarding/ContextualOnboardingPresenter.swift +++ b/DuckDuckGo/OnboardingExperiment/ContextualOnboarding/ContextualOnboardingPresenter.swift @@ -47,15 +47,11 @@ final class ContextualOnboardingPresenter: ContextualOnboardingPresenting { } func presentContextualOnboarding(for spec: DaxDialogs.BrowsingSpec, in vc: TabViewOnboardingDelegate) { - if variantManager.isContextualDaxDialogsEnabled { - presentExperimentContextualOnboarding(for: spec, in: vc) - } else { - presentControlContextualOnboarding(for: spec, in: vc) - } + presentExperimentContextualOnboarding(for: spec, in: vc) } func dismissContextualOnboardingIfNeeded(from vc: TabViewOnboardingDelegate) { - guard variantManager.isContextualDaxDialogsEnabled, let daxContextualOnboarding = vc.daxContextualOnboardingController else { return } + guard let daxContextualOnboarding = vc.daxContextualOnboardingController else { return } remove(daxController: daxContextualOnboarding, fromParent: vc) } diff --git a/DuckDuckGo/OnboardingExperiment/Manager/OnboardingManager.swift b/DuckDuckGo/OnboardingExperiment/Manager/OnboardingManager.swift index b2f212410f..14d06eddbc 100644 --- a/DuckDuckGo/OnboardingExperiment/Manager/OnboardingManager.swift +++ b/DuckDuckGo/OnboardingExperiment/Manager/OnboardingManager.swift @@ -56,39 +56,6 @@ final class OnboardingManager { } } -// MARK: - Onboarding Highlights - -protocol OnboardingHighlightsManaging: AnyObject { - var isOnboardingHighlightsEnabled: Bool { get } -} - -protocol OnboardingHighlightsDebugging: OnboardingHighlightsManaging { - var isOnboardingHighlightsLocalFlagEnabled: Bool { get set } - var isOnboardingHighlightsFeatureFlagEnabled: Bool { get } -} - - -extension OnboardingManager: OnboardingHighlightsManaging, OnboardingHighlightsDebugging { - - var isOnboardingHighlightsEnabled: Bool { - variantManager.isOnboardingHighlightsExperiment || (isOnboardingHighlightsLocalFlagEnabled && isOnboardingHighlightsFeatureFlagEnabled) - } - - var isOnboardingHighlightsLocalFlagEnabled: Bool { - get { - appDefaults.onboardingHighlightsEnabled - } - set { - appDefaults.onboardingHighlightsEnabled = newValue - } - } - - var isOnboardingHighlightsFeatureFlagEnabled: Bool { - featureFlagger.isFeatureOn(.onboardingHighlights) - } - -} - // MARK: - Add to Dock protocol OnboardingAddToDockManaging: AnyObject { @@ -103,7 +70,14 @@ protocol OnboardingAddToDockDebugging: AnyObject { extension OnboardingManager: OnboardingAddToDockManaging, OnboardingAddToDockDebugging { var addToDockEnabledState: OnboardingAddToDockState { - // TODO: Add variant condition OR local conditions + // Check if the variant supports Add to Dock + if variantManager.isSupported(feature: .addToDockIntro) { + return .intro + } else if variantManager.isSupported(feature: .addToDockContextual) { + return .contextual + } + + // If the variant does not support Add to Dock check if it's enabled for internal users. guard isAddToDockFeatureFlagEnabled && isIphone else { return .disabled } return addToDockLocalFlagState diff --git a/DuckDuckGo/OnboardingExperiment/OnboardingIntro/OnboardingIntroViewModel+Copy.swift b/DuckDuckGo/OnboardingExperiment/OnboardingIntro/OnboardingIntroViewModel+Copy.swift index 5e2e713a7e..f8e8c777ff 100644 --- a/DuckDuckGo/OnboardingExperiment/OnboardingIntro/OnboardingIntroViewModel+Copy.swift +++ b/DuckDuckGo/OnboardingExperiment/OnboardingIntro/OnboardingIntroViewModel+Copy.swift @@ -23,30 +23,14 @@ extension OnboardingIntroViewModel { struct Copy { let introTitle: String let browserComparisonTitle: String - let trackerBlockers: String - let cookiePopups: String - let creepyAds: String - let eraseBrowsingData: String } } extension OnboardingIntroViewModel.Copy { static let `default` = OnboardingIntroViewModel.Copy( - introTitle: UserText.DaxOnboardingExperiment.Intro.title, - browserComparisonTitle: UserText.DaxOnboardingExperiment.BrowsersComparison.title, - trackerBlockers: UserText.DaxOnboardingExperiment.BrowsersComparison.Features.trackerBlockers, - cookiePopups: UserText.DaxOnboardingExperiment.BrowsersComparison.Features.cookiePopups, - creepyAds: UserText.DaxOnboardingExperiment.BrowsersComparison.Features.creepyAds, - eraseBrowsingData: UserText.DaxOnboardingExperiment.BrowsersComparison.Features.eraseBrowsingData + introTitle: UserText.Onboarding.Intro.title, + browserComparisonTitle: UserText.Onboarding.BrowsersComparison.title ) - static let highlights = OnboardingIntroViewModel.Copy( - introTitle: UserText.HighlightsOnboardingExperiment.Intro.title, - browserComparisonTitle: UserText.HighlightsOnboardingExperiment.BrowsersComparison.title, - trackerBlockers: UserText.HighlightsOnboardingExperiment.BrowsersComparison.Features.trackerBlockers, - cookiePopups: UserText.HighlightsOnboardingExperiment.BrowsersComparison.Features.cookiePopups, - creepyAds: UserText.HighlightsOnboardingExperiment.BrowsersComparison.Features.creepyAds, - eraseBrowsingData: UserText.HighlightsOnboardingExperiment.BrowsersComparison.Features.eraseBrowsingData - ) } diff --git a/DuckDuckGo/OnboardingExperiment/OnboardingIntro/OnboardingIntroViewModel.swift b/DuckDuckGo/OnboardingExperiment/OnboardingIntro/OnboardingIntroViewModel.swift index d16992322b..740e6659ca 100644 --- a/DuckDuckGo/OnboardingExperiment/OnboardingIntro/OnboardingIntroViewModel.swift +++ b/DuckDuckGo/OnboardingExperiment/OnboardingIntro/OnboardingIntroViewModel.swift @@ -26,12 +26,11 @@ final class OnboardingIntroViewModel: ObservableObject { @Published private(set) var state: OnboardingView.ViewState = .landing let copy: Copy - let gradientType: OnboardingGradientType var onCompletingOnboardingIntro: (() -> Void)? private var introSteps: [OnboardingIntroStep] private let pixelReporter: OnboardingIntroPixelReporting & OnboardingAddToDockReporting - private let onboardingManager: OnboardingHighlightsManaging & OnboardingAddToDockManaging + private let onboardingManager: OnboardingAddToDockManaging private let isIpad: Bool private let urlOpener: URLOpener private let appIconProvider: () -> AppIcon @@ -39,7 +38,7 @@ final class OnboardingIntroViewModel: ObservableObject { init( pixelReporter: OnboardingIntroPixelReporting & OnboardingAddToDockReporting, - onboardingManager: OnboardingHighlightsManaging & OnboardingAddToDockManaging = OnboardingManager(), + onboardingManager: OnboardingAddToDockManaging = OnboardingManager(), isIpad: Bool = UIDevice.current.userInterfaceIdiom == .pad, urlOpener: URLOpener = UIApplication.shared, appIconProvider: @escaping () -> AppIcon = { AppIconManager.shared.appIcon }, @@ -52,16 +51,14 @@ final class OnboardingIntroViewModel: ObservableObject { self.appIconProvider = appIconProvider self.addressBarPositionProvider = addressBarPositionProvider - introSteps = if onboardingManager.isOnboardingHighlightsEnabled && onboardingManager.addToDockEnabledState == .intro { - isIpad ? OnboardingIntroStep.highlightsIPadFlow : OnboardingIntroStep.highlightsAddToDockIphoneFlow - } else if onboardingManager.isOnboardingHighlightsEnabled { - isIpad ? OnboardingIntroStep.highlightsIPadFlow : OnboardingIntroStep.highlightsIPhoneFlow + // Add to Dock experiment assigned only to iPhone users + introSteps = if onboardingManager.addToDockEnabledState == .intro { + OnboardingIntroStep.addToDockIphoneFlow } else { - OnboardingIntroStep.defaultFlow + isIpad ? OnboardingIntroStep.defaultIPadFlow : OnboardingIntroStep.defaultIPhoneFlow } - copy = onboardingManager.isOnboardingHighlightsEnabled ? .highlights : .default - gradientType = onboardingManager.isOnboardingHighlightsEnabled ? .highlights : .default + copy = .default } func onAppear() { @@ -129,12 +126,7 @@ private extension OnboardingIntroViewModel { func makeViewState(for introStep: OnboardingIntroStep) -> OnboardingView.ViewState { func stepInfo() -> OnboardingView.ViewState.Intro.StepInfo { - guard - let currentStepIndex = introSteps.firstIndex(of: introStep), - onboardingManager.isOnboardingHighlightsEnabled - else { - return .hidden - } + guard let currentStepIndex = introSteps.firstIndex(of: introStep) else { return .hidden } // Remove startOnboardingDialog from the count of total steps since we don't show the progress for that step. return OnboardingView.ViewState.Intro.StepInfo(currentStep: currentStepIndex, totalSteps: introSteps.count - 1) @@ -157,14 +149,12 @@ private extension OnboardingIntroViewModel { } func handleSetDefaultBrowserAction() { - if onboardingManager.addToDockEnabledState == .intro && onboardingManager.isOnboardingHighlightsEnabled { + if onboardingManager.addToDockEnabledState == .intro { state = makeViewState(for: .addToDockPromo) pixelReporter.trackAddToDockPromoImpression() - } else if onboardingManager.isOnboardingHighlightsEnabled { + } else { state = makeViewState(for: .appIconSelection) pixelReporter.trackChooseAppIconImpression() - } else { - onCompletingOnboardingIntro?() } } @@ -179,8 +169,7 @@ private enum OnboardingIntroStep { case addressBarPositionSelection case addToDockPromo - static let defaultFlow: [OnboardingIntroStep] = [.introDialog, .browserComparison] - static let highlightsIPhoneFlow: [OnboardingIntroStep] = [.introDialog, .browserComparison, .appIconSelection, .addressBarPositionSelection] - static let highlightsIPadFlow: [OnboardingIntroStep] = [.introDialog, .browserComparison, .appIconSelection] - static let highlightsAddToDockIphoneFlow: [OnboardingIntroStep] = [.introDialog, .browserComparison, .addToDockPromo, .appIconSelection, .addressBarPositionSelection] + static let defaultIPhoneFlow: [OnboardingIntroStep] = [.introDialog, .browserComparison, .appIconSelection, .addressBarPositionSelection] + static let defaultIPadFlow: [OnboardingIntroStep] = [.introDialog, .browserComparison, .appIconSelection] + static let addToDockIphoneFlow: [OnboardingIntroStep] = [.introDialog, .browserComparison, .addToDockPromo, .appIconSelection, .addressBarPositionSelection] } diff --git a/DuckDuckGo/OnboardingExperiment/OnboardingIntro/OnboardingView+AddressBarPositionContent.swift b/DuckDuckGo/OnboardingExperiment/OnboardingIntro/OnboardingView+AddressBarPositionContent.swift index 5ef61b133c..979593d0db 100644 --- a/DuckDuckGo/OnboardingExperiment/OnboardingIntro/OnboardingView+AddressBarPositionContent.swift +++ b/DuckDuckGo/OnboardingExperiment/OnboardingIntro/OnboardingView+AddressBarPositionContent.swift @@ -23,7 +23,6 @@ import Onboarding private enum Metrics { static let titleFont = Font.system(size: 20, weight: .semibold) - static let messageFont = Font.system(size: 16) } extension OnboardingView { @@ -51,7 +50,7 @@ extension OnboardingView { var body: some View { VStack(spacing: 16.0) { - AnimatableTypingText(UserText.HighlightsOnboardingExperiment.AddressBarPosition.title, startAnimating: animateTitle) { + AnimatableTypingText(UserText.Onboarding.AddressBarPosition.title, startAnimating: animateTitle) { showContent.wrappedValue = true } .foregroundColor(.primary) @@ -61,7 +60,7 @@ extension OnboardingView { OnboardingAddressBarPositionPicker() Button(action: action) { - Text(verbatim: UserText.HighlightsOnboardingExperiment.AddressBarPosition.cta) + Text(verbatim: UserText.Onboarding.AddressBarPosition.cta) } .buttonStyle(PrimaryButtonStyle()) } diff --git a/DuckDuckGo/OnboardingExperiment/OnboardingIntro/OnboardingView+AppIconPickerContent.swift b/DuckDuckGo/OnboardingExperiment/OnboardingIntro/OnboardingView+AppIconPickerContent.swift index a85d4df605..5fd7a30be0 100644 --- a/DuckDuckGo/OnboardingExperiment/OnboardingIntro/OnboardingView+AppIconPickerContent.swift +++ b/DuckDuckGo/OnboardingExperiment/OnboardingIntro/OnboardingView+AppIconPickerContent.swift @@ -50,13 +50,13 @@ extension OnboardingView { var body: some View { VStack(spacing: 16.0) { - AnimatableTypingText(UserText.HighlightsOnboardingExperiment.AppIconSelection.title, startAnimating: animateTitle) { + AnimatableTypingText(UserText.Onboarding.AppIconSelection.title, startAnimating: animateTitle) { animateMessage.wrappedValue = true } .foregroundColor(.primary) .font(Metrics.titleFont) - AnimatableTypingText(UserText.HighlightsOnboardingExperiment.AppIconSelection.message, startAnimating: animateMessage) { + AnimatableTypingText(UserText.Onboarding.AppIconSelection.message, startAnimating: animateMessage) { withAnimation { showContent.wrappedValue = true } @@ -68,7 +68,7 @@ extension OnboardingView { AppIconPicker() Button(action: action) { - Text(UserText.HighlightsOnboardingExperiment.AppIconSelection.cta) + Text(UserText.Onboarding.AppIconSelection.cta) } .buttonStyle(PrimaryButtonStyle()) } @@ -83,5 +83,4 @@ extension OnboardingView { private enum Metrics { static let titleFont = Font.system(size: 20, weight: .semibold) static let messageFont = Font.system(size: 16) - static let pickerLeadingOffset: CGFloat = -20 } diff --git a/DuckDuckGo/OnboardingExperiment/OnboardingIntro/OnboardingView+BrowsersComparisonContent.swift b/DuckDuckGo/OnboardingExperiment/OnboardingIntro/OnboardingView+BrowsersComparisonContent.swift index 0772d9e3b8..fdaf12a120 100644 --- a/DuckDuckGo/OnboardingExperiment/OnboardingIntro/OnboardingView+BrowsersComparisonContent.swift +++ b/DuckDuckGo/OnboardingExperiment/OnboardingIntro/OnboardingView+BrowsersComparisonContent.swift @@ -61,7 +61,7 @@ extension OnboardingView { OnboardingActions( viewModel: .init( - primaryButtonTitle: UserText.DaxOnboardingExperiment.BrowsersComparison.cta, + primaryButtonTitle: UserText.Onboarding.BrowsersComparison.cta, secondaryButtonTitle: UserText.onboardingSkip ), primaryAction: setAsDefaultBrowserAction, diff --git a/DuckDuckGo/OnboardingExperiment/OnboardingIntro/OnboardingView+IntroDialogContent.swift b/DuckDuckGo/OnboardingExperiment/OnboardingIntro/OnboardingView+IntroDialogContent.swift index 430be926ea..d4324609f6 100644 --- a/DuckDuckGo/OnboardingExperiment/OnboardingIntro/OnboardingView+IntroDialogContent.swift +++ b/DuckDuckGo/OnboardingExperiment/OnboardingIntro/OnboardingView+IntroDialogContent.swift @@ -39,7 +39,7 @@ extension OnboardingView { var body: some View { VStack(spacing: 24.0) { - AnimatableTypingText(title) { + AnimatableTypingText(title, startAnimating: animateText) { withAnimation { showCTA.wrappedValue = true } @@ -48,7 +48,7 @@ extension OnboardingView { .font(Font.system(size: 20, weight: .bold)) Button(action: action) { - Text(UserText.DaxOnboardingExperiment.Intro.cta) + Text(UserText.Onboarding.Intro.cta) } .buttonStyle(PrimaryButtonStyle()) .visibility(showCTA.wrappedValue ? .visible : .invisible) diff --git a/DuckDuckGo/OnboardingExperiment/OnboardingIntro/OnboardingView.swift b/DuckDuckGo/OnboardingExperiment/OnboardingIntro/OnboardingView.swift index 2d99d818be..38d7d37a3b 100644 --- a/DuckDuckGo/OnboardingExperiment/OnboardingIntro/OnboardingView.swift +++ b/DuckDuckGo/OnboardingExperiment/OnboardingIntro/OnboardingView.swift @@ -40,7 +40,6 @@ struct OnboardingView: View { @State private var appIconPickerContentState = AppIconPickerContentState() @State private var addressBarPositionContentState = AddressBarPositionContentState() - @State private var addToDockPromoContentState = AddToDockPromoContentState() init(model: OnboardingIntroViewModel) { self.model = model @@ -57,7 +56,6 @@ struct OnboardingView: View { onboardingDialogView(state: viewState) } } - .onboardingGradient(model.gradientType) } private func onboardingDialogView(state: ViewState.Intro) -> some View { @@ -76,10 +74,6 @@ struct OnboardingView: View { case .browsersComparisonDialog: showComparisonButton = true animateComparisonText = false - case .addToDockPromoDialog: - addToDockPromoContentState.animateTitle = false - addToDockPromoContentState.animateMessage = false - addToDockPromoContentState.showContent = true case .chooseAppIconDialog: appIconPickerContentState.animateTitle = false appIconPickerContentState.animateMessage = false diff --git a/DuckDuckGo/OnboardingExperiment/Styles/DaxDialogStyles.swift b/DuckDuckGo/OnboardingExperiment/Styles/DaxDialogStyles.swift index 881e5fd721..09efd90925 100644 --- a/DuckDuckGo/OnboardingExperiment/Styles/DaxDialogStyles.swift +++ b/DuckDuckGo/OnboardingExperiment/Styles/DaxDialogStyles.swift @@ -38,12 +38,11 @@ extension OnboardingStyles { func body(content: Content) -> some View { ZStack { switch backgroundType { - case let .illustratedGradient(gradientType): + case .illustratedGradient: OnboardingBackground() - .onboardingGradient(gradientType) .ignoresSafeArea(.keyboard) - case let .gradientOnly(gradientType): - OnboardingGradientView(type: gradientType) + case .gradientOnly: + OnboardingGradientView() .ignoresSafeArea(.keyboard) } @@ -71,25 +70,6 @@ extension View { } enum OnboardingBackgroundType { - case illustratedGradient(OnboardingGradientType) - case gradientOnly(OnboardingGradientType) -} - -enum OnboardingGradientTypeKey: EnvironmentKey { - static var defaultValue: OnboardingGradientType = .default -} - -extension EnvironmentValues { - var onboardingGradientType: OnboardingGradientType { - get { self[OnboardingGradientTypeKey.self] } - set { self[OnboardingGradientTypeKey.self] = newValue } - } -} - -extension View { - - func onboardingGradient(_ type: OnboardingGradientType) -> some View { - environment(\.onboardingGradientType, type) - } - + case illustratedGradient + case gradientOnly } diff --git a/DuckDuckGo/OnboardingHelpers/OnboardingSuggestedSearchesProvider.swift b/DuckDuckGo/OnboardingHelpers/OnboardingSuggestedSearchesProvider.swift index ed71123a3c..fa8c180ded 100644 --- a/DuckDuckGo/OnboardingHelpers/OnboardingSuggestedSearchesProvider.swift +++ b/DuckDuckGo/OnboardingHelpers/OnboardingSuggestedSearchesProvider.swift @@ -24,31 +24,17 @@ struct OnboardingSuggestedSearchesProvider: OnboardingSuggestionsItemsProviding private static let imageSearch = "!image " private let countryAndLanguageProvider: OnboardingRegionAndLanguageProvider - private let onboardingManager: OnboardingHighlightsManaging - init( - countryAndLanguageProvider: OnboardingRegionAndLanguageProvider = Locale.current, - onboardingManager: OnboardingHighlightsManaging = OnboardingManager() - ) { + init(countryAndLanguageProvider: OnboardingRegionAndLanguageProvider = Locale.current) { self.countryAndLanguageProvider = countryAndLanguageProvider - self.onboardingManager = onboardingManager } var list: [ContextualOnboardingListItem] { - if onboardingManager.isOnboardingHighlightsEnabled { - [ - option1, - option2, - surpriseMe - ] - } else { - [ - option1, - option2, - option3, - surpriseMe - ] - } + [ + option1, + option2, + surpriseMe + ] } private var country: String? { @@ -61,9 +47,9 @@ struct OnboardingSuggestedSearchesProvider: OnboardingSuggestionsItemsProviding private var option1: ContextualOnboardingListItem { var search: String if language == "en" { - search = UserText.DaxOnboardingExperiment.ContextualOnboarding.tryASearchOption1English + search = UserText.Onboarding.ContextualOnboarding.tryASearchOption1English } else { - search = UserText.DaxOnboardingExperiment.ContextualOnboarding.tryASearchOption1International + search = UserText.Onboarding.ContextualOnboarding.tryASearchOption1International } return ContextualOnboardingListItem.search(title: search) } @@ -73,28 +59,17 @@ struct OnboardingSuggestedSearchesProvider: OnboardingSuggestionsItemsProviding // ISO 3166-1 Region capitalized. // https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPInternational/LanguageandLocaleIDs/LanguageandLocaleIDs.html if isUSCountry { - search = UserText.DaxOnboardingExperiment.ContextualOnboarding.tryASearchOption2English + search = UserText.Onboarding.ContextualOnboarding.tryASearchOption2English } else { - search = UserText.DaxOnboardingExperiment.ContextualOnboarding.tryASearchOption2International + search = UserText.Onboarding.ContextualOnboarding.tryASearchOption2International } return ContextualOnboardingListItem.search(title: search) } - private var option3: ContextualOnboardingListItem { - let search = UserText.DaxOnboardingExperiment.ContextualOnboarding.tryASearchOption3 - return ContextualOnboardingListItem.search(title: search) - } - private var surpriseMe: ContextualOnboardingListItem { - let search = if onboardingManager.isOnboardingHighlightsEnabled { - Self.imageSearch + UserText.HighlightsOnboardingExperiment.ContextualOnboarding.tryASearchOptionSurpriseMe - } else { - isUSCountry ? - UserText.DaxOnboardingExperiment.ContextualOnboarding.tryASearchOptionSurpriseMeEnglish : - UserText.DaxOnboardingExperiment.ContextualOnboarding.tryASearchOptionSurpriseMeInternational - } + let search = Self.imageSearch + UserText.Onboarding.ContextualOnboarding.tryASearchOptionSurpriseMe - return ContextualOnboardingListItem.surprise(title: search, visibleTitle: UserText.DaxOnboardingExperiment.ContextualOnboarding.tryASearchOptionSurpriseMeTitle) + return ContextualOnboardingListItem.surprise(title: search, visibleTitle: UserText.Onboarding.ContextualOnboarding.tryASearchOptionSurpriseMeTitle) } private var isUSCountry: Bool { diff --git a/DuckDuckGo/PrivateSearchView.swift b/DuckDuckGo/PrivateSearchView.swift index 43190bb81e..cf00930e85 100644 --- a/DuckDuckGo/PrivateSearchView.swift +++ b/DuckDuckGo/PrivateSearchView.swift @@ -40,6 +40,9 @@ struct PrivateSearchView: View { .applySettingsListModifiers(title: UserText.privateSearch, displayMode: .inline, viewModel: viewModel) + .onFirstAppear { + Pixel.fire(pixel: .settingsPrivateSearchOpen) + } } } diff --git a/DuckDuckGo/RoundedPageContainer/RoundedPageSheetContainerViewController.swift b/DuckDuckGo/RoundedPageContainer/RoundedPageSheetContainerViewController.swift new file mode 100644 index 0000000000..a9cb54dc7e --- /dev/null +++ b/DuckDuckGo/RoundedPageContainer/RoundedPageSheetContainerViewController.swift @@ -0,0 +1,179 @@ +// +// RoundedPageSheetContainerViewController.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +final class RoundedPageSheetContainerViewController: UIViewController { + let contentViewController: UIViewController + private let logoImage: UIImage? + private let titleText: String + private let allowedOrientation: UIInterfaceOrientationMask + + private lazy var titleBarView: TitleBarView = { + let titleBarView = TitleBarView(logoImage: logoImage, title: titleText) { [weak self] in + self?.closeController() + } + return titleBarView + }() + + init(contentViewController: UIViewController, logoImage: UIImage?, title: String, allowedOrientation: UIInterfaceOrientationMask = .all) { + self.contentViewController = contentViewController + self.logoImage = logoImage + self.titleText = title + self.allowedOrientation = allowedOrientation + super.init(nibName: nil, bundle: nil) + modalPresentationStyle = .custom + transitioningDelegate = self + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override var shouldAutorotate: Bool { + return false + } + + override var supportedInterfaceOrientations: UIInterfaceOrientationMask { + return allowedOrientation + } + + override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation { + return UIInterfaceOrientation.portrait + } + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = .black + + setupTitleBar() + setupContentViewController() + } + + private func setupTitleBar() { + view.addSubview(titleBarView) + titleBarView.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + titleBarView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), + titleBarView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + titleBarView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + titleBarView.heightAnchor.constraint(equalToConstant: 44) + ]) + } + + private func setupContentViewController() { + addChild(contentViewController) + view.addSubview(contentViewController.view) + contentViewController.view.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + contentViewController.view.topAnchor.constraint(equalTo: titleBarView.bottomAnchor), // Below the title bar + contentViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor), + contentViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), + contentViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor) + ]) + + contentViewController.view.layer.cornerRadius = 20 + contentViewController.view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] + contentViewController.view.clipsToBounds = true + + contentViewController.didMove(toParent: self) + } + + @objc func closeController() { + dismiss(animated: true, completion: nil) + } +} + +extension RoundedPageSheetContainerViewController: UIViewControllerTransitioningDelegate { + func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { + return RoundedPageSheetPresentationAnimator() + } + + func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { + return RoundedPageSheetDismissalAnimator() + } +} + +final private class TitleBarView: UIView { + private let imageView: UIImageView + private let titleLabel: UILabel + private let closeButton: UIButton + + init(logoImage: UIImage?, title: String, closeAction: @escaping () -> Void) { + imageView = UIImageView(image: logoImage) + titleLabel = UILabel() + closeButton = UIButton(type: .system) + + super.init(frame: .zero) + + setupView(title: title, closeAction: closeAction) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupView(title: String, closeAction: @escaping () -> Void) { + backgroundColor = .clear + + imageView.contentMode = .scaleAspectFit + imageView.translatesAutoresizingMaskIntoConstraints = false + + let imageSize: CGFloat = 28 + NSLayoutConstraint.activate([ + imageView.widthAnchor.constraint(equalToConstant: imageSize), + imageView.heightAnchor.constraint(equalToConstant: imageSize) + ]) + + titleLabel.text = title + titleLabel.font = UIFont.systemFont(ofSize: 17, weight: .semibold) + titleLabel.textColor = .white + titleLabel.translatesAutoresizingMaskIntoConstraints = false + + closeButton.setImage(UIImage(named: "Close-24"), for: .normal) + closeButton.tintColor = .white + closeButton.translatesAutoresizingMaskIntoConstraints = false + closeButton.addTarget(self, action: #selector(closeButtonTapped), for: .touchUpInside) + + addSubview(imageView) + addSubview(titleLabel) + addSubview(closeButton) + + NSLayoutConstraint.activate([ + imageView.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor, constant: 16), + imageView.centerYAnchor.constraint(equalTo: centerYAnchor), + + titleLabel.leadingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: 8), + titleLabel.centerYAnchor.constraint(equalTo: centerYAnchor), + + closeButton.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor, constant: -16), + closeButton.centerYAnchor.constraint(equalTo: centerYAnchor) + ]) + + self.closeAction = closeAction + } + + private var closeAction: (() -> Void)? + + @objc private func closeButtonTapped() { + closeAction?() + } +} diff --git a/DuckDuckGo/RoundedPageContainer/RoundedPageSheetPresentationAnimator.swift b/DuckDuckGo/RoundedPageContainer/RoundedPageSheetPresentationAnimator.swift new file mode 100644 index 0000000000..8f5f584591 --- /dev/null +++ b/DuckDuckGo/RoundedPageContainer/RoundedPageSheetPresentationAnimator.swift @@ -0,0 +1,71 @@ +// +// RoundedPageSheetPresentationAnimator.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +enum AnimatorConstants { + static let duration: TimeInterval = 0.4 +} + +class RoundedPageSheetPresentationAnimator: NSObject, UIViewControllerAnimatedTransitioning { + func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { + return AnimatorConstants.duration + } + + func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { + guard let toViewController = transitionContext.viewController(forKey: .to) as? RoundedPageSheetContainerViewController, + let toView = toViewController.view, + let contentView = toViewController.contentViewController.view else { return } + + let containerView = transitionContext.containerView + + containerView.addSubview(toView) + toView.alpha = 0 + contentView.transform = CGAffineTransform(translationX: 0, y: containerView.bounds.height) + + UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: { + toView.alpha = 1 + contentView.transform = .identity + }, completion: { finished in + transitionContext.completeTransition(finished) + }) + } +} + +class RoundedPageSheetDismissalAnimator: NSObject, UIViewControllerAnimatedTransitioning { + func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { + return AnimatorConstants.duration + } + + func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { + guard let fromViewController = transitionContext.viewController(forKey: .from) as? RoundedPageSheetContainerViewController, + let fromView = fromViewController.view, + let contentView = fromViewController.contentViewController.view else { return } + + let containerView = transitionContext.containerView + + UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: { + fromView.alpha = 0 + contentView.transform = CGAffineTransform(translationX: 0, y: containerView.bounds.height) + }, completion: { finished in + fromView.removeFromSuperview() + transitionContext.completeTransition(finished) + }) + } +} diff --git a/DuckDuckGo/SaveAutofillLoginManager.swift b/DuckDuckGo/SaveAutofillLoginManager.swift index ea932d0ce0..4749b6dfd4 100644 --- a/DuckDuckGo/SaveAutofillLoginManager.swift +++ b/DuckDuckGo/SaveAutofillLoginManager.swift @@ -29,6 +29,7 @@ protocol SaveAutofillLoginManagerProtocol { var isPasswordOnlyAccount: Bool { get } var hasOtherCredentialsOnSameDomain: Bool { get } var hasSavedMatchingPasswordWithoutUsername: Bool { get } + var hasSavedMatchingUsernameWithoutPassword: Bool { get } var hasSavedMatchingUsername: Bool { get } static func saveCredentials(_ credentials: SecureVaultModels.WebsiteCredentials, with factory: AutofillVaultFactory) throws -> Int64 @@ -85,7 +86,15 @@ final class SaveAutofillLoginManager: SaveAutofillLoginManagerProtocol { var hasSavedMatchingUsername: Bool { savedMatchingUsernameCredential != nil } - + + var hasSavedMatchingUsernameWithoutPassword: Bool { + if let savedMatchingUsernameCredential { + return savedMatchingUsernameCredential.password.flatMap { String(data: $0, encoding: .utf8) }.isNilOrEmpty + } else { + return false + } + } + func prepareData(completion: @escaping () -> Void) { fetchDomainStoredCredentials { [weak self] credentials in self?.domainStoredCredentials = credentials @@ -112,7 +121,7 @@ final class SaveAutofillLoginManager: SaveAutofillLoginManagerProtocol { let credentialsWithSameUsername = domainStoredCredentials.filter { $0.account.username == credentials.account.username } return credentialsWithSameUsername.first } - + private func fetchDomainStoredCredentials(completion: @escaping ([SecureVaultModels.WebsiteCredentials]) -> Void) { DispatchQueue.global(qos: .userInteractive).async { var result = [SecureVaultModels.WebsiteCredentials]() diff --git a/DuckDuckGo/SaveLoginView.swift b/DuckDuckGo/SaveLoginView.swift index 2a738213d0..1b624ca3c4 100644 --- a/DuckDuckGo/SaveLoginView.swift +++ b/DuckDuckGo/SaveLoginView.swift @@ -264,6 +264,7 @@ private enum Const { struct SaveLoginView_Previews: PreviewProvider { private struct MockManager: SaveAutofillLoginManagerProtocol { + var hasSavedMatchingUsernameWithoutPassword: Bool { false } var username: String { "dax@duck.com" } var visiblePassword: String { "supersecurepasswordquack" } diff --git a/DuckDuckGo/SaveLoginViewController.swift b/DuckDuckGo/SaveLoginViewController.swift index 610e8bb0fb..31dad0e309 100644 --- a/DuckDuckGo/SaveLoginViewController.swift +++ b/DuckDuckGo/SaveLoginViewController.swift @@ -77,9 +77,11 @@ class SaveLoginViewController: UIViewController { case .savePassword: Pixel.fire(pixel: .autofillLoginsSavePasswordModalDismissed) case .updateUsername: - Pixel.fire(pixel: .autofillLoginsUpdateUsernameModalDismissed) + let isBackfilled = viewModel.isUpdatingEmptyUsername + Pixel.fire(pixel: .autofillLoginsUpdateUsernameModalDismissed, withAdditionalParameters: [PixelParameters.backfilled: String(describing: isBackfilled)]) case .updatePassword: - Pixel.fire(pixel: .autofillLoginsUpdatePasswordModalDismissed) + let isBackfilled = viewModel.isUpdatingEmptyPassword + Pixel.fire(pixel: .autofillLoginsUpdatePasswordModalDismissed, withAdditionalParameters: [PixelParameters.backfilled: String(describing: isBackfilled)]) } viewModel.viewControllerDidDisappear() @@ -103,9 +105,11 @@ class SaveLoginViewController: UIViewController { case .savePassword: Pixel.fire(pixel: .autofillLoginsSavePasswordModalDisplayed) case .updateUsername: - Pixel.fire(pixel: .autofillLoginsUpdateUsernameModalDisplayed) + let isBackfilled = saveViewModel.isUpdatingEmptyUsername + Pixel.fire(pixel: .autofillLoginsUpdateUsernameModalDisplayed, withAdditionalParameters: [PixelParameters.backfilled: String(describing: isBackfilled)]) case .updatePassword: - Pixel.fire(pixel: .autofillLoginsUpdatePasswordModalDisplayed) + let isBackfilled = saveViewModel.isUpdatingEmptyPassword + Pixel.fire(pixel: .autofillLoginsUpdatePasswordModalDisplayed, withAdditionalParameters: [PixelParameters.backfilled: String(describing: isBackfilled)]) } } } @@ -124,9 +128,11 @@ extension SaveLoginViewController: SaveLoginViewModelDelegate { delegate?.saveLoginViewController(self, didSaveCredentials: credentialManager.credentials) case .updatePassword, .updateUsername: if viewModel.layoutType == .updatePassword { - Pixel.fire(pixel: .autofillLoginsUpdatePasswordModalConfirmed) + let isBackfilled = viewModel.isUpdatingEmptyPassword + Pixel.fire(pixel: .autofillLoginsUpdatePasswordModalConfirmed, withAdditionalParameters: [PixelParameters.backfilled: String(describing: isBackfilled)]) } else { - Pixel.fire(pixel: .autofillLoginsUpdateUsernameModalConfirmed) + let isBackfilled = viewModel.isUpdatingEmptyUsername + Pixel.fire(pixel: .autofillLoginsUpdateUsernameModalConfirmed, withAdditionalParameters: [PixelParameters.backfilled: String(describing: isBackfilled)]) } delegate?.saveLoginViewController(self, didUpdateCredentials: credentialManager.credentials) } diff --git a/DuckDuckGo/SaveLoginViewModel.swift b/DuckDuckGo/SaveLoginViewModel.swift index d8adba63dc..b1e57ef935 100644 --- a/DuckDuckGo/SaveLoginViewModel.swift +++ b/DuckDuckGo/SaveLoginViewModel.swift @@ -88,10 +88,14 @@ final class SaveLoginViewModel: ObservableObject { credentialManager.hasSavedMatchingUsername } - var isUpdatingUsername: Bool { + var isUpdatingEmptyUsername: Bool { credentialManager.hasSavedMatchingPasswordWithoutUsername } + var isUpdatingEmptyPassword: Bool { + credentialManager.hasSavedMatchingUsernameWithoutPassword + } + var hiddenPassword: String { PasswordHider(password: credentialManager.visiblePassword).hiddenPassword } @@ -132,7 +136,7 @@ final class SaveLoginViewModel: ObservableObject { return .savePassword } - if isUpdatingUsername { + if isUpdatingEmptyUsername { return .updateUsername } diff --git a/DuckDuckGo/Settings.bundle/Root.plist b/DuckDuckGo/Settings.bundle/Root.plist index c23b94b9bc..5b6f693ce0 100644 --- a/DuckDuckGo/Settings.bundle/Root.plist +++ b/DuckDuckGo/Settings.bundle/Root.plist @@ -6,7 +6,7 @@ DefaultValue - 7.148.0 + 7.149.1 Key version Title diff --git a/DuckDuckGo/Settings.xcassets/Images/SettingsAIChat.imageset/Contents.json b/DuckDuckGo/Settings.xcassets/Images/SettingsAIChat.imageset/Contents.json new file mode 100644 index 0000000000..356f0fd6ea --- /dev/null +++ b/DuckDuckGo/Settings.xcassets/Images/SettingsAIChat.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "SettingsAIChat.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DuckDuckGo/Settings.xcassets/Images/SettingsAIChat.imageset/SettingsAIChat.pdf b/DuckDuckGo/Settings.xcassets/Images/SettingsAIChat.imageset/SettingsAIChat.pdf new file mode 100644 index 0000000000..adad6866a4 Binary files /dev/null and b/DuckDuckGo/Settings.xcassets/Images/SettingsAIChat.imageset/SettingsAIChat.pdf differ diff --git a/DuckDuckGo/Settings.xcassets/Images/SettingsAIChatHero.imageset/Contents.json b/DuckDuckGo/Settings.xcassets/Images/SettingsAIChatHero.imageset/Contents.json new file mode 100644 index 0000000000..bb0ab0cbc3 --- /dev/null +++ b/DuckDuckGo/Settings.xcassets/Images/SettingsAIChatHero.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "SettingsAIChatHero.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DuckDuckGo/Settings.xcassets/Images/SettingsAIChatHero.imageset/SettingsAIChatHero.pdf b/DuckDuckGo/Settings.xcassets/Images/SettingsAIChatHero.imageset/SettingsAIChatHero.pdf new file mode 100644 index 0000000000..a47a181019 Binary files /dev/null and b/DuckDuckGo/Settings.xcassets/Images/SettingsAIChatHero.imageset/SettingsAIChatHero.pdf differ diff --git a/DuckDuckGo/SettingsAIChatView.swift b/DuckDuckGo/SettingsAIChatView.swift new file mode 100644 index 0000000000..9d1c8631bc --- /dev/null +++ b/DuckDuckGo/SettingsAIChatView.swift @@ -0,0 +1,62 @@ +// +// SettingsAIChatView.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import SwiftUI +import DesignResourcesKit + +struct SettingsAIChatView: View { + @EnvironmentObject var viewModel: SettingsViewModel + + var body: some View { + List { + + VStack(alignment: .center) { + Image("SettingsAIChatHero") + .padding(.top, -20) + + Text(UserText.aiChatFeatureName) + .daxTitle3() + + Text(.init(UserText.aiChatSettingsCaptionWithLinkMarkdown)) + .tint(Color.init(designSystemColor: .accent)) + .daxBodyRegular() + .multilineTextAlignment(.center) + .foregroundColor(Color(designSystemColor: .textSecondary)) + .padding(.top, 12) + } + .frame(maxWidth: .infinity) + .listRowBackground(Color.clear) + + Section { + if viewModel.state.aiChat.isAIChatBrowsingMenuFeatureFlagEnabled { + SettingsCellView(label: UserText.aiChatSettingsEnableBrowsingMenuToggle, + accessory: .toggle(isOn: viewModel.aiChatBrowsingMenuEnabledBinding)) + } + + if viewModel.state.aiChat.isAIChatAddressBarFeatureFlagEnabled { + SettingsCellView(label: UserText.aiChatSettingsEnableAddressBarToggle, + accessory: .toggle(isOn: viewModel.aiChatAddressBarEnabledBinding)) + } + + } + }.applySettingsListModifiers(title: UserText.aiChatFeatureName, + displayMode: .inline, + viewModel: viewModel) + } +} diff --git a/DuckDuckGo/SettingsAppearanceView.swift b/DuckDuckGo/SettingsAppearanceView.swift index e29a2d2169..2a1dfcd4ae 100644 --- a/DuckDuckGo/SettingsAppearanceView.swift +++ b/DuckDuckGo/SettingsAppearanceView.swift @@ -58,5 +58,8 @@ struct SettingsAppearanceView: View { .applySettingsListModifiers(title: UserText.settingsAppearanceSection, displayMode: .inline, viewModel: viewModel) + .onFirstAppear { + Pixel.fire(pixel: .settingsAppearanceOpen) + } } } diff --git a/DuckDuckGo/SettingsDataClearingView.swift b/DuckDuckGo/SettingsDataClearingView.swift index 5a73902537..5054381edc 100644 --- a/DuckDuckGo/SettingsDataClearingView.swift +++ b/DuckDuckGo/SettingsDataClearingView.swift @@ -54,5 +54,8 @@ struct SettingsDataClearingView: View { .applySettingsListModifiers(title: UserText.dataClearing, displayMode: .inline, viewModel: viewModel) + .onFirstAppear { + Pixel.fire(pixel: .settingsDataClearingOpen) + } } } diff --git a/DuckDuckGo/SettingsGeneralView.swift b/DuckDuckGo/SettingsGeneralView.swift index 427ec3bfc3..115c88a0fb 100644 --- a/DuckDuckGo/SettingsGeneralView.swift +++ b/DuckDuckGo/SettingsGeneralView.swift @@ -89,5 +89,8 @@ struct SettingsGeneralView: View { .applySettingsListModifiers(title: UserText.general, displayMode: .inline, viewModel: viewModel) + .onFirstAppear { + Pixel.fire(pixel: .settingsGeneralOpen) + } } } diff --git a/DuckDuckGo/SettingsMainSettingsView.swift b/DuckDuckGo/SettingsMainSettingsView.swift index 02b487031b..32bd5f36e8 100644 --- a/DuckDuckGo/SettingsMainSettingsView.swift +++ b/DuckDuckGo/SettingsMainSettingsView.swift @@ -67,7 +67,7 @@ struct SettingsMainSettingsView: View { SettingsCellView(label: UserText.dataClearing, image: Image("SettingsDataClearing")) } - + // Duck Player // We need to hide the settings until the user is enrolled in the experiment if viewModel.state.duckPlayerEnabled { @@ -76,6 +76,14 @@ struct SettingsMainSettingsView: View { image: Image("SettingsDuckPlayer")) } } + + // AI Chat + if viewModel.state.aiChat.enabled { + NavigationLink(destination: SettingsAIChatView().environmentObject(viewModel)) { + SettingsCellView(label: UserText.aiChatFeatureName, + image: Image("SettingsAIChat")) + } + } } } diff --git a/DuckDuckGo/SettingsRootView.swift b/DuckDuckGo/SettingsRootView.swift index 0378363566..01ad7b6a60 100644 --- a/DuckDuckGo/SettingsRootView.swift +++ b/DuckDuckGo/SettingsRootView.swift @@ -129,6 +129,8 @@ struct SettingsRootView: View { SettingsDuckPlayerView().environmentObject(viewModel) case .netP: NetworkProtectionRootView() + case .aiChat: + SettingsAIChatView().environmentObject(viewModel) } } diff --git a/DuckDuckGo/SettingsState.swift b/DuckDuckGo/SettingsState.swift index 6d1fb7c43d..2437693326 100644 --- a/DuckDuckGo/SettingsState.swift +++ b/DuckDuckGo/SettingsState.swift @@ -48,7 +48,13 @@ struct SettingsState { var platform: DDGSubscription.Platform var isShowingStripeView: Bool } - + + struct AIChat: Codable { + var enabled: Bool + var isAIChatBrowsingMenuFeatureFlagEnabled: Bool + var isAIChatAddressBarFeatureFlagEnabled: Bool + } + struct SyncSettings { var enabled: Bool var title: String @@ -102,7 +108,10 @@ struct SettingsState { var duckPlayerMode: DuckPlayerMode? var duckPlayerOpenInNewTab: Bool var duckPlayerOpenInNewTabEnabled: Bool - + + // AI Chat + var aiChat: AIChat + static var defaults: SettingsState { return SettingsState( appTheme: .systemDefault, @@ -142,7 +151,10 @@ struct SettingsState { duckPlayerEnabled: false, duckPlayerMode: .alwaysAsk, duckPlayerOpenInNewTab: true, - duckPlayerOpenInNewTabEnabled: false + duckPlayerOpenInNewTabEnabled: false, + aiChat: AIChat(enabled: false, + isAIChatBrowsingMenuFeatureFlagEnabled: false, + isAIChatAddressBarFeatureFlagEnabled: false) ) } } diff --git a/DuckDuckGo/SettingsSubscriptionView.swift b/DuckDuckGo/SettingsSubscriptionView.swift index 7c6ce225eb..fdd097594b 100644 --- a/DuckDuckGo/SettingsSubscriptionView.swift +++ b/DuckDuckGo/SettingsSubscriptionView.swift @@ -67,7 +67,7 @@ struct SettingsSubscriptionView: View { let subtitleText = { switch subscriptionManager.storePurchaseManager().currentStorefrontRegion { case .usa: - UserText.settingsPProDescription + UserText.settingsPProUSDescription case .restOfWorld: UserText.settingsPProROWDescription } diff --git a/DuckDuckGo/SettingsViewModel.swift b/DuckDuckGo/SettingsViewModel.swift index 48e20b774f..224f880c2c 100644 --- a/DuckDuckGo/SettingsViewModel.swift +++ b/DuckDuckGo/SettingsViewModel.swift @@ -29,6 +29,7 @@ import Crashes import Subscription import NetworkProtection +import AIChat final class SettingsViewModel: ObservableObject { @@ -45,6 +46,7 @@ final class SettingsViewModel: ObservableObject { private let historyManager: HistoryManaging let privacyProDataReporter: PrivacyProDataReporting? let textZoomCoordinator: TextZoomCoordinating + let aiChatSettings: AIChatSettingsProvider // Subscription Dependencies let subscriptionManager: SubscriptionManager @@ -108,6 +110,7 @@ final class SettingsViewModel: ObservableObject { Binding( get: { self.state.appTheme }, set: { + Pixel.fire(pixel: .settingsThemeSelectorPressed) self.state.appTheme = $0 ThemeManager.shared.enableTheme(with: $0) } @@ -117,6 +120,7 @@ final class SettingsViewModel: ObservableObject { Binding( get: { self.state.fireButtonAnimation }, set: { + Pixel.fire(pixel: .settingsFireButtonSelectorPressed) self.appSettings.currentFireButtonAnimation = $0 self.state.fireButtonAnimation = $0 NotificationCenter.default.post(name: AppUserDefaults.Notifications.currentFireButtonAnimationChange, object: self) @@ -137,6 +141,7 @@ final class SettingsViewModel: ObservableObject { self.state.addressBar.position }, set: { + Pixel.fire(pixel: $0 == .top ? .settingsAddressBarTopSelected : .settingsAddressBarBottomSelected) self.appSettings.currentAddressBarPosition = $0 self.state.addressBar.position = $0 } @@ -147,6 +152,7 @@ final class SettingsViewModel: ObservableObject { Binding( get: { self.state.showsFullURL }, set: { + Pixel.fire(pixel: $0 ? .settingsShowFullURLOn : .settingsShowFullURLOff) self.state.showsFullURL = $0 self.appSettings.showFullSiteAddress = $0 } @@ -260,6 +266,24 @@ final class SettingsViewModel: ObservableObject { ) } + var aiChatBrowsingMenuEnabledBinding: Binding { + Binding( + get: { self.aiChatSettings.isAIChatBrowsingMenuUserSettingsEnabled }, + set: { newValue in + self.aiChatSettings.enableAIChatBrowsingMenuUserSettings(enable: newValue) + } + ) + } + + var aiChatAddressBarEnabledBinding: Binding { + Binding( + get: { self.aiChatSettings.isAIChatAddressBarUserSettingsEnabled }, + set: { newValue in + self.aiChatSettings.enableAIChatAddressBarUserSettings(enable: newValue) + } + ) + } + var textZoomLevelBinding: Binding { Binding( get: { self.state.textZoom.level }, @@ -391,7 +415,8 @@ final class SettingsViewModel: ObservableObject { historyManager: HistoryManaging, syncPausedStateManager: any SyncPausedStateManaging, privacyProDataReporter: PrivacyProDataReporting, - textZoomCoordinator: TextZoomCoordinating) { + textZoomCoordinator: TextZoomCoordinating, + aiChatSettings: AIChatSettingsProvider) { self.state = SettingsState.defaults self.legacyViewProvider = legacyViewProvider @@ -403,6 +428,7 @@ final class SettingsViewModel: ObservableObject { self.syncPausedStateManager = syncPausedStateManager self.privacyProDataReporter = privacyProDataReporter self.textZoomCoordinator = textZoomCoordinator + self.aiChatSettings = aiChatSettings setupNotificationObservers() updateRecentlyVisitedSitesVisibility() @@ -452,8 +478,10 @@ extension SettingsViewModel { duckPlayerEnabled: featureFlagger.isFeatureOn(.duckPlayer) || shouldDisplayDuckPlayerContingencyMessage, duckPlayerMode: appSettings.duckPlayerMode, duckPlayerOpenInNewTab: appSettings.duckPlayerOpenInNewTab, - duckPlayerOpenInNewTabEnabled: featureFlagger.isFeatureOn(.duckPlayerOpenInNewTab) - + duckPlayerOpenInNewTabEnabled: featureFlagger.isFeatureOn(.duckPlayerOpenInNewTab), + aiChat: SettingsState.AIChat(enabled: aiChatSettings.isAIChatFeatureEnabled, + isAIChatBrowsingMenuFeatureFlagEnabled: aiChatSettings.isAIChatBrowsingMenubarShortcutFeatureEnabled, + isAIChatAddressBarFeatureFlagEnabled: aiChatSettings.isAIChatAddressBarShortcutFeatureEnabled) ) updateRecentlyVisitedSitesVisibility() @@ -579,6 +607,7 @@ extension SettingsViewModel { } func openMoreSearchSettings() { + Pixel.fire(pixel: .settingsMoreSearchSettings) UIApplication.shared.open(URL.searchSettings) } @@ -670,6 +699,7 @@ extension SettingsViewModel { case subscriptionFlow(origin: String? = nil) case restoreFlow case duckPlayer + case aiChat // Add other cases as needed var id: String { @@ -680,6 +710,7 @@ extension SettingsViewModel { case .subscriptionFlow: return "subscriptionFlow" case .restoreFlow: return "restoreFlow" case .duckPlayer: return "duckPlayer" + case .aiChat: return "aiChat" // Ensure all cases are covered } } @@ -688,7 +719,7 @@ extension SettingsViewModel { // Default to .sheet, specify .push where needed var type: DeepLinkType { switch self { - case .netP, .dbp, .itr, .subscriptionFlow, .restoreFlow, .duckPlayer: + case .netP, .dbp, .itr, .subscriptionFlow, .restoreFlow, .duckPlayer, .aiChat: return .navigationLink } } diff --git a/DuckDuckGo/SimpleNewTabPageView.swift b/DuckDuckGo/SimpleNewTabPageView.swift index a228686ef5..a4fae0632b 100644 --- a/DuckDuckGo/SimpleNewTabPageView.swift +++ b/DuckDuckGo/SimpleNewTabPageView.swift @@ -49,7 +49,7 @@ struct SimpleNewTabPageView: View { .simultaneousGesture( DragGesture() .onChanged({ value in - if value.translation.height > 0 { + if value.translation.height != 0.0 { viewModel.beginDragging() } }) @@ -85,6 +85,8 @@ private extension SimpleNewTabPageView { } .withScrollKeyboardDismiss() } + // Prevent recreating geomery reader when keyboard is shown/hidden. + .ignoresSafeArea(.keyboard) } @ViewBuilder diff --git a/DuckDuckGo/SmallOmniBarState.swift b/DuckDuckGo/SmallOmniBarState.swift index f00abfac23..18b6fdd49e 100644 --- a/DuckDuckGo/SmallOmniBarState.swift +++ b/DuckDuckGo/SmallOmniBarState.swift @@ -27,7 +27,7 @@ struct SmallOmniBarState { let showBackButton: Bool = false let showForwardButton: Bool = false let showBookmarksButton: Bool = false - let showShareButton: Bool = false + let showAccessoryButton: Bool = false let clearTextOnStart = true let allowsTrackersAnimation = false let showPrivacyIcon = false @@ -60,7 +60,7 @@ struct SmallOmniBarState { let showBackButton: Bool = false let showForwardButton: Bool = false let showBookmarksButton: Bool = false - let showShareButton: Bool = false + let showAccessoryButton: Bool = false let clearTextOnStart = false let allowsTrackersAnimation = false let showPrivacyIcon = false @@ -93,7 +93,7 @@ struct SmallOmniBarState { let showBackButton: Bool = false let showForwardButton: Bool = false let showBookmarksButton: Bool = false - let showShareButton: Bool = false + let showAccessoryButton: Bool = false let clearTextOnStart = true let allowsTrackersAnimation = false let showSearchLoupe = true @@ -127,7 +127,7 @@ struct SmallOmniBarState { let showBackButton: Bool = false let showForwardButton: Bool = false let showBookmarksButton: Bool = false - let showShareButton: Bool = false + let showAccessoryButton: Bool = false let clearTextOnStart = true let allowsTrackersAnimation = false let showPrivacyIcon = false @@ -161,7 +161,7 @@ struct SmallOmniBarState { let showBackButton: Bool = false let showForwardButton: Bool = false let showBookmarksButton: Bool = false - let showShareButton: Bool = false + let showAccessoryButton: Bool = false let clearTextOnStart = false let allowsTrackersAnimation = false let showPrivacyIcon = false @@ -195,7 +195,7 @@ struct SmallOmniBarState { let showBackButton: Bool = false let showForwardButton: Bool = false let showBookmarksButton: Bool = false - let showShareButton: Bool = true + let showAccessoryButton: Bool = true let clearTextOnStart = false let allowsTrackersAnimation = true let showSearchLoupe = false @@ -228,7 +228,7 @@ struct SmallOmniBarState { let showBackButton: Bool = false let showForwardButton: Bool = false let showBookmarksButton: Bool = false - let showShareButton: Bool = false + let showAccessoryButton: Bool = false let clearTextOnStart = false let allowsTrackersAnimation = false let showPrivacyIcon = false diff --git a/DuckDuckGo/Subscription/AsyncHeadlessWebview/HeadlessWebViewCoordinator.swift b/DuckDuckGo/Subscription/AsyncHeadlessWebview/HeadlessWebViewCoordinator.swift index 2cb46b10dc..f6afc847d4 100644 --- a/DuckDuckGo/Subscription/AsyncHeadlessWebview/HeadlessWebViewCoordinator.swift +++ b/DuckDuckGo/Subscription/AsyncHeadlessWebview/HeadlessWebViewCoordinator.swift @@ -226,7 +226,7 @@ extension HeadlessWebViewCoordinator: WKNavigationDelegate { return } - let alertController = UIAlertController(title: UserText.subscriptionAlertTitle, message: message, preferredStyle: .alert) + let alertController = UIAlertController(title: "", message: message, preferredStyle: .alert) alertController.addAction(UIAlertAction(title: UserText.actionOK, style: .default, handler: { _ in completionHandler() })) presenter.present(alertController, animated: true, completion: nil) } diff --git a/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift b/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift index 7792b22677..03412bae5a 100644 --- a/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift +++ b/DuckDuckGo/Subscription/UserScripts/SubscriptionPagesUseSubscriptionFeature.swift @@ -194,10 +194,14 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec // MARK: Broker Methods (Called from WebView via UserScripts) func getSubscription(params: Any, original: WKScriptMessage) async -> Encodable? { - await appStoreAccountManagementFlow.refreshAuthTokenIfNeeded() - let authToken = accountManager.authToken ?? Constants.empty + guard accountManager.isUserAuthenticated else { return [Constants.token: Constants.empty] } - return [Constants.token: authToken] + switch await appStoreAccountManagementFlow.refreshAuthTokenIfNeeded() { + case .success(let currentAuthToken): + return [Constants.token: currentAuthToken] + case .failure: + return [Constants.token: Constants.empty] + } } func getSubscriptionOptions(params: Any, original: WKScriptMessage) async -> Encodable? { diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift index 5dc11256ab..fd9ae97268 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionFlowViewModel.swift @@ -183,7 +183,7 @@ final class SubscriptionFlowViewModel: ObservableObject { isBackendError = true state.transactionError = .hasActiveSubscription case .cancelledByUser: - state.transactionError = nil + state.transactionError = .cancelledByUser case .accountCreationFailed: DailyPixel.fireDailyAndCount(pixel: .privacyProPurchaseFailureAccountNotCreated, pixelNameSuffixes: DailyPixel.Constant.legacyDailyPixelSuffixes) diff --git a/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift b/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift index 3d3fafb4cc..a8a3c3a875 100644 --- a/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift +++ b/DuckDuckGo/Subscription/ViewModel/SubscriptionSettingsViewModel.swift @@ -81,7 +81,8 @@ final class SubscriptionSettingsViewModel: ObservableObject { private var dateFormatter: DateFormatter = { let formatter = DateFormatter() - formatter.dateFormat = "MMMM dd, yyyy" + formatter.dateStyle = .long + formatter.timeStyle = .none return formatter }() @@ -211,7 +212,6 @@ final class SubscriptionSettingsViewModel: ObservableObject { @MainActor private func updateSubscriptionsStatusMessage(status: Subscription.Status, date: Date, product: String, billingPeriod: Subscription.BillingPeriod) { - let billingPeriod = billingPeriod == .monthly ? UserText.subscriptionMonthlyBillingPeriod : UserText.subscriptionAnnualBillingPeriod let date = dateFormatter.string(from: date) switch status { diff --git a/DuckDuckGo/SwipeTabsCoordinator.swift b/DuckDuckGo/SwipeTabsCoordinator.swift index 8071d83ec9..0b25f7bcac 100644 --- a/DuckDuckGo/SwipeTabsCoordinator.swift +++ b/DuckDuckGo/SwipeTabsCoordinator.swift @@ -51,11 +51,14 @@ class SwipeTabsCoordinator: NSObject { var collectionView: MainViewFactory.NavigationBarCollectionView { coordinator.navigationBarCollectionView } - + + private let omnibarAccessoryHandler: OmnibarAccessoryHandler + init(coordinator: MainViewCoordinator, tabPreviewsSource: TabPreviewsSource, appSettings: AppSettings, voiceSearchHelper: VoiceSearchHelperProtocol, + omnibarAccessoryHandler: OmnibarAccessoryHandler, selectTab: @escaping (Int) -> Void, newTab: @escaping () -> Void, onSwipeStarted: @escaping () -> Void) { @@ -64,7 +67,7 @@ class SwipeTabsCoordinator: NSObject { self.tabPreviewsSource = tabPreviewsSource self.appSettings = appSettings self.voiceSearchHelper = voiceSearchHelper - + self.omnibarAccessoryHandler = omnibarAccessoryHandler self.selectTab = selectTab self.newTab = newTab self.onSwipeStarted = onSwipeStarted @@ -326,8 +329,9 @@ extension SwipeTabsCoordinator: UICollectionViewDataSource { cell.omniBar?.startBrowsing() cell.omniBar?.refreshText(forUrl: url, forceFullURL: appSettings.showFullSiteAddress) cell.omniBar?.resetPrivacyIcon(for: url) - } + cell.omniBar?.accessoryType = omnibarAccessoryHandler.omnibarAccessory(for: url) + } } cell.setNeedsUpdateConstraints() diff --git a/DuckDuckGo/SyncSettingsViewController.swift b/DuckDuckGo/SyncSettingsViewController.swift index 0c88d0719a..925b291897 100644 --- a/DuckDuckGo/SyncSettingsViewController.swift +++ b/DuckDuckGo/SyncSettingsViewController.swift @@ -35,7 +35,8 @@ class SyncSettingsViewController: UIHostingController { let syncCredentialsAdapter: SyncCredentialsAdapter var connector: RemoteConnecting? - let userAuthenticator = UserAuthenticator(reason: UserText.syncUserUserAuthenticationReason) + let userAuthenticator = UserAuthenticator(reason: UserText.syncUserUserAuthenticationReason, + cancelTitle: UserText.autofillLoginListAuthenticationCancelButton) let userSession = UserSession() var recoveryCode: String { @@ -243,6 +244,11 @@ class SyncSettingsViewController: UIHostingController { syncService.scheduler.requestSyncImmediately() } + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + Pixel.fire(pixel: .settingsSyncOpen) + } + func updateOptions() { syncService.scheduler.requestSyncImmediately() } diff --git a/DuckDuckGo/TabDelegate.swift b/DuckDuckGo/TabDelegate.swift index 90424e25fb..6865b4bf65 100644 --- a/DuckDuckGo/TabDelegate.swift +++ b/DuckDuckGo/TabDelegate.swift @@ -59,6 +59,8 @@ protocol TabDelegate: AnyObject { func tabDidRequestDownloads(tab: TabViewController) + func tabDidRequestAIChat(tab: TabViewController) + func tabDidRequestAutofillLogins(tab: TabViewController) func tabDidRequestSettings(tab: TabViewController) diff --git a/DuckDuckGo/TabSwitcherOpenDailyPixel.swift b/DuckDuckGo/TabSwitcherOpenDailyPixel.swift index c46735c497..b3dab0a802 100644 --- a/DuckDuckGo/TabSwitcherOpenDailyPixel.swift +++ b/DuckDuckGo/TabSwitcherOpenDailyPixel.swift @@ -49,12 +49,7 @@ struct TabSwitcherOpenDailyPixel { case 21...40: return "21-40" case 41...60: return "41-60" case 61...80: return "61-80" - case 81...100: return "81-100" - case 101...125: return "101-125" - case 126...150: return "126-150" - case 151...250: return "151-250" - case 251...500: return "251-500" - default: return "501+" + default: return "81+" } } diff --git a/DuckDuckGo/TabSwitcherViewController.swift b/DuckDuckGo/TabSwitcherViewController.swift index 585d7dcafb..617ea6054c 100644 --- a/DuckDuckGo/TabSwitcherViewController.swift +++ b/DuckDuckGo/TabSwitcherViewController.swift @@ -331,18 +331,8 @@ class TabSwitcherViewController: UIViewController { } Pixel.fire(pixel: .forgetAllPressedTabSwitching) - let isNewOnboarding = DefaultVariantManager().isContextualDaxDialogsEnabled - - if !isNewOnboarding - && DaxDialogs.shared.shouldShowFireButtonPulse { - let spec = DaxDialogs.shared.fireButtonEducationMessage() - performSegue(withIdentifier: "ActionSheetDaxDialog", sender: spec) - } else { - if isNewOnboarding { - ViewHighlighter.hideAll() - } - presentForgetDataAlert() - } + ViewHighlighter.hideAll() + presentForgetDataAlert() } private func forgetAll() { diff --git a/DuckDuckGo/TabViewController.swift b/DuckDuckGo/TabViewController.swift index 24dddd1628..19bb29eeec 100644 --- a/DuckDuckGo/TabViewController.swift +++ b/DuckDuckGo/TabViewController.swift @@ -270,11 +270,21 @@ class TabViewController: UIViewController { lazy var vaultManager: SecureVaultManager = { let manager = SecureVaultManager(includePartialAccountMatches: true, + shouldAllowPartialFormSaves: featureFlagger.isFeatureOn(.autofillPartialFormSaves), tld: AppDependencyProvider.shared.storageCache.tld) manager.delegate = self return manager }() - + + private lazy var credentialIdentityStoreManager: AutofillCredentialIdentityStoreManager? = { + guard let vault = try? AutofillSecureVaultFactory.makeVault(reporter: SecureVaultReporter()) else { + return nil + } + + return AutofillCredentialIdentityStoreManager(vault: vault, + tld: AppDependencyProvider.shared.storageCache.tld) + }() + private static let debugEvents = EventMapping { event, _, _, onComplete in let domainEvent: Pixel.Event switch event { @@ -591,6 +601,12 @@ class TabViewController: UIViewController { } instrumentation.didPrepareWebView() + + // Initialize DuckPlayerNavigationHandler + if let handler = duckPlayerNavigationHandler, + let webView = webView { + handler.handleAttach(webView: webView) + } if consumeCookies { consumeCookiesThenLoadRequest(request) @@ -613,12 +629,7 @@ class TabViewController: UIViewController { // break a js-initiated popup request such as printing from a popup guard self?.url != cleanURLRequest.url || loadingStopped || !loadingInitiatedByParentTab else { return } self?.load(urlRequest: cleanURLRequest) - - - if let handler = self?.duckPlayerNavigationHandler, - let webView = self?.webView { - handler.handleAttach(webView: webView) - } + }) } @@ -1007,7 +1018,7 @@ class TabViewController: UIViewController { } @IBAction func onBottomOfScreenTapped(_ sender: UITapGestureRecognizer) { - showBars(animated: false) + showBars() } private func showBars(animated: Bool = true) { @@ -1587,9 +1598,6 @@ extension TabViewController: WKNavigationDelegate { return } - if !DefaultVariantManager().isContextualDaxDialogsEnabled { - isShowingFullScreenDaxDialog = true - } scheduleTrackerNetworksAnimation(collapsing: !spec.highlightAddressBar) let daxDialogSourceURL = self.url @@ -2479,13 +2487,15 @@ extension TabViewController: UIGestureRecognizerDelegate { } private func isShowBarsTap(_ gestureRecognizer: UIGestureRecognizer) -> Bool { - let y = gestureRecognizer.location(in: webView).y + let y = gestureRecognizer.location(in: self.view).y return gestureRecognizer == showBarsTapGestureRecogniser && chromeDelegate?.isToolbarHidden == true && isBottom(yPosition: y) } private func isBottom(yPosition y: CGFloat) -> Bool { - guard let chromeDelegate = chromeDelegate else { return false } - return y > (view.frame.size.height - chromeDelegate.toolbarHeight) + let webViewFrameInTabView = webView.convert(webView.bounds, to: view) + let bottomOfWebViewInTabView = webViewFrameInTabView.maxY - webView.scrollView.contentInset.bottom + + return y > bottomOfWebViewInTabView } func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherRecognizer: UIGestureRecognizer) -> Bool { @@ -2836,8 +2846,13 @@ extension TabViewController: SecureVaultManagerDelegate { if accounts.count > 0 { let accountMatches = autofillWebsiteAccountMatcher.findDeduplicatedSortedMatches(accounts: accounts, for: domain) - presentAutofillPromptViewController(accountMatches: accountMatches, domain: domain, trigger: trigger, useLargeDetent: false) { account in + presentAutofillPromptViewController(accountMatches: accountMatches, domain: domain, trigger: trigger, useLargeDetent: false) { [weak self] account in onAccountSelected(account) + + guard let domain = account?.domain else { return } + Task { + await self?.credentialIdentityStoreManager?.updateCredentialStore(for: domain) + } } completionHandler: { account in if account != nil { NotificationCenter.default.post(name: .autofillFillEvent, object: nil) @@ -3031,6 +3046,11 @@ extension TabViewController: SaveLoginViewControllerDelegate { }) Favicons.shared.loadFavicon(forDomain: newCredential.account.domain, intoCache: .fireproof, fromCache: .tabs) } + + guard let domain = newCredential.account.domain else { return } + Task { + await credentialIdentityStoreManager?.updateCredentialStore(for: domain) + } } } catch { Logger.general.error("failed to fetch credentials: \(error.localizedDescription, privacy: .public)") diff --git a/DuckDuckGo/TabViewControllerBrowsingMenuExtension.swift b/DuckDuckGo/TabViewControllerBrowsingMenuExtension.swift index d76c6c18b2..4dc0613771 100644 --- a/DuckDuckGo/TabViewControllerBrowsingMenuExtension.swift +++ b/DuckDuckGo/TabViewControllerBrowsingMenuExtension.swift @@ -28,25 +28,32 @@ import PrivacyDashboard extension TabViewController { - func buildBrowsingMenuHeaderContent() -> [BrowsingMenuEntry] { + private var shouldShowAIChatInMenuHeader: Bool { + let settings = AIChatSettings(privacyConfigurationManager: ContentBlocking.shared.privacyConfigurationManager, + internalUserDecider: AppDependencyProvider.shared.internalUserDecider) + return settings.isAIChatBrowsingMenuUserSettingsEnabled + } + private var shouldShowPrintButtonInBrowsingMenuList: Bool { shouldShowAIChatInMenuHeader } + + func buildBrowsingMenuHeaderContent() -> [BrowsingMenuEntry] { var entries = [BrowsingMenuEntry]() - entries.append(BrowsingMenuEntry.regular(name: UserText.actionNewTab, - accessibilityLabel: UserText.keyCommandNewTab, - image: UIImage(named: "Add-24")!, - action: { [weak self] in + let newTabEntry = BrowsingMenuEntry.regular(name: UserText.actionNewTab, + accessibilityLabel: UserText.keyCommandNewTab, + image: UIImage(named: "Add-24")!, + action: { [weak self] in self?.onNewTabAction() - })) + }) - entries.append(BrowsingMenuEntry.regular(name: UserText.actionShare, image: UIImage(named: "Share-24")!, action: { [weak self] in + let shareEntry = BrowsingMenuEntry.regular(name: UserText.actionShare, image: UIImage(named: "Share-24")!, action: { [weak self] in guard let self = self else { return } guard let menu = self.chromeDelegate?.omniBar.menuButton else { return } Pixel.fire(pixel: .browsingMenuShare) self.onShareAction(forLink: self.link!, fromView: menu) - })) + }) - entries.append(BrowsingMenuEntry.regular(name: UserText.actionCopy, image: UIImage(named: "Copy-24")!, action: { [weak self] in + let copyEntry = BrowsingMenuEntry.regular(name: UserText.actionCopy, image: UIImage(named: "Copy-24")!, action: { [weak self] in guard let strongSelf = self else { return } if !strongSelf.isError, let url = strongSelf.webView.url { strongSelf.onCopyAction(forUrl: url) @@ -58,16 +65,34 @@ extension TabViewController { let addressBarBottom = strongSelf.appSettings.currentAddressBarPosition.isBottom ActionMessageView.present(message: UserText.actionCopyMessage, presentationLocation: .withBottomBar(andAddressBarBottom: addressBarBottom)) - })) + }) - entries.append(BrowsingMenuEntry.regular(name: UserText.actionPrint, image: UIImage(named: "Print-24")!, action: { [weak self] in + let printEntry = BrowsingMenuEntry.regular(name: UserText.actionPrint, image: UIImage(named: "Print-24")!, action: { [weak self] in Pixel.fire(pixel: .browsingMenuPrint) self?.print() - })) + }) + + let chatEntry = BrowsingMenuEntry.regular(name: UserText.actionOpenAIChat, image: UIImage(named: "AIChat-24")!, action: { [weak self] in + Pixel.fire(pixel: .browsingMenuAIChat) + self?.openAIChat() + }) + + if shouldShowAIChatInMenuHeader { + entries.append(newTabEntry) + entries.append(chatEntry) + entries.append(shareEntry) + entries.append(copyEntry) + } else { + entries.append(newTabEntry) + entries.append(shareEntry) + entries.append(copyEntry) + entries.append(printEntry) + } return entries } + var favoriteEntryIndex: Int { 1 } func buildShortcutsMenu() -> [BrowsingMenuEntry] { @@ -80,6 +105,16 @@ extension TabViewController { let linkEntries = buildLinkEntries(with: bookmarksInterface) entries.append(contentsOf: linkEntries) + if shouldShowPrintButtonInBrowsingMenuList { + entries.append(.regular(name: UserText.actionPrintSite, + accessibilityLabel: UserText.actionPrintSite, + image: UIImage(named: "Print-16")!, + action: { [weak self] in + Pixel.fire(pixel: .browsingMenuListPrint) + self?.print() + })) + } + if let domain = self.privacyInfo?.domain { entries.append(self.buildToggleProtectionEntry(forDomain: domain)) } @@ -93,7 +128,10 @@ extension TabViewController { })) } - entries.append(.separator) + // Do not add separator if there are no entries so far + if entries.count > 0 { + entries.append(.separator) + } let shortcutsEntries = buildShortcutsEntries(includeBookmarks: false) entries.append(contentsOf: shortcutsEntries) @@ -440,6 +478,10 @@ extension TabViewController { delegate?.tabDidRequestBookmarks(tab: self) } + private func openAIChat() { + delegate?.tabDidRequestAIChat(tab: self) + } + private func buildToggleProtectionEntry(forDomain domain: String) -> BrowsingMenuEntry { let config = ContentBlocking.shared.privacyConfigurationManager.privacyConfig let isProtected = !config.isUserUnprotected(domain: domain) diff --git a/DuckDuckGo/TabsBarViewController.swift b/DuckDuckGo/TabsBarViewController.swift index a2204f467d..494d5da197 100644 --- a/DuckDuckGo/TabsBarViewController.swift +++ b/DuckDuckGo/TabsBarViewController.swift @@ -104,16 +104,8 @@ class TabsBarViewController: UIViewController { self.present(controller: alert, fromView: fireButton) } - if DefaultVariantManager().isContextualDaxDialogsEnabled { - delegate?.tabsBarDidRequestFireEducationDialog(self) - showClearDataAlert() - } else { - if DaxDialogs.shared.shouldShowFireButtonPulse { - delegate?.tabsBarDidRequestFireEducationDialog(self) - } else { - showClearDataAlert() - } - } + delegate?.tabsBarDidRequestFireEducationDialog(self) + showClearDataAlert() } @IBAction func onNewTabPressed() { @@ -322,14 +314,8 @@ extension MainViewController: TabsBarDelegate { } func tabsBarDidRequestFireEducationDialog(_ controller: TabsBarViewController) { - if DefaultVariantManager().isContextualDaxDialogsEnabled { - currentTab?.dismissContextualDaxFireDialog() - ViewHighlighter.hideAll() - } else { - if let spec = DaxDialogs.shared.fireButtonEducationMessage() { - segueToActionSheetDaxDialogWithSpec(spec) - } - } + currentTab?.dismissContextualDaxFireDialog() + ViewHighlighter.hideAll() } func tabsBarDidRequestTabSwitcher(_ controller: TabsBarViewController) { diff --git a/DuckDuckGo/UniversalOmniBarState.swift b/DuckDuckGo/UniversalOmniBarState.swift index 3dd7fd48f7..d11a46aad4 100644 --- a/DuckDuckGo/UniversalOmniBarState.swift +++ b/DuckDuckGo/UniversalOmniBarState.swift @@ -28,7 +28,7 @@ enum UniversalOmniBarState { var showBackButton: Bool { baseState.showBackButton } var showForwardButton: Bool { baseState.showForwardButton } var showBookmarksButton: Bool { baseState.showBookmarksButton } - var showShareButton: Bool { baseState.showShareButton } + var showAccessoryButton: Bool { baseState.showAccessoryButton } var clearTextOnStart: Bool { baseState.clearTextOnStart } var allowsTrackersAnimation: Bool { baseState.allowsTrackersAnimation } var showSearchLoupe: Bool { baseState.showSearchLoupe } diff --git a/DuckDuckGo/UserText.swift b/DuckDuckGo/UserText.swift index f4d0d7460b..86a2337975 100644 --- a/DuckDuckGo/UserText.swift +++ b/DuckDuckGo/UserText.swift @@ -19,6 +19,7 @@ import Core +import Subscription public struct UserText { @@ -49,7 +50,10 @@ public struct UserText { public static let actionCopy = NSLocalizedString("action.title.copy", value: "Copy", comment: "Copy action") public static let actionCopyMessage = NSLocalizedString("action.title.copy.message", value: "URL copied", comment: "Floating message indicating URL has been copied") public static let actionShare = NSLocalizedString("action.title.share", value: "Share", comment: "Share action") - public static let actionPrint = NSLocalizedString("action.title.print", value: "Print", comment: "Print action") + public static let actionPrint = NSLocalizedString("action.title.print", value: "Print", comment: "Print action in the menu header") + public static let actionPrintSite = NSLocalizedString("action.title.print.site", value: "Print", comment: "Print action in the menu list") + public static let actionOpenAIChat = NSLocalizedString("action.title.aichat", value: "Chat", comment: "Open AI Chat action in the menu list") + public static let actionOpenBookmarks = NSLocalizedString("action.title.bookmarks", value: "Bookmarks", comment: "Button: Open bookmarks list") public static let actionEnableProtection = NSLocalizedString("action.title.enable.protection", value: "Enable Privacy Protection", comment: "Enable protection action") public static let actionDisableProtection = NSLocalizedString("action.title.disable.protection", value: "Disable Privacy Protection", comment: "Disable protection action") @@ -634,6 +638,19 @@ public struct UserText { static let vpnAccessRevokedAlertActionSubscribe = NSLocalizedString("vpn.access-revoked.alert.action.subscribe", value: "Subscribe", comment: "Primary action for the alert when the subscription expires") static let vpnAccessRevokedAlertActionCancel = NSLocalizedString("vpn.access-revoked.alert.action.cancel", value: "Dismiss", comment: "Cancel action for the alert when the subscription expires") + // MARK: Tool tips + + static let networkProtectionAddWidgetTipTitle = NSLocalizedString("network.protection.addwidget.tip.title", value: "Add VPN Widget", comment: "Title for tooltip about adding VPN widget") + static let networkProtectionAddWidgetTipMessage = NSLocalizedString("network.protection.addwidget.tip.message", value: "Turn the VPN on and off right from the Home Screen.", comment: "Message for tooltip about adding VPN widget") + static let networkProtectionAddWidgetTipAction = NSLocalizedString("network.protection.addwidget.tip.action", value: "Add widget", comment: "Button title for tooltip about adding VPN widget") + + static let networkProtectionGeoswitchingTipTitle = NSLocalizedString("network.protection.geoswitching.tip.title", value: "Change Your Location", comment: "Title for tooltip about geoswitching") + static let networkProtectionGeoswitchingTipMessage = NSLocalizedString("network.protection.geoswitching.tip.message", value: "You can customize your VPN location by connecting to any of our servers worldwide.", comment: "Message for tooltip about geoswitching") + + static let networkProtectionSnoozeTipTitle = NSLocalizedString("network.protection.snooze.tip.title", value: "Avoid VPN Conflicts", comment: "Title for tooltip about VPN snooze mode") + static let networkProtectionSnoozeTipMessage = NSLocalizedString("network.protection.snooze.tip.message", value: "You can use sites or apps that block VPN traffic by snoozing the VPN connection.", comment: "Message for tooltip about VPN snooze mode") + static let networkProtectionSnoozeTipAction = NSLocalizedString("network.protection.snooze.tip.action", value: "Learn more", comment: "Button title for tooltip about VPN snooze mode") + // MARK: Unified Feedback Form static let browserFeedbackReportProblem = NSLocalizedString("send.browser.feedback.report-problem", value: "Report a problem", comment: "Name of the option the user can chose to give browser feedback about a problem they enountered") static let browserFeedbackRequestFeature = NSLocalizedString("send.browser.feedback.request-feature", value: "Request a feature", comment: "Name of the option the user can chose to give browser feedback about a feature they would like") @@ -978,13 +995,13 @@ But if you *do* want a peek under the hood, you can find more information about // MARK: VPN - static let networkProtectionNotificationsTitle = NSLocalizedString("network.protection.notification.title", value: "DuckDuckGo", comment: "The title of the notifications shown from Network Protection") - static let networkProtectionConnectionSuccessNotificationBody = NSLocalizedString("network.protection.success.notification.body", value: "Network Protection is On. Your location and online activity are protected.", comment: "The body of the notification shown when Network Protection reconnects successfully") + static let networkProtectionNotificationsTitle = NSLocalizedString("network.protection.notification.title", value: "DuckDuckGo", comment: "The title of the notifications shown from VPN") + static let networkProtectionConnectionSuccessNotificationBody = NSLocalizedString("network.protection.success.notification.body", value: "DuckDuckGo VPN is On. Your location and online activity are protected.", comment: "The body of the notification shown when VPN reconnects successfully") static func networkProtectionConnectionSuccessNotificationBody(serverLocation: String) -> String { let localized = NSLocalizedString( "network.protection.success.notification.subtitle.including.serverLocation", value: "Routing device traffic through %@.", - comment: "The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter" + comment: "The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter" ) return String(format: localized, serverLocation) } @@ -992,12 +1009,12 @@ But if you *do* want a peek under the hood, you can find more information about let localized = NSLocalizedString( "network.protection.success.notification.subtitle.snooze.ended.including.serverLocation", value: "VPN snooze has ended. Routing device traffic through %@.", - comment: "The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter" + comment: "The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter" ) return String(format: localized, serverLocation) } - static let networkProtectionConnectionInterruptedNotificationBody = NSLocalizedString("network.protection.interrupted.notification.body", value: "Network Protection was interrupted. Attempting to reconnect now...", comment: "The body of the notification shown when Network Protection's connection is interrupted") - static let networkProtectionConnectionFailureNotificationBody = NSLocalizedString("network.protection.failure.notification.body", value: "Network Protection failed to connect. Please try again later.", comment: "The body of the notification shown when Network Protection fails to reconnect") + static let networkProtectionConnectionInterruptedNotificationBody = NSLocalizedString("network.protection.interrupted.notification.body", value: "DuckDuckGo VPN was interrupted. Attempting to reconnect now...", comment: "The body of the notification shown when VPN connection is interrupted") + static let networkProtectionConnectionFailureNotificationBody = NSLocalizedString("network.protection.failure.notification.body", value: "DuckDuckGo VPN failed to connect. Please try again later.", comment: "The body of the notification shown when VPN fails to reconnect") static let networkProtectionEntitlementExpiredNotificationBody = NSLocalizedString("network.protection.entitlement.expired.notification.body", value: "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN.", comment: "The body of the notification when Privacy Pro subscription expired") static func networkProtectionSnoozedNotificationBody(duration: String) -> String { @@ -1086,7 +1103,7 @@ But if you *do* want a peek under the hood, you can find more information about public static let settingsPProSection = NSLocalizedString("settings.ppro", value: "Privacy Pro", comment: "Product name for the subscription bundle") public static let settingsPProSectionFooter = NSLocalizedString("settings.ppro.footer", value: "Privacy Policy and Terms of Service", comment: "Title for Link in the Footer of Privacy Pro section") public static let settingsPProSubscribe = NSLocalizedString("settings.subscription.subscribe", value: "Protect your connection and identity with Privacy Pro", comment: "Call to action title for Privacy Pro settings") - public static let settingsPProDescription = NSLocalizedString("settings.subscription.description", value:"Includes our VPN, Personal Information Removal, and Identity Theft Restoration.", comment: "Privacy pro description subtitle in settings") + public static let settingsPProUSDescription = NSLocalizedString("settings.subscription.us.description", value:"Includes our VPN, Personal Information Removal, and Identity Theft Restoration.", comment: "Privacy pro description subtitle in settings") public static let settingsPProROWDescription = NSLocalizedString("settings.subscription.row.description", value:"Includes our VPN and Identity Theft Restoration.", comment: "Privacy Pro description subtitle in settings") public static let settingsPProActivating = NSLocalizedString("settings.subscription.activating", value:"Activating", comment: "Privacy pro description subtitle in settings when the is activating") @@ -1152,19 +1169,47 @@ But if you *do* want a peek under the hood, you can find more information about public static let subscriptionTitle = NSLocalizedString("subscription.title", value: "Privacy Pro", comment: "Navigation bar Title for subscriptions") public static let subscriptionSubscribed = NSLocalizedString("subscription.subscribed", value: "Subscribed", comment: "Subtitle in header when subscribed") public static let subscriptionCloseButton = NSLocalizedString("subscription.close", value: "Close", comment: "Navigation Button for closing subscription view") - - static func renewingSubscriptionInfo(billingPeriod: String, renewalDate: String) -> String { - let localized = NSLocalizedString("subscription.subscription.renewing.caption", - value: "Your %@ subscription renews on %@.", - comment: "Subscription renewal info. This reads as 'Your (monthly or annual) subscription renews on (date)'") - return String(format: localized, billingPeriod, renewalDate) + + static func renewingSubscriptionInfo(billingPeriod: Subscription.BillingPeriod, renewalDate: String) -> String { + let localized: String + + switch billingPeriod { + case .monthly: + localized = NSLocalizedString("subscription.subscription.renewing.monthly.caption", + value: "Your monthly subscription renews on %@.", + comment: "Monthly subscription renewal info where parameter is renewal date. This reads as 'Your monthly subscription renews on (date)'") + case .yearly: + localized = NSLocalizedString("subscription.subscription.renewing.yearly.caption", + value: "Your annual subscription renews on %@.", + comment: "Annual subscription renewal info where parameter is renewal date. This reads as 'Your annual subscription renews on (date)'") + case .unknown: + localized = NSLocalizedString("subscription.subscription.renewing.unknown.caption", + value: "Your subscription renews on %@.", + comment: "Unknown period subscription renewal info where parameter is renewal date. This reads as 'Your subscription renews on (date)'") + } + + return String(format: localized, renewalDate) } - static func expiringSubscriptionInfo(billingPeriod: String, expiryDate: String) -> String { - let localized = NSLocalizedString("subscription.subscription.expiring.caption", - value: "Your %@ subscription expires on %@.", - comment: "Subscription expiration info. This reads as 'Your (monthly or annual) subscription expires on (date)'") - return String(format: localized, billingPeriod, expiryDate) + static func expiringSubscriptionInfo(billingPeriod: Subscription.BillingPeriod, expiryDate: String) -> String { + let localized: String + + switch billingPeriod { + case .monthly: + localized = NSLocalizedString("subscription.subscription.expiring.monthly.caption", + value: "Your monthly subscription expires on %@.", + comment: "Monthly subscription expiration info where parameter is expiration date. This reads as 'Your monthly subscription expires on (date)'") + case .yearly: + localized = NSLocalizedString("subscription.subscription.expiring.yearly.caption", + value: "Your annual subscription expires on %@.", + comment: "Annual subscription expiration info where parameter is expiration date. This reads as 'Your annual subscription expires on (date)'") + case .unknown: + localized = NSLocalizedString("subscription.subscription.expiring.unknown.caption", + value: "Your subscription expires on %@.", + comment: "Unknown period subscription expiration info where parameter is expiration date. This reads as 'Your subscription expires on (date)'") + } + + return String(format: localized, expiryDate) } static func expiredSubscriptionInfo(expiration: String) -> String { @@ -1173,9 +1218,6 @@ But if you *do* want a peek under the hood, you can find more information about comment: "Subscription Expired Data. This reads as 'Your subscription expired on (date)'") return String(format: localized, expiration) } - - public static let subscriptionMonthlyBillingPeriod = NSLocalizedString("subscription.billing.period.monthly", value: "monthly", comment: "Subscription monthly billing period type") - public static let subscriptionAnnualBillingPeriod = NSLocalizedString("subscription.billing.period.annual", value: "annual", comment: "Subscription annual billing period type") public static let subscriptionDevicesSectionHeader = NSLocalizedString("subscription.devices.header", value: "Activate on Other Devices", comment: "Header for section for activating subscription on other devices") public static let subscriptionDevicesSectionNoEmailFooter = NSLocalizedString("subscription.devices.no.email.footer", value: "Add an optional email to your subscription to access Privacy Pro on other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**", comment: "Footer for section for activating subscription on other devices when email was not yet added") @@ -1216,8 +1258,7 @@ But if you *do* want a peek under the hood, you can find more information about public static let subscriptionManageEmailResendInstructions = NSLocalizedString("subscription.add.device.resend.instructions", value: "Resend Instructions", comment: "Resend activation instructions button") public static let subscriptionConfirmTitle = NSLocalizedString("subscription.confirm.title", value: "Are you sure?", comment: "Title for Confirm messages") - public static let subscriptionAlertTitle = NSLocalizedString("subscription.alert.title", value: "", comment: "Title for Alert messages") - + // Add Email To subscription public static let subscriptionAddEmail = NSLocalizedString("subscription.add.email", value: "Add an email address to activate your subscription on your other devices. We’ll only use this address to verify your subscription.", comment: "Add email to an existing subscription") public static let subscriptionRestoreAddEmailTitle = NSLocalizedString("subscription.add.email.title", value: "Add Email", comment: "View title for adding email to subscription") @@ -1293,8 +1334,8 @@ But if you *do* want a peek under the hood, you can find more information about public static let settingsOpenVideosInDuckPlayerLabel = NSLocalizedString("duckplayer.settings.open-videos-in", value: "Open YouTube Videos in Duck Player", comment: "Settings screen cell text for DuckPlayer settings") public static let duckPlayerFeatureName = NSLocalizedString("duckplayer.settings.title", value: "Duck Player", comment: "Settings screen cell text for DuckPlayer settings") - public static let settingsOpenDuckPlayerNewTabLabel = NSLocalizedString("duckplayer.settings.open-new-tab-label", value: "Open Duck Player in a new tab", comment: "Settings screen cell text for DuckPlayer settings to open in new tab") - + public static let settingsOpenDuckPlayerNewTabLabel = NSLocalizedString("duckplayer.settings.open-new-tab-label", value: "Open Duck Player in a New Tab", comment: "Settings screen cell text for DuckPlayer settings to open in new tab") + public static let settingsOpenVideosInDuckPlayerTitle = NSLocalizedString("duckplayer.settings.title", value: "Duck Player", comment: "Settings screen cell text for DuckPlayer settings") public static let settingsDuckPlayerFooter = NSLocalizedString("duckplayer.settings.footer", value: "DuckDuckGo provides all the privacy essentials you need to protect yourself as you browse the web.", comment: "Footer label in the settings screen for Duck Player") @@ -1310,6 +1351,19 @@ But if you *do* want a peek under the hood, you can find more information about static let duckPlayerContingencyMessageBody = NSLocalizedString("duck-player.video-contingency-message", value: "Duck Player's functionality has been affected by recent changes to YouTube. We’re working to fix these issues and appreciate your understanding.", comment: "Message explaining to the user that Duck Player is not available") static let duckPlayerContingencyMessageCTA = NSLocalizedString("duck-player.video-contingency-cta", value: "Learn More", comment: "Button for the message explaining to the user that Duck Player is not available so the user can learn more") + // MARK: - AI Chat + public static let aiChatTitle = NSLocalizedString("aichat.title", value: "DuckDuckGo AI Chat", comment: "Title for DuckDuckGo AI Chat. Should not be translated") + public static let aiChatFeatureName = NSLocalizedString("aichat.settings.title", value: "AI Chat", comment: "Settings screen cell text for AI Chat settings") + + public static let aiChatSettingsEnableFooter = NSLocalizedString("aichat.settings.enable.footer", value: "Turning this off will hide the AI Chat feature in the DuckDuckGo app.", comment: "Footer text for AI Chat settings") + static let aiChatSettingsCaptionWithLinkMarkdown = NSLocalizedString("ai-chat.preferences.text.markdown", value: """ +AI Chat is an optional feature available at [duck.ai](ddgquicklink://duck.ai) that lets you have private conversations with popular 3rd-party AI chat models. Your chats are not used to train chat models. +[Learn More](ddgquicklink://duckduckgo.com/duckduckgo-help-pages/aichat/) +""", comment: "Ai Chat preferences explanation with a markdown link. Do not translate what's inside [] and ()") + public static let aiChatSettingsEnableBrowsingMenuToggle = NSLocalizedString("aichat.settings.enable.browsing-menu-toggle", value: "Show AI Chat in Browser Menu", comment: "Toggle text to enable/disable AI Chat in the browsing menu") + + public static let aiChatSettingsEnableAddressBarToggle = NSLocalizedString("aichat.settings.enable.address-bar-toggle", value: "Show AI Chat While Searching", comment: "Toggle text to enable/disable AI Chat in the address bar") + // MARK: - New Tab Page // MARK: Shortcuts @@ -1338,61 +1392,20 @@ But if you *do* want a peek under the hood, you can find more information about public static let newTabPageIntroMessageBody = NSLocalizedString("new.tab.page.intro.message.body", value: "Customize your Favorites and go-to features. Reorder things or hide them to keep it clean.", comment: "Information message about New Tab Page redesign") - // MARK: - Dax Onboarding Experiment - public enum DaxOnboardingExperiment { - enum Intro { - public static let title = NSLocalizedString("onboarding.intro.title", value: "Hi there.\n\nReady for a better, more private internet?", comment: "The title of the onboarding dialog popup") + // MARK: - Onboarding + public enum Onboarding { + + public enum Intro { + public static let title = NSLocalizedString("onboarding.highlights.intro.title", value: "Hi there.\n\nReady for a faster browser that keeps you protected?", comment: "The title of the onboarding dialog popup") public static let cta = NSLocalizedString("onboarding.intro.cta", value: "Let’s do it!", comment: "Button to continue the onboarding process") } - enum BrowsersComparison { - public static let title = NSLocalizedString("onboarding.browsers.title", value: "Privacy protections activated!", comment: "The title of the dialog to show the privacy features that DuckDuckGo offers") + public enum BrowsersComparison { + public static let title = NSLocalizedString("onboarding.highlights.browsers.title", value: "Protections activated!", comment: "The title of the dialog to show the privacy features that DuckDuckGo offers") public static let cta = NSLocalizedString("onboarding.browsers.cta", value: "Choose Your Browser", comment: "Button to change the default browser") - enum Features { + public enum Features { public static let privateSearch = NSLocalizedString("onboarding.browsers.features.privateSearch.title", value: "Search privately by default", comment: "Message to highlight browser capability of private searches") - public static let trackerBlockers = NSLocalizedString("onboarding.browsers.features.trackerBlocker.title", value: "Block 3rd-party trackers", comment: "Message to highlight browser capability ofblocking 3rd party trackers") - public static let cookiePopups = NSLocalizedString("onboarding.browsers.features.cookiePopups.title", value: "Block cookie pop-ups", comment: "Message to highlight how the browser allows you to block cookie pop-ups") - public static let creepyAds = NSLocalizedString("onboarding.browsers.features.creepyAds.title", value: "Block creepy ads", comment: "Message to highlight browser capability of blocking creepy ads") - public static let eraseBrowsingData = NSLocalizedString("onboarding.browsers.features.eraseBrowsingData.title", value: "Swiftly erase browsing data", comment: "Message to highlight browser capability of swiftly erase browsing data") - } - } - - enum ContextualOnboarding { - static let onboardingTryASearchTitle = NSLocalizedString("contextual.onboarding.try-a-search.title", value: "Ready to get started?\nTry a search!", comment: "Title of a popover on the browser that invites the user to try a search") - static let onboardingTryASearchMessage = NSLocalizedString("contextual.onboarding.try-a-search.message", value: "Your DuckDuckGo searches are always anonymous.", comment: "Message of a popover on the browser that invites the user to try a search explaining that their searches are anonymous") - static let onboardingTryASiteTitle = NSLocalizedString("contextual.onboarding.try-a-site.title", value: "Next, try visiting a site!", comment: "Title of a popover on the browser that invites the user to try a visiting a website") - static let onboardingTryASiteNTPTitle = NSLocalizedString("contextual.onboarding.ntp.try-a-site.title", value: "Try visiting a site!", comment: "Title of a popover on the new tab page browser that invites the user to try a visiting a website") - static let onboardingTryASiteMessage = NSLocalizedString("contextual.onboarding.try-a-site.message", value: "I’ll block trackers so they can’t spy on you.", comment: "Message of a popover on the browser that invites the user to try visiting a website to explain that we block trackers") - static let onboardingTryFireButtonMessage = NSLocalizedString("contextual.onboarding.try-fire-button.message", value: "Instantly clear your browsing activity with the Fire Button.\n\nGive it a try! 🔥", comment: "Message of a popover on the browser that invites the user to try visiting the browser Fire Button. Please leave the line break") - static let onboardingGotItButton = NSLocalizedString("contextual.onboarding.got-it.button", value: "Got it!", comment: "During onboarding steps this button is shown and takes either to the next steps or closes the onboarding.") - static let onboardingFirstSearchDoneMessage = NSLocalizedString("contextual.onboarding.first-search-done.message", value: "That’s DuckDuckGo Search. Private. Fast. Fewer ads.", comment: "After the user performs their first search using the browser, this dialog explains the advantages of using DuckDuckGo") - static let onboardingFinalScreenTitle = NSLocalizedString("contextual.onboarding.final-screen.title", value: "You’ve got this!", comment: "Title of the last screen of the onboarding to the browser app") - static let onboardingFinalScreenMessage = NSLocalizedString("contextual.onboarding.final-screen.message", value: "Remember: every time you browse with me a creepy ad loses its wings. 👌", comment: "Message of the last screen of the onboarding to the browser app.") - static let onboardingFinalScreenButton = NSLocalizedString("contextual.onboarding.final-screen.button", value: "High five!", comment: "Button on the last screen of the onboarding, it will dismiss the onboarding screen.") - static let tryASearchOption1English = NSLocalizedString("contextual.onboarding.try-search.option1-English", value: "how to say “duck” in spanish", comment: "Browser Search query for how to say duck in english") - static let tryASearchOption1International = NSLocalizedString("contextual.onboarding.try-search.option1international", value: "how to say “duck” in english", comment: "Browser Search query for how to say duck in english") - static let tryASearchOption2English = NSLocalizedString("contextual.onboarding.try-search.option2-english", value: "mighty ducks cast", comment: "Search query for the cast of Mighty Ducks") - static let tryASearchOption2International = NSLocalizedString("contextual.onboarding.try-search.option2-international", value: "cast of avatar", comment: "Search query for the cast of Avatar") - static let tryASearchOption3 = NSLocalizedString("contextual.onboarding.try-search.option3", value: "local weather", comment: "Browser Search query for local weather") - static let tryASearchOptionSurpriseMeTitle = NSLocalizedString("contextual.onboarding.try-search.surprise-me-title", value: "Surprise me!", comment: "Title for a button that triggers an unknown search query for the user.") - static let tryASearchOptionSurpriseMeEnglish = NSLocalizedString("contextual.onboarding.try-search.surprise-me-english", value: "chocolate chip cookie recipes", comment: "Browser Search query for chocolate chip cookie recipes") - static let tryASearchOptionSurpriseMeInternational = NSLocalizedString("contextual.onboarding.try-search.surprise-me-international", value: "dinner recipes", comment: "Browser Search query for dinner recipes") - - static let daxDialogBrowsingWithOneTracker = NSLocalizedString("contextual.onboarding.browsing.one.tracker", value: "*%1$@* was trying to track you here. I blocked them!\n\n☝️ Tap the shield for more info.", comment: "Parameter is domain name (string)") - static let daxDialogBrowsingWithMultipleTrackers = NSLocalizedString("contextual.onboarding.browsing.multiple.trackers", comment: "First parameter is a count of additional trackers, second and third are names of the tracker networks (strings)") - } - } - - public enum HighlightsOnboardingExperiment { - enum Intro { - public static let title = NSLocalizedString("onboarding.highlights.intro.title", value: "Hi there.\n\nReady for a faster browser that keeps you protected?", comment: "The title of the onboarding dialog popup") - } - - enum BrowsersComparison { - public static let title = NSLocalizedString("onboarding.highlights.browsers.title", value: "Protections activated!", comment: "The title of the dialog to show the privacy features that DuckDuckGo offers") - - enum Features { public static let trackerBlockers = NSLocalizedString("onboarding.highlights.browsers.features.trackerBlocker.title", value: "Block 3rd party trackers", comment: "Message to highlight browser capability ofblocking 3rd party trackers") public static let cookiePopups = NSLocalizedString("onboarding.highlights.browsers.features.cookiePopups.title", value: "Block cookie requests & popups", comment: "Message to highlight how the browser allows you to block cookie pop-ups") public static let creepyAds = NSLocalizedString("onboarding.highlights.browsers.features.creepyAds.title", value: "Block targeted ads", comment: "Message to highlight browser capability of blocking creepy ads") @@ -1417,10 +1430,26 @@ But if you *do* want a peek under the hood, you can find more information about } enum ContextualOnboarding { + static let onboardingTryASearchTitle = NSLocalizedString("contextual.onboarding.try-a-search.title", value: "Ready to get started?\nTry a search!", comment: "Title of a popover on the browser that invites the user to try a search") static let onboardingTryASearchMessage = NSLocalizedString("contextual.onboarding.highlights.try-a-search.message", value: "Your DuckDuckGo searches are always private.", comment: "Message of a popover on the browser that invites the user to try a search explaining that their searches are private") static let onboardingFirstSearchDoneMessage = NSLocalizedString("contextual.onboarding.highlights.first-search-done.message", value: "That’s DuckDuckGo Search! Private. Fast. Fewer ads.", comment: "After the user performs their first search using the browser, this dialog explains the advantages of using DuckDuckGo") + static let onboardingTryASiteTitle = NSLocalizedString("contextual.onboarding.try-a-site.title", value: "Next, try visiting a site!", comment: "Title of a popover on the browser that invites the user to try a visiting a website") static let onboardingFinalScreenMessage = NSLocalizedString("contextual.onboarding.highlights.final-screen.message", value: "Remember: every time you browse with me a creepy ad loses its wings.", comment: "Message of the last screen of the onboarding to the browser app.") static let tryASearchOptionSurpriseMe = NSLocalizedString("contextual.onboarding.highlights.try-search.surprise-me", value: "baby ducklings", comment: "Browser Search query for baby ducklings") + static let onboardingTryASiteNTPTitle = NSLocalizedString("contextual.onboarding.ntp.try-a-site.title", value: "Try visiting a site!", comment: "Title of a popover on the new tab page browser that invites the user to try a visiting a website") + static let onboardingTryASiteMessage = NSLocalizedString("contextual.onboarding.try-a-site.message", value: "I’ll block trackers so they can’t spy on you.", comment: "Message of a popover on the browser that invites the user to try visiting a website to explain that we block trackers") + static let onboardingTryFireButtonMessage = NSLocalizedString("contextual.onboarding.try-fire-button.message", value: "Instantly clear your browsing activity with the Fire Button.\n\nGive it a try! 🔥", comment: "Message of a popover on the browser that invites the user to try visiting the browser Fire Button. Please leave the line break") + static let onboardingGotItButton = NSLocalizedString("contextual.onboarding.got-it.button", value: "Got it!", comment: "During onboarding steps this button is shown and takes either to the next steps or closes the onboarding.") + static let onboardingFinalScreenTitle = NSLocalizedString("contextual.onboarding.final-screen.title", value: "You’ve got this!", comment: "Title of the last screen of the onboarding to the browser app") + static let onboardingFinalScreenButton = NSLocalizedString("contextual.onboarding.final-screen.button", value: "High five!", comment: "Button on the last screen of the onboarding, it will dismiss the onboarding screen.") + static let tryASearchOption1English = NSLocalizedString("contextual.onboarding.try-search.option1-English", value: "how to say “duck” in spanish", comment: "Browser Search query for how to say duck in english") + static let tryASearchOption1International = NSLocalizedString("contextual.onboarding.try-search.option1international", value: "how to say “duck” in english", comment: "Browser Search query for how to say duck in english") + static let tryASearchOption2English = NSLocalizedString("contextual.onboarding.try-search.option2-english", value: "mighty ducks cast", comment: "Search query for the cast of Mighty Ducks") + static let tryASearchOption2International = NSLocalizedString("contextual.onboarding.try-search.option2-international", value: "cast of avatar", comment: "Search query for the cast of Avatar") + static let tryASearchOptionSurpriseMeTitle = NSLocalizedString("contextual.onboarding.try-search.surprise-me-title", value: "Surprise me!", comment: "Title for a button that triggers an unknown search query for the user.") + + static let daxDialogBrowsingWithOneTracker = NSLocalizedString("contextual.onboarding.browsing.one.tracker", value: "*%1$@* was trying to track you here. I blocked them!\n\n☝️ Tap the shield for more info.", comment: "Parameter is domain name (string)") + static let daxDialogBrowsingWithMultipleTrackers = NSLocalizedString("contextual.onboarding.browsing.multiple.trackers", comment: "First parameter is a count of additional trackers, second and third are names of the tracker networks (strings)") } enum FireDialog { diff --git a/DuckDuckGo/VPNAddWidgetTip.swift b/DuckDuckGo/VPNAddWidgetTip.swift index 8abedf86a9..465041431d 100644 --- a/DuckDuckGo/VPNAddWidgetTip.swift +++ b/DuckDuckGo/VPNAddWidgetTip.swift @@ -51,11 +51,11 @@ extension VPNAddWidgetTip: Tip { } var title: Text { - Text("Add VPN Widget") + Text(UserText.networkProtectionAddWidgetTipTitle) } var message: Text? { - Text("Turn the VPN on and off right from the Home Screen.") + Text(UserText.networkProtectionAddWidgetTipMessage) } var image: Image? { @@ -64,7 +64,7 @@ extension VPNAddWidgetTip: Tip { var actions: [Action] { [Action(id: ActionIdentifiers.addWidget.rawValue) { - Text("Add widget") + Text(UserText.networkProtectionAddWidgetTipAction) .foregroundStyle(Color(designSystemColor: .accent)) }] } diff --git a/DuckDuckGo/VPNGeoswitchingTip.swift b/DuckDuckGo/VPNGeoswitchingTip.swift index e2c3fe0b21..00d984848b 100644 --- a/DuckDuckGo/VPNGeoswitchingTip.swift +++ b/DuckDuckGo/VPNGeoswitchingTip.swift @@ -38,11 +38,11 @@ extension VPNGeoswitchingTip: Tip { } var title: Text { - Text("Change Your Location") + Text(UserText.networkProtectionGeoswitchingTipTitle) } var message: Text? { - Text("You can customize your VPN location by connecting to any of our servers worldwide.") + Text(UserText.networkProtectionGeoswitchingTipMessage) } var image: Image? { diff --git a/DuckDuckGo/VPNSnoozeTip.swift b/DuckDuckGo/VPNSnoozeTip.swift index aa4ab1f72a..0b301ad5ad 100644 --- a/DuckDuckGo/VPNSnoozeTip.swift +++ b/DuckDuckGo/VPNSnoozeTip.swift @@ -49,11 +49,11 @@ extension VPNSnoozeTip: Tip { } var title: Text { - Text("Avoid VPN Conflicts") + Text(UserText.networkProtectionSnoozeTipTitle) } var message: Text? { - Text("You can use sites or apps that block VPN traffic by snoozing the VPN connection.") + Text(UserText.networkProtectionSnoozeTipMessage) } var image: Image? { @@ -62,7 +62,7 @@ extension VPNSnoozeTip: Tip { var actions: [Action] { [Action(id: ActionIdentifiers.learnMore.rawValue) { - Text("Learn more") + Text(UserText.networkProtectionSnoozeTipAction) .foregroundStyle(Color(designSystemColor: .accent)) }] } diff --git a/DuckDuckGo/WidgetEducationView.swift b/DuckDuckGo/WidgetEducationView.swift index b4bbca2e2c..81095e78b8 100644 --- a/DuckDuckGo/WidgetEducationView.swift +++ b/DuckDuckGo/WidgetEducationView.swift @@ -72,7 +72,11 @@ struct WidgetEducationView: View { .padding(.horizontal) .padding(.top, Const.Padding.top) } - }.navigationBarTitle(navBarTitle, displayMode: .inline) + } + .navigationBarTitle(navBarTitle, displayMode: .inline) + .onFirstAppear { + Pixel.fire(pixel: .settingsNextStepsAddWidget) + } } private var secondParagraphText: Text { diff --git a/DuckDuckGo/bg.lproj/DaxOnboarding.strings b/DuckDuckGo/bg.lproj/DaxOnboarding.strings index e413a922bc..865e2118e6 100644 --- a/DuckDuckGo/bg.lproj/DaxOnboarding.strings +++ b/DuckDuckGo/bg.lproj/DaxOnboarding.strings @@ -1,12 +1,6 @@ /* Class = "UIButton"; normalTitle = "Hide"; ObjectID = "hgy-75-OvJ"; */ "hgy-75-OvJ.normalTitle" = "Скрий"; -/* Class = "UILabel"; text = "Welcome to\nDuckDuckGo!"; ObjectID = "Lvy-9G-3gr"; */ -"Lvy-9G-3gr.text" = "Добре дошли в\nDuckDuckGo!"; - -/* Class = "UIButton"; normalTitle = "Let’s Do It!"; ObjectID = "oAv-Zt-uIt"; */ -"oAv-Zt-uIt.normalTitle" = "Да го направим!"; - /* Class = "UIButton"; normalTitle = "Button"; ObjectID = "zJt-F9-L48"; */ "zJt-F9-L48.normalTitle" = "Бутон"; diff --git a/DuckDuckGo/bg.lproj/Localizable.strings b/DuckDuckGo/bg.lproj/Localizable.strings index 16bb673761..cd11e8d970 100644 --- a/DuckDuckGo/bg.lproj/Localizable.strings +++ b/DuckDuckGo/bg.lproj/Localizable.strings @@ -341,7 +341,7 @@ "autofill.keep-enabled.alert.disable" = "Забрани"; /* A link that takes the user to the DuckDuckGo help pages explaining password managers */ -"autofill.learn.more.link.title" = "Learn More"; +"autofill.learn.more.link.title" = "Научете повече"; /* Button displayed after saving/updating an autofill login that takes the user to the saved login */ "autofill.login-save-action-button.toast" = "Преглед"; @@ -977,9 +977,6 @@ /* ad = advertisment */ "dax.onboarding.home.subsequent" = "Нали разбрахте!\n\nЗапомнете: Всеки път, когато сърфирате с мен, аз подрязвам крилцата на някоя досадна реклама. 👍"; -/* No comment provided by engineer. */ -"dax.onboarding.message" = "Интернет може да бъде и доста опасно място.\n\nНе се притеснявайте! Поверителното търсене и сърфиране е много по-лесно, отколкото си мислите."; - /* No comment provided by engineer. */ "Debug" = "Отстраняване на грешки"; @@ -1080,7 +1077,7 @@ "duckPlayer.never.label" = "Никога"; /* Body text for the modal feature explanation */ -"duckplayer.presentation.modal.body" = "С Duck Player на DuckDuckGo можете да гледате YouTube без насочени реклами и да се чувствате като в киносалон, а вече гледаните видеоклипове няма да повлияят на препоръчаните."; +"duckplayer.presentation.modal.body" = "С Duck Player на DuckDuckGo можете да гледате YouTube без насочени реклами и вече гледаните видеоклипове няма да повлияят на препоръчаните."; /* Button that will dismiss the modal */ "duckplayer.presentation.modal.dismiss-button" = "Разбрах!"; @@ -1088,17 +1085,23 @@ /* Two line title (separated by \n) for the feature explanation */ "duckplayer.presentation.modal.title" = "Писна ли Ви от рекламите в YouTube? Опитайте Duck Player!"; +/* Two line title (separated by \n) for the feature explanation */ +"duckplayer.presentation.modal.title.youtube" = "Писна ли ви от рекламите в YouTube? Не и с Duck Player!"; + /* Footer label in the settings screen for Duck Player */ "duckplayer.settings.footer" = "В Privacy Essentials на DuckDuckGo са осигурени всички настройки за поверителност, необходими за защита при сърфиране в мрежата."; /* Text explaining what Duck Player is in the settings screen. */ -"duckplayer.settings.info-text" = "Duck Player осигурява чисто изживяване без персонализирани реклами в YouTube и предотвратява влиянието на вече гледаните видеоклипове върху препоръките на YouTube."; +"duckplayer.settings.info-text" = "С Duck Player на DuckDuckGo можете да гледате YouTube без насочени реклами и вече гледаните видеоклипове няма да повлияят на препоръчаните."; /* Button that takes the user to learn more about Duck Player. */ "duckplayer.settings.learn-more" = "Научете повече"; +/* Settings screen cell text for DuckPlayer settings to open in new tab */ +"duckplayer.settings.open-new-tab-label" = "Отворете Duck Player в нов раздел"; + /* Settings screen cell text for DuckPlayer settings */ -"duckplayer.settings.open-videos-in" = "Отваряне на видеоклипове в Duck Player"; +"duckplayer.settings.open-videos-in" = "Отваряне на видеоклипове от YouTube в Duck Player"; /* Settings screen cell text for DuckPlayer settings */ "duckplayer.settings.title" = "Duck Player"; @@ -1169,9 +1172,6 @@ /* Title for prompt to sign up for email protection */ "email.signup-prompt.title" = "Скрийте имейл адреса си и\nблокирайте тракерите"; -/* No comment provided by engineer. */ -"empty!" = "empty!"; - /* Empty list state placholder */ "empty.bookmarks" = "Все още няма добавени отметки"; @@ -1322,6 +1322,39 @@ /* Confirmation button */ "feedback.form.submit" = "Изпрати"; +/* Title of the feedback form */ +"feedback.form.title" = "Помощ за подобряване на Privacy Pro"; + +/* Category for Identity Theft Restoration feedback */ +"feedback.general.category.itr" = "Identity Theft Restoration"; + +/* Category for Personal Info Removal feedback */ +"feedback.general.category.pir" = "Премахване на лични данни"; + +/* Category for subscription and payments feedback */ +"feedback.general.category.ppro" = "Абонамент и плащания"; + +/* Prompt to select a category for general feedback */ +"feedback.general.category.select" = "ИЗБЕРЕТЕ КАТЕГОРИЯ"; + +/* Category for VPN feedback */ +"feedback.general.category.vpn" = "VPN"; + +/* Category for access code issues */ +"feedback.itr.category.access-code" = "Проблем с кода за достъп"; + +/* Category for issues contacting an advisor */ +"feedback.itr.category.cant-contact-advisor" = "Неуспешно свързване с консултант"; + +/* Prompt to select a category for Identity Theft Restoration feedback */ +"feedback.itr.category.select" = "ИЗБЕРЕТЕ КАТЕГОРИЯ"; + +/* Category for other Identity Theft Restoration issues */ +"feedback.itr.category.something-else" = "Нещо друго"; + +/* Category for unhelpful advisor calls */ +"feedback.itr.category.unhelpful" = "Разговорът с консултанта беше неефективен"; + /* No comment provided by engineer. */ "feedback.negative.form.genericPlaceholder" = "Моля, бъдете колкото може по-конкретни"; @@ -1361,6 +1394,24 @@ /* No comment provided by engineer. */ "feedback.performance.slowLoading" = "Уеб страниците или резултатите от търсене се зареждат бавно"; +/* Category for when scan finds incorrect records */ +"feedback.pir.category.not-me" = "Сканирането откри записи, които не са направени от мен"; + +/* Category for when scan doesn't find info on a specific site */ +"feedback.pir.category.nothing-on-site" = "Сканирането не откри моята информация на конкретен сайт"; + +/* Category for other Personal Info Removal issues */ +"feedback.pir.category.other" = "Нещо друго"; + +/* Category for when the removal process is stuck */ +"feedback.pir.category.removal-stuck" = "Процесът на премахване е блокирал"; + +/* Category for when the scan is stuck */ +"feedback.pir.category.scan-stuck" = "Сканирането за записи е блокирало"; + +/* Prompt to select a category for Personal Info Removal feedback */ +"feedback.pir.category.select" = "ИЗБЕРЕТЕ КАТЕГОРИЯ"; + /* Header above input field */ "feedback.positive.form.header" = "Споделете подробности"; @@ -1379,6 +1430,15 @@ /* Button encouraging uses to share details aboout their feedback */ "feedback.positive.submit" = "Споделете подробности"; +/* Category for other Privacy Pro issues */ +"feedback.ppro.category.other" = "Нещо друго"; + +/* Category for one-time password issues */ +"feedback.ppro.category.otp" = "Проблем с еднократна парола"; + +/* Prompt to select a category for Privacy Pro feedback */ +"feedback.ppro.category.select" = "ИЗБЕРЕТЕ КАТЕГОРИЯ"; + /* No comment provided by engineer. */ "feedback.start.footer" = "Вашите анонимни отзиви са важни за нас."; @@ -1529,12 +1589,6 @@ /* No comment provided by engineer. */ "keyCommandShowAllTabs" = "Показване на всички раздели"; -/* Please preserve newline character */ -"launchscreenWelcomeMessage" = "Добре дошли в\nDuckDuckGo!"; - -/* No comment provided by engineer. */ -"LOREM IPSUM" = "LOREM IPSUM"; - /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "DuckDuckGo за Mac притежава необходимата скорост, както и очакваните от Вас функции за сърфиране, и предлага най-добрите в този клас елементи за защита."; @@ -1586,62 +1640,83 @@ /* Edit button */ "navigation.title.edit" = "Редактиране"; -/* String indicating NetP is connected when viewed from the settings screen */ -"netP.cell.connected" = "Connected"; - -/* String indicating NetP is disconnected when viewed from the settings screen */ -"netP.cell.disconnected" = "Not connected"; - -/* Title for the DuckDuckGo VPN feature in settings */ -"netP.settings.title" = "VPN"; +/* String indicating NetP is snoozing when viewed from the settings screen */ +"netP.cell.snoozing" = "Отлагане"; /* Title for the DuckDuckGo VPN feature */ "netP.title" = "DuckDuckGo VPN"; +/* Button title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.action" = "Добави приспособлението"; + +/* Message for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.message" = "Можете да включвате и изключвате VPN направо от началния екран."; + +/* Title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.title" = "Добавете приспособлението VPN"; + /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN връзката е прекъсната поради изтекъл абонамент. Абонирайте се за Privacy Pro, за да свържете отново DuckDuckGo VPN."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Неуспешно свързване на мрежовата защита. Моля, опитайте отново по-късно."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "DuckDuckGo VPN не можа да се свърже. Моля, опитайте отново по-късно."; /* Title for the VPN FAQ screen. */ "network.protection.faq.title" = "Често задавани въпроси за DuckDuckGo VPN"; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Мрежовата защита е прекъсната. Извършва се опит за повторно свързване..."; +/* Message for tooltip about geoswitching */ +"network.protection.geoswitching.tip.message" = "Можете да персонализирате Вашето VPN местоположение, като се свържете с някой от нашите сървъри по целия свят."; + +/* Title for tooltip about geoswitching */ +"network.protection.geoswitching.tip.title" = "Променете Вашето местоположение"; + +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "DuckDuckGo VPN беше прекъснат. Извършва се опит за повторно свързване..."; /* Message for the network protection invite dialog */ -"network.protection.invite.dialog.message" = "Enter your invite code to get started."; +"network.protection.invite.dialog.message" = "Въведете кода за покана, за да започнете."; /* Title for the network protection invite screen */ "network.protection.invite.dialog.title" = "Каним Ви да изпробвате DuckDuckGo VPN"; /* Prompt for the network protection invite code text field */ -"network.protection.invite.field.prompt" = "Invite Code"; +"network.protection.invite.field.prompt" = "Код за покана"; /* Message for the network protection invite success view */ -"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; +"network.protection.invite.success.message" = "Скрийте местоположението си от уебсайтовете и онлайн активността си от доставчиците на интернет и другите потребители в мрежата."; /* Title for the network protection invite success view */ -"network.protection.invite.success.title" = "Success! You’re in."; +"network.protection.invite.success.title" = "Успешна заявка! Вече сте регистрирани."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; /* Title text for an iOS quick action that opens VPN settings */ "network.protection.quick-action.open-vpn" = "Отваряне на VPN"; +/* Button title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.action" = "Научете повече"; + +/* Message for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.message" = "Можете да използвате сайтове или приложения, които блокират VPN трафика, като изключите временно VPN връзката."; + +/* Title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.title" = "Така ще избегнете на конфликти с VPN"; + +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN е отложен за %@"; + /* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ "network.protection.status.connected.format" = "Свързан · %@"; /* The label for the NetP VPN when connecting */ -"network.protection.status.connecting" = "Connecting..."; +"network.protection.status.connecting" = "Свързване..."; /* The label for the NetP VPN when disconnected */ -"network.protection.status.disconnected" = "Not connected"; +"network.protection.status.disconnected" = "Няма връзка"; /* The label for the NetP VPN when disconnecting */ -"network.protection.status.disconnecting" = "Disconnecting..."; +"network.protection.status.disconnecting" = "Прекъсване..."; /* Message label text for the status view when VPN is disconnected */ "network.protection.status.header.message.off" = "Свържете се, за да защитите всичките си устройства\nИнтернет трафик."; @@ -1655,26 +1730,41 @@ /* Header title label text for the status view when VPN is connected */ "network.protection.status.header.title.on" = "DuckDuckGo VPN е включен"; +/* Header title label text for the status view when VPN is snoozing */ +"network.protection.status.header.title.snoozed" = "VPN на DuckDuckGo е отложен"; + /* The status view 'Share Feedback' button which is shown inline on the status view after the temporary free use footer text */ -"network.protection.status.menu.share.feedback" = "Share Feedback"; +"network.protection.status.menu.share.feedback" = "Споделяне на отзив"; + +/* The label for the NetP VPN when paused */ +"network.protection.status.paused" = "На пауза"; + +/* The label for when NetP VPN is snoozing plus the length of time remaining formatted as '0:00' */ +"network.protection.status.snoozing.format" = "Отлагане, остават %@"; + +/* Snooze button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.snooze" = "Отлагане за 20 минути"; + +/* Wake Up button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.wake-up" = "Събуждане"; /* Connection details label shown in NetworkProtection's status view. */ -"network.protection.status.view.connection.details" = "Connection Details"; +"network.protection.status.view.connection.details" = "Подробности за връзката"; /* Custom DNS label shown in NetworkProtection's status view. */ -"network.protection.status.view.custom.dns" = "DNS Server"; +"network.protection.status.view.custom.dns" = "DNS сървър"; /* Generic connection failed error message shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.message" = "Please try again later."; +"network.protection.status.view.error.connection.failed.message" = "Моля, опитайте отново по-късно."; /* Generic connection failed error title shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; +"network.protection.status.view.error.connection.failed.title" = "Неуспешно свързване."; /* IP Address label shown in NetworkProtection's status view. */ -"network.protection.status.view.ip.address" = "IP Address"; +"network.protection.status.view.ip.address" = "IP адрес"; /* Location label shown in NetworkProtection's status view. */ -"network.protection.status.view.location" = "Location"; +"network.protection.status.view.location" = "Местоположение"; /* Label shown on the title of the settings section in NetworkProtection's status view. */ "network.protection.status.view.settings.section.title" = "Управление"; @@ -1682,12 +1772,15 @@ /* Title label text for the status view when netP is disconnected */ "network.protection.status.view.title" = "VPN"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Мрежовата защита е включена. Вашето местоположение и онлайн активност са защитени."; +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "DuckDuckGo VPN е включен. Вашето местоположение и онлайн активност са защитени."; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Трафикът на устройството се маршрутизира през %@."; +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "Отлагането на VPN приключи. Трафикът на устройството се маршрутизира през %@."; + /* Title for the button to link to the iOS app settings and enable notifications app-wide. */ "network.protection.turn.on.notifications.button.title" = "Включване на известията"; @@ -1698,13 +1791,13 @@ "network.protection.vpn.about" = "За нас"; /* Section header for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.section.header" = "Notifications"; +"network.protection.vpn.alerts.section.header" = "Известия"; /* List section footer for the toggle for VPN alerts. */ "network.protection.vpn.alerts.toggle.section.footer" = "Получавайте известия при прекъсване на връзката или промяна на състоянието на VPN."; /* Title for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.toggle.title" = "VPN Notifications"; +"network.protection.vpn.alerts.toggle.title" = "Известия за VPN"; /* Title for the data volume section in the VPN status screen */ "network.protection.vpn.data-volume" = "Обем от данни"; @@ -1713,13 +1806,13 @@ "network.protection.vpn.exclude.local.networks.setting.footer" = "Можете да разрешите локалният трафик да заобикаля VPN услугата при свързване с устройства в локалната мрежа, като принтер например."; /* Header text for the Exclude Local Networks setting item. */ -"network.protection.vpn.exclude.local.networks.setting.header" = "General"; +"network.protection.vpn.exclude.local.networks.setting.header" = "Общо"; /* Title for the Exclude Local Networks setting item. */ "network.protection.vpn.exclude.local.networks.setting.title" = "Изключване на локални мрежи"; /* Title for the VPN Location screen's All Countries section. */ -"network.protection.vpn.location.all.countries.section.title" = "All Countries"; +"network.protection.vpn.location.all.countries.section.title" = "Всички държави"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.connected" = "Свързано местоположение"; @@ -1734,7 +1827,7 @@ "network.protection.vpn.location.recommended.section.footer" = "Автоматично се свързваме с най-близкия сървър, който можем да намерим."; /* Title for the VPN Location screen's Recommended section. */ -"network.protection.vpn.location.recommended.section.title" = "Recommended"; +"network.protection.vpn.location.recommended.section.title" = "Препоръчително"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.selected" = "Избрано местоположение"; @@ -1752,13 +1845,13 @@ "network.protection.vpn.preferred.location.nearest" = "Най-близко местоположение"; /* Title for the Preferred Location VPN Settings item. */ -"network.protection.vpn.preferred.location.title" = "Preferred Location"; +"network.protection.vpn.preferred.location.title" = "Предпочитано местоположение"; /* Footer text for the DNS server setting item. */ -"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo routes DNS queries through our DNS servers so your internet provider can't see what websites you visit."; +"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo пренасочва DNS заявките през нашите DNS сървъри, за да не вижда интернет доставчикът какви уебсайтове посещавате."; /* Title for the FAQ row in the VPN status screen. */ -"network.protection.vpn.settings.faq" = "FAQs and Support"; +"network.protection.vpn.settings.faq" = "Често задавани въпроси и поддръжка"; /* Title for the feedback row in the VPN status screen. */ "network.protection.vpn.settings.share-feedback" = "Споделяне на отзив за VPN"; @@ -1841,8 +1934,6 @@ /* The title of the dialog to show the privacy features that DuckDuckGo offers */ "onboarding.browsers.title" = "Защитата на поверителността е активирана!"; -/* Subheader message for the screen to choose DuckDuckGo as default browser */ -"onboarding.defaultBrowser.message" = "Отваряйте връзки без притеснения, всеки път."; /* The message of the option to set the address bar to the bottom. */ "onboarding.highlights.addressBarPosition.bottom.message" = "По-лесен достъп"; @@ -1942,6 +2033,66 @@ /* Deactivate button */ "pm.deactivate" = "Деактивиране"; +/* Label for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.label" = "Имейл (по избор)"; + +/* Placeholder for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.placeholder" = "име@имейл.com"; + +/* Placeholder for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.placeholder" = "Моля, споделете своето мнение…"; + +/* Title for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.title" = "Отзив с обща информация"; + +/* Title for the ITR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-itr-problem.title" = "Identity Theft Restoration"; + +/* Title for the PIR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-pir-problem.title" = "Personal Information Removal"; + +/* Title for the Subscriptions and Payments category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-ppro-problem.title" = "Абонаменти и плащания"; + +/* Placeholder for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.placeholder" = "Кажете ни какъв е проблемът…"; + +/* Title for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.title" = "Докладване на проблем"; + +/* Title for the VPN category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-vpn-problem.title" = "VPN"; + +/* Placeholder for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.placeholder" = "Каква функция бихте искали да въведем?"; + +/* Title for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.title" = "Заявка за функция"; + +/* Title for the category selection section in the Privacy Pro feedback form */ +"ppro.feedback-form.select-category.title" = "ИЗБЕРЕТЕ КАТЕГОРИЯ"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-1" = "Попаднахте на проблем, който не е разгледан в нашия [помощен център](duck://)? Определено искаме да знаем за това.\n\nПосочете имейл, ако искате да се свържем с Вас относно този проблем (може да не успеем да изпратим отговор за всички проблеми):"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-2" = "В допълнение към данните, въведени по-горе, изпращаме анонимна информация с вашия отзив:"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-3" = "• Дали някои функции на браузъра са активни"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-4" = "• Обобщени диагностични данни за приложението (напр. кодове за грешки)"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-5" = "С натискането на бутона „Изпращане“ се съгласявате, че DuckDuckGo може да използва предоставената информация за подобряване на приложението."; + +/* Title for the text box in the Privacy Pro feedback form */ +"ppro.feedback-form.text-box.title" = "Обратна връзка"; + +/* Title for the Privacy Pro feedback form */ +"ppro.feedback-form.title" = "Изпратете отзив"; + /* Button title for sync bookmarks limits exceeded warning to go to manage bookmarks */ "prefrences.sync.bookmarks-limit-exceeded-action" = "Управление на отметки"; @@ -2031,6 +2182,18 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Любими"; +/* Name of the option the user can chose to give general browser feedback */ +"send.browser.feedback.general-feedback" = "Отзив с обща информация"; + +/* Name of the option the user can chose to give browser feedback about a problem they enountered */ +"send.browser.feedback.report-problem" = "Докладване на проблем"; + +/* Name of the option the user can chose to give browser feedback about a feature they would like */ +"send.browser.feedback.request-feature" = "Заявка за функция"; + +/* Title of the picker where the user can chose the category of the feedback they want ot send. */ +"send.browser.feedback.select-category" = "ИЗБЕРЕТЕ КАТЕГОРИЯ"; + /* Settings cell for About DDG */ "settings.about.ddg" = "Относно DuckDuckGo"; @@ -2086,6 +2249,9 @@ /* Section footer Autolock description */ "settings.autolock.description" = "Ако сте активирали пръстов отпечатък, лицево разпознаване или системна парола, ще бъдете приканени да отключите приложението при отваряне."; +/* Settings cell for Browser Feedback */ +"settings.browser.feedback" = "Отзив за браузъра"; + /* Settings screen cell text for Automatically Clearing Data */ "settings.clear.data" = "Автоматично изчистване на данните"; @@ -2184,7 +2350,7 @@ "settings.ppro" = "Privacy Pro"; /* Title for Link in the Footer of Privacy Pro section */ -"settings.ppro.footer" = "Privacy Policy and Terms of Service"; +"settings.ppro.footer" = "Политика за поверителност и Условия за ползване"; /* Settings screen cell for long press previews */ "settings.previews" = "Прегледи с продължително натискане"; @@ -2210,23 +2376,20 @@ /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Задайте позицията на адресната лента"; -/* Subscription activation pending description */ -"settings.subscription.activation.pending.description" = "This is taking longer than usual, please check back later."; +/* Privacy pro description subtitle in settings when the is activating */ +"settings.subscription.activating" = "Активиране"; -/* Subscription activation pending title */ -"settings.subscription.activation.pending.title" = "Вашият абонамент се активира"; +/* Subscription activation pending description */ +"settings.subscription.activation.pending.description" = "Това отнема повече време от обикновено, върнете се по-късно."; /* Data Broker protection cell subtitle for privacy pro */ -"settings.subscription.DBP.subtitle" = "Remove your info from sites that sell it"; +"settings.subscription.DBP.subtitle" = "Премахване на личната информация от сайтове, които я продават"; /* Data Broker protection cell title for privacy pro */ "settings.subscription.DBP.title" = "Personal Information Removal"; -/* Privacy pro description subtext */ -"settings.subscription.description" = "По-лесна защита на личните данни с три нови защити:"; - /* I have a Subscription button text for privacy pro */ -"settings.subscription.existing.subscription" = "I Have a Subscription"; +"settings.subscription.existing.subscription" = "Имам абонамент"; /* Subscription expired description */ "settings.subscription.expired.comment" = "Абонирайте се отново, за да продължите да използвате Privacy Pro"; @@ -2234,23 +2397,26 @@ /* Subscription expired tittle message */ "settings.subscription.expired.title" = "Абонамент ви за Privacy Pro е изтекъл"; -/* Privacy pro features list */ -"settings.subscription.features" = " • VPN\n • Personal Information Removal\n • Identity Theft Restoration"; - /* Identity theft restoration cell subtitle for privacy pro */ -"settings.subscription.ITR.subtitle" = "If your identity is stolen, we'll help restore it"; +"settings.subscription.ITR.subtitle" = "Ако самоличността Ви бъде открадната, ние ще помогнем да я възстановите"; /* Identity theft restoration cell title for privacy pro */ "settings.subscription.ITR.title" = "Identity Theft Restoration"; /* Get Privacy Pro button text for privacy pro */ -"settings.subscription.learn.more" = "Get Privacy Pro"; +"settings.subscription.learn.more" = "Вземете Privacy Pro"; /* Subscription Settings button text for privacy pro */ -"settings.subscription.manage" = "Subscription Settings"; +"settings.subscription.manage" = "Настройки на абонамент"; + +/* Privacy Pro description subtitle in settings */ +"settings.subscription.row.description" = "Включва нашите функции VPN и Identity Theft Restoration."; -/* Call to action title for Privacy Pro */ -"settings.subscription.subscribe" = "Subscribe to Privacy Pro"; +/* Call to action title for Privacy Pro settings */ +"settings.subscription.subscribe" = "Защитете връзката и самоличността си с Privacy Pro"; + +/* Privacy pro description subtitle in settings */ +"settings.subscription.us.description" = "Включва нашите функции VPN, Personal Information Removal и Identity Theft Restoration."; /* VPN cell title for privacy pro */ "settings.subscription.VPN.title" = "VPN"; @@ -2319,67 +2485,64 @@ "siteFeedback.urlPlaceholder" = "Кой уебсайт е повреден?"; /* Subscription Activation Info */ -"subscription.activate..header.description" = "Access your Privacy Pro subscription on this device via Apple ID or an email address."; +"subscription.activate..header.description" = "Използвайте абонамента си за Privacy Pro на това устройство чрез Apple ID или имейл адрес."; /* Button for adding email address to subscription */ -"subscription.activate.add.email.button" = "Add Email"; +"subscription.activate.add.email.button" = "Добавяне на имейл"; /* Apple ID option for activation */ "subscription.activate.appleid" = "Apple ID"; /* Button text for restoring purchase via Apple ID */ -"subscription.activate.appleid.button" = "Restore Purchase"; +"subscription.activate.appleid.button" = "Възстановяване на покупката"; /* Description for Apple ID activation */ -"subscription.activate.appleid.description" = "Restore your purchase to activate your subscription on this device."; +"subscription.activate.appleid.description" = "Възстановете покупката си, за да активирате абонамента на това устройство."; /* Subscription Activation Info */ -"subscription.activate.description" = "Your subscription is automatically available in DuckDuckGo on any device signed in to your Apple ID."; +"subscription.activate.description" = "Достъпът с абонамент става автоматично в DuckDuckGo на всяко устройство, което е вписано с Вашия Apple ID."; /* Button for editing email address added to subscription */ -"subscription.activate.edit.email.button" = "Edit Email"; +"subscription.activate.edit.email.button" = "Редактиране на имейл"; /* View Title for editing your email account */ -"subscription.activate.edit.email.title" = "Edit Email"; +"subscription.activate.edit.email.title" = "Редактиране на имейл"; /* Email option for activation */ -"subscription.activate.email" = "Email"; +"subscription.activate.email" = "Имейл"; /* Restore button title for Email */ -"subscription.activate.email.button" = "Enter Email"; +"subscription.activate.email.button" = "Въведете имейл"; /* Description for Email activation */ -"subscription.activate.email.description" = "Use your email to activate your subscription on this device."; +"subscription.activate.email.description" = "Използвайте имейла си, за да активирате абонамента на това устройство."; /* Activate subscription title */ "subscription.activate.email.title" = "Активиране на абонамент"; /* Button title for cancelling email deletion */ -"subscription.activate.manage.email.cancel" = "Cancel"; +"subscription.activate.manage.email.cancel" = "Отмени"; /* Button title for confirming email deletion */ -"subscription.activate.manage.email.OK" = "OK"; +"subscription.activate.manage.email.OK" = "ОК"; /* Restore button title for AppleID */ -"subscription.activate.restore.apple" = "Restore Purchase"; +"subscription.activate.restore.apple" = "Възстановяване на покупката"; /* Subscription Activation Title */ -"subscription.activate.title" = "Activate your subscription on this device"; +"subscription.activate.title" = "Активирайте абонамента си на това устройство"; /* Resend activation instructions button */ -"subscription.add.device.resend.instructions" = "Resend Instructions"; +"subscription.add.device.resend.instructions" = "Повторно изпращане на инструкции"; /* Add email to an existing subscription */ -"subscription.add.email" = "Add an email address to activate your subscription on your other devices. We’ll only use this address to verify your subscription."; +"subscription.add.email" = "Добавете имейл адрес, за да активирате абонамента си на други устройства. Ще използваме този адрес само за потвърждаване на абонамента."; /* View title for adding email to subscription */ -"subscription.add.email.title" = "Add Email"; - -/* Title for Alert messages */ -"subscription.alert.title" = "subscription.alert.title"; +"subscription.add.email.title" = "Добавяне на имейл"; /* Subscription availability message on Apple devices */ -"subscription.available.apple" = "Privacy Pro is available on any device signed in to the same Apple ID."; +"subscription.available.apple" = "Функцията Privacy Pro може да се използва на всяко устройство, което е вписано със същия Apple ID."; /* Text for the manage billing page */ "subscription.billing.google.text" = "Вашият абонамент е закупен през Google Play Store. За да подновите абонамента си, отворете настройките за абонамент в Google Play Store на устройство, вписано в същия акаунт в Google, който е използван при първоначално закупуване на абонамента."; @@ -2387,89 +2550,86 @@ /* Title for the manage billing page */ "subscription.billing.google.title" = "Абонаментни планове"; -/* Subscription annual billing period type */ -"subscription.billing.period.annual" = "annual"; - -/* Subscription monthly billing period type */ -"subscription.billing.period.monthly" = "monthly"; - /* Subscription Removal confirmation message */ -"subscription.cancel.message" = "Your subscription has been removed from this device."; +"subscription.cancel.message" = "Абонаментът е премахнат от това устройство."; /* Change plan or cancel title */ -"subscription.change.plan" = "Update Plan or Cancel"; +"subscription.change.plan" = "Актуализиране или отмяна на плана"; /* Navigation Button for closing subscription view */ -"subscription.close" = "Close"; +"subscription.close" = "Затваряне"; /* Title for Confirm messages */ -"subscription.confirm.title" = "Are you sure?"; +"subscription.confirm.title" = "Сигурни ли сте?"; /* Header for section for activating subscription on other devices */ -"subscription.devices.header" = "Activate on Other Devices"; +"subscription.devices.header" = "Активиране на други устройства"; /* Footer for section for activating subscription on other devices when email was not yet added */ -"subscription.devices.no.email.footer" = "Add an optional email to your subscription or use your Apple ID to access Privacy Pro on other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.no.email.footer" = "Добавете допълнителен имейл към абонамента, за да получите достъп до Privacy Pro на други устройства. **[Научете повече](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Footer for section for activating subscription on other devices when email is added */ -"subscription.devices.with.email.footer" = "Use this email to activate your subscription in Settings > Privacy Pro in the DuckDuckGo app on your other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.with.email.footer" = "Използвайте този имейл, за да активирате абонамента от Настройки > Privacy Pro в приложението DuckDuckGo на други устройства. **[Научете повече](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Alert content for not found subscription */ -"subscription.email.inactive.alert.message" = "The subscription associated with this email is no longer active."; +"subscription.email.inactive.alert.message" = "Абонаментът, свързан с този имейл, вече не е активен."; /* Alert title for not found subscription */ -"subscription.email.inactive.alert.title" = "Subscription Not Found"; +"subscription.email.inactive.alert.title" = "Не е намерен абонамент"; /* Alert content for not found subscription */ -"subscription.expired.alert.message" = "The subscription associated with this Apple ID is no longer active."; +"subscription.expired.alert.message" = "Абонаментът, свързан с този Apple ID, вече не е активен."; /* FAQ Button */ -"subscription.faq" = "FAQs and Support"; +"subscription.faq" = "Често задавани въпроси и поддръжка"; /* FAQ Description */ -"subscription.faq.description" = "Get answers to frequently asked questions or contact Privacy Pro support from our help pages."; +"subscription.faq.description" = "Вижте отговори на често задавани въпроси или се свържете с поддръжката на Privacy Pro от нашите страници за помощ."; + +/* Send Feedback Button */ +"subscription.feedback" = "Изпратете отзив"; /* Cancel action for the existing subscription dialog */ -"subscription.found.cancel" = "Cancel"; +"subscription.found.cancel" = "Отмени"; /* Restore action for the existing subscription dialog */ -"subscription.found.restore" = "Restore"; +"subscription.found.restore" = "Възстановяване"; /* Message for the existing subscription dialog */ -"subscription.found.text" = "We found a subscription associated with this Apple ID."; +"subscription.found.text" = "Открихме абонамент, свързан с този Apple ID."; /* Title for the existing subscription dialog */ -"subscription.found.title" = "Subscription Found"; +"subscription.found.title" = "Намерен е абонамент"; /* Help and support Section header */ -"subscription.help" = "Help and support"; +"subscription.help" = "Помощ и поддръжка"; /* Manage Plan header */ -"subscription.manage.plan" = "Manage Plan"; +"subscription.manage.plan" = "Управление на плана"; /* Header for the subscription section */ -"subscription.manage.title" = "Subscription"; +"subscription.manage.title" = "Абонамент"; /* Alert content for not found subscription */ -"subscription.notFound.alert.message" = "There is no subscription associated with this Apple ID."; +"subscription.notFound.alert.message" = "Няма абонамент, свързан с този Apple ID."; /* Alert title for not found subscription */ -"subscription.notFound.alert.title" = "Subscription Not Found"; +"subscription.notFound.alert.title" = "Не е намерен абонамент"; /* View plans button text */ -"subscription.notFound.view.plans" = "View Plans"; +"subscription.notFound.view.plans" = "Преглед на плановете"; /* Hero Text for Personal information removal */ -"subscription.pir.hero" = "Activate Privacy Pro on desktop to set up Personal Information Removal"; +"subscription.pir.hero" = "Активирайте Privacy Pro на настолен компютър, за да настроите Personal Information Removal"; /* Description on how to use Personal information removal in desktop. The first placeholder references a location in the Desktop application. Privacy Pro>, and the second, the menu entry. i.e. */ -"subscription.pir.heroText" = "In the DuckDuckGo browser for desktop, go to %1$@ and click %2$@ to get started."; +"subscription.pir.heroText" = "В браузъра DuckDuckGo за настолен компютър отворете %1$@ и изберете %2$@, за да започнете."; /* Settings references a menu in the Desktop app, Privacy Pro, references our product name */ -"subscription.pir.heroTextLocation" = "Settings > Privacy Pro"; +"subscription.pir.heroTextLocation" = "Настройки > Privacy Pro"; /* Menu item for enabling Personal Information Removal on Desktop */ -"subscription.pir.heroTextMenyEntry" = "I have a subscription"; +"subscription.pir.heroTextMenyEntry" = "Имам абонамент"; /* Text for the 'macOS' button */ "subscription.pir.macos" = "Mac"; @@ -2478,28 +2638,28 @@ "subscription.pir.windows" = "Windows"; /* Progress view title when completing the purchase */ -"subscription.progress.view.completing.purchase" = "Completing purchase..."; +"subscription.progress.view.completing.purchase" = "Завършване на покупката..."; /* Progress view title when starting the purchase */ -"subscription.progress.view.purchasing.subscription" = "Purchase in progress..."; +"subscription.progress.view.purchasing.subscription" = "Покупката се извършва..."; /* Progress view title when restoring past subscription purchase */ -"subscription.progress.view.restoring.subscription" = "Restoring subscription..."; +"subscription.progress.view.restoring.subscription" = "Възстановяване на абонамента..."; /* Remove from this device button */ -"subscription.remove.from.device.button" = "Remove From This Device"; +"subscription.remove.from.device.button" = "Премахване от това устройство"; /* Remove from device confirmation dialog text */ -"subscription.remove.from.device.text" = "You will no longer be able to access your Privacy Pro subscription on this device. This will not cancel your subscription, and it will remain active on your other devices."; +"subscription.remove.from.device.text" = "Вече няма да имате достъп до абонамента си за Privacy Pro на това устройство. Това няма да отмени абонамента и той ще остане активен на останалите устройства."; /* Remove from device confirmation dialog title */ -"subscription.remove.from.device.title" = "Remove from this device?"; +"subscription.remove.from.device.title" = "Премахване от това устройство?"; /* Remove subscription button text */ -"subscription.remove.subscription" = "Remove Subscription"; +"subscription.remove.subscription" = "Премахване на абонамент"; /* Remove subscription cancel button text */ -"subscription.remove.subscription.cancel" = "Cancel"; +"subscription.remove.subscription.cancel" = "Отмени"; /* Button text for general error message */ "subscription.restore.backend.error.button" = "Назад към Настройки"; @@ -2511,28 +2671,43 @@ "subscription.restore.backend.error.title" = "Възникна някаква грешка"; /* Alert for general error message */ -"subscription.restore.general.error.message" = "The App Store was unable to process your purchase. Please try again later."; +"subscription.restore.general.error.message" = "App Store не успя да обработи Вашата покупка. Моля, опитайте отново по-късно."; /* Alert for general error title */ -"subscription.restore.general.error.title" = "Something Went Wrong"; +"subscription.restore.general.error.title" = "Възникна някаква грешка"; /* Alert button text for restored purchase alert */ -"subscription.restore.success.alert.button" = "OK"; +"subscription.restore.success.alert.button" = "ОК"; /* Alert message for restored purchase */ -"subscription.restore.success.alert.message" = "Your purchases have been restored."; +"subscription.restore.success.alert.message" = "Вашите покупки са възстановени."; /* Alert title for restored purchase */ -"subscription.restore.success.alert.title" = "You’re all set."; +"subscription.restore.success.alert.title" = "Всичко е готово."; + +/* Subtitle in header when subscribed */ +"subscription.subscribed" = "Активен абонамент"; /* Subscription Expired Data. This reads as 'Your subscription expired on (date)' */ "subscription.subscription.expired.caption" = "Абонаментът ви е изтекъл на %@"; -/* Subscription expiration info. This reads as 'Your (monthly or annual) subscription expires on (date)' */ -"subscription.subscription.expiring.caption" = "Your %1$@ subscription expires on %2$@."; +/* Monthly subscription expiration info where parameter is expiration date. This reads as 'Your monthly subscription expires on (date)' */ +"subscription.subscription.expiring.monthly.caption" = "Месечният ви абонамент изтича на %@."; + +/* Unknown period subscription expiration info where parameter is expiration date. This reads as 'Your subscription expires on (date)' */ +"subscription.subscription.expiring.unknown.caption" = "Абонаментът ви изтича на %@."; + +/* Annual subscription expiration info where parameter is expiration date. This reads as 'Your annual subscription expires on (date)' */ +"subscription.subscription.expiring.yearly.caption" = "Годишният ви абонамент изтича на %@."; + +/* Monthly subscription renewal info where parameter is renewal date. This reads as 'Your monthly subscription renews on (date)' */ +"subscription.subscription.renewing.monthly.caption" = "Месечният ви абонамент се подновява на %@."; + +/* Unknown period subscription renewal info where parameter is renewal date. This reads as 'Your subscription renews on (date)' */ +"subscription.subscription.renewing.unknown.caption" = "Абонаментът ви се подновява на %@."; -/* Subscription renewal info. This reads as 'Your (monthly or annual) subscription renews on (date)' */ -"subscription.subscription.renewing.caption" = "Your %1$@ subscription renews on %2$@."; +/* Annual subscription renewal info where parameter is renewal date. This reads as 'Your annual subscription renews on (date)' */ +"subscription.subscription.renewing.yearly.caption" = "Годишният ви абонамент се подновява на %@."; /* Navigation bar Title for subscriptions */ "subscription.title" = "Privacy Pro"; @@ -2679,118 +2854,118 @@ "voiceSearch.footer.note.old" = "Аудиото се обработва на устройството. То не се съхранява или споделя с никого, включително DuckDuckGo."; /* Cancel action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.cancel" = "Dismiss"; +"vpn.access-revoked.alert.action.cancel" = "Отказване"; /* Primary action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.subscribe" = "Subscribe"; +"vpn.access-revoked.alert.action.subscribe" = "Абонамент"; /* Alert message for the alert when the Privacy Pro subscription expiress */ -"vpn.access-revoked.alert.message" = "Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"vpn.access-revoked.alert.message" = "Абонирайте се за Privacy Pro, за да свържете отново DuckDuckGo VPN."; /* Alert title for the alert when the Privacy Pro subscription expires */ -"vpn.access-revoked.alert.title" = "VPN disconnected due to expired subscription"; +"vpn.access-revoked.alert.title" = "VPN връзката е прекъсната поради изтекъл абонамент"; /* Title for the VPN widget onboarding screen */ "vpn.addWidget.settings.title" = "Открийте и изберете DuckDuckGo. След това плъзнете до VPN и изберете „Добавяне на уиджет“."; /* Title for the Cancel button of the VPN feedback form */ -"vpn.feedback-form.button.cancel" = "Cancel"; +"vpn.feedback-form.button.cancel" = "Отмени"; /* Title for the Done button of the VPN feedback form */ -"vpn.feedback-form.button.done" = "Done"; +"vpn.feedback-form.button.done" = "Готово"; /* Title for the Submit button of the VPN feedback form */ -"vpn.feedback-form.button.submit" = "Submit"; +"vpn.feedback-form.button.submit" = "Изпрати"; /* Title for the Submitting state of the VPN feedback form */ -"vpn.feedback-form.button.submitting" = "Submitting…"; +"vpn.feedback-form.button.submitting" = "Изпращане…"; /* Title for the browser crash/freeze category of the VPN feedback form */ -"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN causes browser to crash or freeze"; +"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN причинява срив или замръзване на браузъра"; /* Title for the 'VPN fails to connect' category of the VPN feedback form */ -"vpn.feedback-form.category.fails-to-connect" = "VPN fails to connect"; +"vpn.feedback-form.category.fails-to-connect" = "VPN не може да се свърже"; /* Title for the 'VPN feature request' category of the VPN feedback form */ -"vpn.feedback-form.category.feature-request" = "VPN feature request"; +"vpn.feedback-form.category.feature-request" = "Заявка за VPN функция"; /* Title for the category 'VPN causes issues with other apps or websites' category of the VPN feedback form */ -"vpn.feedback-form.category.issues-with-apps" = "VPN causes issues with other apps or websites"; +"vpn.feedback-form.category.issues-with-apps" = "VPN предизвиква проблеми с други приложения или уебсайтове"; /* Title for the local device connectivity category of the VPN feedback form */ -"vpn.feedback-form.category.local-device-connectivity" = "VPN won't let me connect to local device"; +"vpn.feedback-form.category.local-device-connectivity" = "VPN не ми позволява да се свържа с локално устройство"; /* Title for the 'other VPN feedback' category of the VPN feedback form */ -"vpn.feedback-form.category.other" = "Other VPN feedback"; +"vpn.feedback-form.category.other" = "Други отзиви за VPN"; /* Title for the category selection state of the VPN feedback form */ -"vpn.feedback-form.category.select-category" = "Select a category"; +"vpn.feedback-form.category.select-category" = "ИЗБЕРЕТЕ КАТЕГОРИЯ"; /* Title for the 'VPN is too slow' category of the VPN feedback form */ -"vpn.feedback-form.category.too-slow" = "VPN connection is too slow"; +"vpn.feedback-form.category.too-slow" = "VPN връзката е прекалено бавна"; /* Title for the 'unable to install' category of the VPN feedback form */ -"vpn.feedback-form.category.unable-to-install" = "Unable to install VPN"; +"vpn.feedback-form.category.unable-to-install" = "Неуспешна инсталация на VPN"; /* Title for the feedback sent view description of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.description" = "Your feedback will help us improve the\nDuckDuckGo VPN."; +"vpn.feedback-form.sending-confirmation.description" = "Вашите отзиви ще ни помогнат да подобрим\n DuckDuckGo VPN."; /* Title for the feedback sending error text of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.error" = "We couldn't send your feedback right now, please try again."; +"vpn.feedback-form.sending-confirmation.error" = "Не успяхме да изпратим Вашия отзив, опитайте отново."; /* Title for the feedback sent view title of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.title" = "Thank you!"; +"vpn.feedback-form.sending-confirmation.title" = "Благодарим ви!"; /* Toast message when the VPN feedback form is submitted successfully */ -"vpn.feedback-form.submitted.message" = "Thank You! Feedback submitted."; +"vpn.feedback-form.submitted.message" = "Благодарим ви! Отзивът ви е изпратен."; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-1" = "Please describe what's happening, what you expected to happen, and the steps that led to the issue:"; +"vpn.feedback-form.text-1" = "Моля, опишете какво се случва, какво сте очаквали да се случи и какви стъпки са довели до проблема:"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-2" = "In addition to the details entered into this form, your app issue report will contain:"; +"vpn.feedback-form.text-2" = "Освен данните, въведени в този формуляр, докладът за проблема в приложението ще съдържа:"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-3" = "• Whether specific DuckDuckGo features are enabled"; +"vpn.feedback-form.text-3" = "• Информация дали са активирани конкретни функции на DuckDuckGo"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-4" = "• Aggregate DuckDuckGo app diagnostics"; +"vpn.feedback-form.text-4" = "• Обобщени диагностични данни за приложението DuckDuckGo"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-5" = "By tapping \"Submit\" I agree that DuckDuckGo may use the information in this report for purposes of improving the app's features."; +"vpn.feedback-form.text-5" = "С натискането на бутона „Изпращане“ приемам, че DuckDuckGo може да използва информацията в този доклад с цел подобряване на функциите на приложението."; /* Title for each screen of the VPN feedback form */ -"vpn.feedback-form.title" = "Help Improve the DuckDuckGo VPN"; +"vpn.feedback-form.title" = "Помогнете ни да подобрим DuckDuckGo VPN"; /* VPN settings screen cell text for adding the VPN widget to the home screen */ "vpn.settings.add.widget" = "Добавяне на уиджет за VPN към началния екран"; /* Disclaimer for the DNS Server section on the DNS Server screen */ -"vpn.settings.dns.section-disclaimer" = "Using a custom DNS server can impact browsing speeds and expose your activity to third parties if the server isn't secure or reliable."; +"vpn.settings.dns.section-disclaimer" = "Използването на персонализиран DNS сървър може да повлияе на скоростта на сърфиране и дейността да стане видима за трети страни, ако сървърът не е сигурен или надежден."; /* Header text for the DNS section on the VPN Settings screen */ "vpn.settings.dns.section-header" = "DNS"; /* Title for the Apply button on the DNS Server setting screen */ -"vpn.settings.dns.server.apply.button.title" = "Apply"; +"vpn.settings.dns.server.apply.button.title" = "Приложи"; /* Default value for the DNS Server row on the VPN Settings screen */ "vpn.settings.dns.server.default.value" = "DuckDuckGo"; /* Title for the IPv4 Address setting */ -"vpn.settings.dns.server.ipv4.title" = "IPv4 Address"; +"vpn.settings.dns.server.ipv4.title" = "IPv4 адрес"; /* Custom option for the DNS Server setting */ -"vpn.settings.dns.server.option.custom" = "Custom"; +"vpn.settings.dns.server.option.custom" = "Потребителско"; /* Recommended option for the DNS Server setting */ -"vpn.settings.dns.server.option.default" = "DuckDuckGo (Recommended)"; +"vpn.settings.dns.server.option.default" = "DuckDuckGo (Препоръчително)"; /* Title for the DNS Server setting screen */ -"vpn.settings.dns.server.screen.title" = "DNS Server"; +"vpn.settings.dns.server.screen.title" = "DNS сървър"; /* Title for the DNS Server row on the VPN Settings screen */ -"vpn.settings.dns.server.title" = "DNS Server"; +"vpn.settings.dns.server.title" = "DNS сървър"; /* Title for the button to enable push notifications in system settings */ "waitlist.allow-notifications" = "Разрешаване на известия"; diff --git a/DuckDuckGo/bg.lproj/OmniBar.strings b/DuckDuckGo/bg.lproj/OmniBar.strings index 584746faa5..cfe74bdca7 100644 --- a/DuckDuckGo/bg.lproj/OmniBar.strings +++ b/DuckDuckGo/bg.lproj/OmniBar.strings @@ -7,6 +7,9 @@ /* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "Dt8-We-5tV"; */ "Dt8-We-5tV.accessibilityLabel" = "Изчистване на текста"; +/* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "gb5-wL-4OW"; */ +"gb5-wL-4OW.accessibilityLabel" = "Изчистване на текста"; + /* Class = "UIButton"; accessibilityLabel = "Share"; ObjectID = "hor-Jd-3kw"; */ "hor-Jd-3kw.accessibilityLabel" = "Споделяне"; diff --git a/DuckDuckGo/bg.lproj/Settings.strings b/DuckDuckGo/bg.lproj/Settings.strings index 1ccfd36de8..b0a74df548 100644 --- a/DuckDuckGo/bg.lproj/Settings.strings +++ b/DuckDuckGo/bg.lproj/Settings.strings @@ -10,9 +10,6 @@ /* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "5YD-Od-uDU"; */ "5YD-Od-uDU.text" = "Автоматично изчистване на данните"; -/* Class = "UIButton"; normalTitle = "Settings"; ObjectID = "8YG-7Q-Czd"; */ -"8YG-7Q-Czd.normalTitle" = "Настройки"; - /* Class = "UILabel"; text = "App Exit, Inactive for 1 Hour"; ObjectID = "9EC-lx-77p"; */ "9EC-lx-77p.text" = "Изход от приложението, 1 час без активност"; @@ -76,9 +73,6 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Незащитени сайтове"; -/* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ -"ssa-zd-L3T.title" = "Размер на текста"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Показване на клавиатура в"; diff --git a/DuckDuckGo/cs.lproj/DaxOnboarding.strings b/DuckDuckGo/cs.lproj/DaxOnboarding.strings index 243c253cdb..cc705e006b 100644 --- a/DuckDuckGo/cs.lproj/DaxOnboarding.strings +++ b/DuckDuckGo/cs.lproj/DaxOnboarding.strings @@ -1,12 +1,6 @@ /* Class = "UIButton"; normalTitle = "Hide"; ObjectID = "hgy-75-OvJ"; */ "hgy-75-OvJ.normalTitle" = "Skrýt"; -/* Class = "UILabel"; text = "Welcome to\nDuckDuckGo!"; ObjectID = "Lvy-9G-3gr"; */ -"Lvy-9G-3gr.text" = "Vítejte na\nDuckDuckGo!"; - -/* Class = "UIButton"; normalTitle = "Let’s Do It!"; ObjectID = "oAv-Zt-uIt"; */ -"oAv-Zt-uIt.normalTitle" = "Pojďme na to!"; - /* Class = "UIButton"; normalTitle = "Button"; ObjectID = "zJt-F9-L48"; */ "zJt-F9-L48.normalTitle" = "Tlačítko"; diff --git a/DuckDuckGo/cs.lproj/Localizable.strings b/DuckDuckGo/cs.lproj/Localizable.strings index 83b208d86a..7676ec73c4 100644 --- a/DuckDuckGo/cs.lproj/Localizable.strings +++ b/DuckDuckGo/cs.lproj/Localizable.strings @@ -341,7 +341,7 @@ "autofill.keep-enabled.alert.disable" = "Vypnout"; /* A link that takes the user to the DuckDuckGo help pages explaining password managers */ -"autofill.learn.more.link.title" = "Learn More"; +"autofill.learn.more.link.title" = "Více informací"; /* Button displayed after saving/updating an autofill login that takes the user to the saved login */ "autofill.login-save-action-button.toast" = "Zobrazit"; @@ -977,9 +977,6 @@ /* ad = advertisment */ "dax.onboarding.home.subsequent" = "Máte to!\n\nPamatujte: Pokaždé, když internet procházíte s námi, příšerným reklamám přistřihneme křídla. 👍"; -/* No comment provided by engineer. */ -"dax.onboarding.message" = "Internet může být trochu strašidelný.\n\nNebojte se! Anonymní vyhledávání a procházení je jednodušší, než si myslíte."; - /* No comment provided by engineer. */ "Debug" = "Ladění"; @@ -1080,7 +1077,7 @@ "duckPlayer.never.label" = "Nikdy"; /* Body text for the modal feature explanation */ -"duckplayer.presentation.modal.body" = "Duck Player umožňuje dívat se na YouTube v prohlížeči DuckDuckGo v režimu kina a bez cílených reklam. To, co sleduješ, nebude ovlivňovat tvoje doporučení."; +"duckplayer.presentation.modal.body" = "Duck Player umožňuje dívat se na YouTube v prohlížeči DuckDuckGo bez cílených reklam. To, co sleduješ, nebude ovlivňovat tvoje doporučení."; /* Button that will dismiss the modal */ "duckplayer.presentation.modal.dismiss-button" = "Mám to!"; @@ -1088,17 +1085,23 @@ /* Two line title (separated by \n) for the feature explanation */ "duckplayer.presentation.modal.title" = "Topíš se v reklamách na YouTube? Vyzkoušej přehrávač Duck Player!"; +/* Two line title (separated by \n) for the feature explanation */ +"duckplayer.presentation.modal.title.youtube" = "Topíš se v reklamách na YouTube? S přehrávačem Duck Player nebudeš!"; + /* Footer label in the settings screen for Duck Player */ "duckplayer.settings.footer" = "DuckDuckGo má všechno, co ti zaručí ochranu soukromí při procházení webu."; /* Text explaining what Duck Player is in the settings screen. */ -"duckplayer.settings.info-text" = "Přehrávač Duck Player nabízí sledování v minimalistickém prostředí bez personalizovaných reklam a brání tomu, aby sledovaná videa ovlivňovala tvoje doporučení na YouTube."; +"duckplayer.settings.info-text" = "Duck Player umožňuje dívat se na YouTube v prohlížeči DuckDuckGo bez cílených reklam. To, co sleduješ, nebude ovlivňovat tvoje doporučení."; /* Button that takes the user to learn more about Duck Player. */ "duckplayer.settings.learn-more" = "Více informací"; +/* Settings screen cell text for DuckPlayer settings to open in new tab */ +"duckplayer.settings.open-new-tab-label" = "Otevřít Duck Player na nové kartě"; + /* Settings screen cell text for DuckPlayer settings */ -"duckplayer.settings.open-videos-in" = "Otevírat videa v přehrávači Duck Player"; +"duckplayer.settings.open-videos-in" = "Otevírat videa na YouTube v přehrávači Duck Player"; /* Settings screen cell text for DuckPlayer settings */ "duckplayer.settings.title" = "Duck Player"; @@ -1169,9 +1172,6 @@ /* Title for prompt to sign up for email protection */ "email.signup-prompt.title" = "Skryj svůj e-mail\na blokuj trackery"; -/* No comment provided by engineer. */ -"empty!" = "empty!"; - /* Empty list state placholder */ "empty.bookmarks" = "Zatím nebyly přidány žádné záložky."; @@ -1322,6 +1322,39 @@ /* Confirmation button */ "feedback.form.submit" = "Odeslat"; +/* Title of the feedback form */ +"feedback.form.title" = "Pomoz zlepšit službu Privacy Pro"; + +/* Category for Identity Theft Restoration feedback */ +"feedback.general.category.itr" = "Identity Theft Restoration"; + +/* Category for Personal Info Removal feedback */ +"feedback.general.category.pir" = "Odstranění osobních údajů"; + +/* Category for subscription and payments feedback */ +"feedback.general.category.ppro" = "Předplatné a platby"; + +/* Prompt to select a category for general feedback */ +"feedback.general.category.select" = "VYBER KATEGORII"; + +/* Category for VPN feedback */ +"feedback.general.category.vpn" = "VPN"; + +/* Category for access code issues */ +"feedback.itr.category.access-code" = "Problém s přístupovým kódem"; + +/* Category for issues contacting an advisor */ +"feedback.itr.category.cant-contact-advisor" = "Nejde kontaktovat poradce"; + +/* Prompt to select a category for Identity Theft Restoration feedback */ +"feedback.itr.category.select" = "VYBER KATEGORII"; + +/* Category for other Identity Theft Restoration issues */ +"feedback.itr.category.something-else" = "Něco jiného"; + +/* Category for unhelpful advisor calls */ +"feedback.itr.category.unhelpful" = "Hovor s poradcem mi nepomohl"; + /* No comment provided by engineer. */ "feedback.negative.form.genericPlaceholder" = "Buďte co nejkonkrétnější."; @@ -1361,6 +1394,24 @@ /* No comment provided by engineer. */ "feedback.performance.slowLoading" = "Webové stránky nebo výsledky vyhledávání se načítají pomalu."; +/* Category for when scan finds incorrect records */ +"feedback.pir.category.not-me" = "Skenování našlo záznamy, které se mě netýkají"; + +/* Category for when scan doesn't find info on a specific site */ +"feedback.pir.category.nothing-on-site" = "Skenování nenašlo moje informace na konkrétním webu"; + +/* Category for other Personal Info Removal issues */ +"feedback.pir.category.other" = "Něco jiného"; + +/* Category for when the removal process is stuck */ +"feedback.pir.category.removal-stuck" = "Proces odstranění se zasekl"; + +/* Category for when the scan is stuck */ +"feedback.pir.category.scan-stuck" = "Skenování záznamů se zaseklo"; + +/* Prompt to select a category for Personal Info Removal feedback */ +"feedback.pir.category.select" = "VYBER KATEGORII"; + /* Header above input field */ "feedback.positive.form.header" = "Podělte se o podrobnosti."; @@ -1379,6 +1430,15 @@ /* Button encouraging uses to share details aboout their feedback */ "feedback.positive.submit" = "Podělte se o podrobnosti."; +/* Category for other Privacy Pro issues */ +"feedback.ppro.category.other" = "Něco jiného"; + +/* Category for one-time password issues */ +"feedback.ppro.category.otp" = "Problém s jednorázovým heslem"; + +/* Prompt to select a category for Privacy Pro feedback */ +"feedback.ppro.category.select" = "VYBER KATEGORII"; + /* No comment provided by engineer. */ "feedback.start.footer" = "Vaše anonymní zpětná vazba je pro nás důležitá."; @@ -1529,12 +1589,6 @@ /* No comment provided by engineer. */ "keyCommandShowAllTabs" = "Zobrazit všechny karty"; -/* Please preserve newline character */ -"launchscreenWelcomeMessage" = "Vítejte na\nDuckDuckGo!"; - -/* No comment provided by engineer. */ -"LOREM IPSUM" = "LOREM IPSUM"; - /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "DuckDuckGo pro Mac má rychlost, kterou potřebuješ, funkce prohlížeče, které očekáváš, a obsahuje naše nejlepší nástroje na ochranu soukromí."; @@ -1586,62 +1640,83 @@ /* Edit button */ "navigation.title.edit" = "Upravit"; -/* String indicating NetP is connected when viewed from the settings screen */ -"netP.cell.connected" = "Connected"; - -/* String indicating NetP is disconnected when viewed from the settings screen */ -"netP.cell.disconnected" = "Not connected"; - -/* Title for the DuckDuckGo VPN feature in settings */ -"netP.settings.title" = "VPN"; +/* String indicating NetP is snoozing when viewed from the settings screen */ +"netP.cell.snoozing" = "Uspáno"; /* Title for the DuckDuckGo VPN feature */ "netP.title" = "VPN DuckDuckGo"; +/* Button title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.action" = "Přidat widget"; + +/* Message for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.message" = "VPN budeš moct zapnout nebo vypnout přímo na domovské obrazovce."; + +/* Title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.title" = "Přidej si VPN widget"; + /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN byla odpojena kvůli vypršení platnosti předplatného. Předplať si službu Privacy Pro, ať můžeš DuckDuckGo VPN dál používat."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Ochrana sítě se nepřipojila. Zkus to znovu později."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "VPN DuckDuckGo se nepodařilo připojit. Zkus to znovu později."; /* Title for the VPN FAQ screen. */ "network.protection.faq.title" = "Nejčastější dotazy ohledně VPN DuckDuckGo"; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Ochrana sítě je přerušená. Probíhá pokus o opětovné připojení..."; +/* Message for tooltip about geoswitching */ +"network.protection.geoswitching.tip.message" = "Polohu VPN si můžeš přizpůsobit připojením ke kterémukoli z našich serverů po celém světě."; + +/* Title for tooltip about geoswitching */ +"network.protection.geoswitching.tip.title" = "Změň si polohu"; + +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "VPN DuckDuckGo byla odpojená. Probíhá pokus o opětovné připojení..."; /* Message for the network protection invite dialog */ -"network.protection.invite.dialog.message" = "Enter your invite code to get started."; +"network.protection.invite.dialog.message" = "Zadej svůj kód pozvánky, ať můžeš začít."; /* Title for the network protection invite screen */ "network.protection.invite.dialog.title" = "Zveme vás k vyzkoušení VPN DuckDuckGo"; /* Prompt for the network protection invite code text field */ -"network.protection.invite.field.prompt" = "Invite Code"; +"network.protection.invite.field.prompt" = "Zvací kód"; /* Message for the network protection invite success view */ -"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; +"network.protection.invite.success.message" = "Skryj svou polohu před webovými stránkami a schovej svou online aktivitu před poskytovateli internetu a ostatními uživateli tvé sítě."; /* Title for the network protection invite success view */ -"network.protection.invite.success.title" = "Success! You’re in."; +"network.protection.invite.success.title" = "A je to!"; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; /* Title text for an iOS quick action that opens VPN settings */ "network.protection.quick-action.open-vpn" = "Otevřít VPN"; +/* Button title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.action" = "Více informací"; + +/* Message for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.message" = "Pokud web nebo aplikace blokuje provoz přes VPN, stačí VPN připojení dočasně pozastavit."; + +/* Title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.title" = "Předcházej konfliktům VPN"; + +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN je uspaná na %@"; + /* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ "network.protection.status.connected.format" = "Připojeno · %@"; /* The label for the NetP VPN when connecting */ -"network.protection.status.connecting" = "Connecting..."; +"network.protection.status.connecting" = "Připojování..."; /* The label for the NetP VPN when disconnected */ -"network.protection.status.disconnected" = "Not connected"; +"network.protection.status.disconnected" = "Nepřipojeno"; /* The label for the NetP VPN when disconnecting */ -"network.protection.status.disconnecting" = "Disconnecting..."; +"network.protection.status.disconnecting" = "Odpojování..."; /* Message label text for the status view when VPN is disconnected */ "network.protection.status.header.message.off" = "Připojte se a zabezpečte všechna svá zařízení\nInternetový provoz."; @@ -1655,26 +1730,41 @@ /* Header title label text for the status view when VPN is connected */ "network.protection.status.header.title.on" = "VPN DuckDuckGo je zapnutá"; +/* Header title label text for the status view when VPN is snoozing */ +"network.protection.status.header.title.snoozed" = "VPN DuckDuckGo je uspaná"; + /* The status view 'Share Feedback' button which is shown inline on the status view after the temporary free use footer text */ -"network.protection.status.menu.share.feedback" = "Share Feedback"; +"network.protection.status.menu.share.feedback" = "Podělte se o zpětnou vazbu"; + +/* The label for the NetP VPN when paused */ +"network.protection.status.paused" = "Pozastaveno"; + +/* The label for when NetP VPN is snoozing plus the length of time remaining formatted as '0:00' */ +"network.protection.status.snoozing.format" = "Uspáno, zbývá %@"; + +/* Snooze button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.snooze" = "Uspat na 20 minut"; + +/* Wake Up button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.wake-up" = "Probudit"; /* Connection details label shown in NetworkProtection's status view. */ -"network.protection.status.view.connection.details" = "Connection Details"; +"network.protection.status.view.connection.details" = "Podrobnosti o připojení"; /* Custom DNS label shown in NetworkProtection's status view. */ -"network.protection.status.view.custom.dns" = "DNS Server"; +"network.protection.status.view.custom.dns" = "DNS server"; /* Generic connection failed error message shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.message" = "Please try again later."; +"network.protection.status.view.error.connection.failed.message" = "Zkus to znovu později."; /* Generic connection failed error title shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; +"network.protection.status.view.error.connection.failed.title" = "Připojení se nezdařilo."; /* IP Address label shown in NetworkProtection's status view. */ -"network.protection.status.view.ip.address" = "IP Address"; +"network.protection.status.view.ip.address" = "IP adresa"; /* Location label shown in NetworkProtection's status view. */ -"network.protection.status.view.location" = "Location"; +"network.protection.status.view.location" = "Poloha"; /* Label shown on the title of the settings section in NetworkProtection's status view. */ "network.protection.status.view.settings.section.title" = "Spravovat"; @@ -1682,12 +1772,15 @@ /* Title label text for the status view when netP is disconnected */ "network.protection.status.view.title" = "VPN"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Ochrana sítě je zapnutá. Tvoje poloha a online aktivita jsou chráněné."; +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "VPN DuckDuckGo je zapnutá. Tvoje poloha a online aktivita jsou chráněné."; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Směrování provozu zařízení přes %@."; +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "VPN už není uspaná. Směrování provozu zařízení přes %@."; + /* Title for the button to link to the iOS app settings and enable notifications app-wide. */ "network.protection.turn.on.notifications.button.title" = "Zapnout oznámení"; @@ -1698,13 +1791,13 @@ "network.protection.vpn.about" = "O společnosti"; /* Section header for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.section.header" = "Notifications"; +"network.protection.vpn.alerts.section.header" = "Oznámení"; /* List section footer for the toggle for VPN alerts. */ "network.protection.vpn.alerts.toggle.section.footer" = "Nech si poslat upozornění, když se přeruší připojení nebo se změní stav VPN."; /* Title for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.toggle.title" = "VPN Notifications"; +"network.protection.vpn.alerts.toggle.title" = "Oznámení sítě VPN"; /* Title for the data volume section in the VPN status screen */ "network.protection.vpn.data-volume" = "Objem dat"; @@ -1713,13 +1806,13 @@ "network.protection.vpn.exclude.local.networks.setting.footer" = "Místní provoz může obejít síť VPN a připojit se k zařízením v místní síti, třeba k tiskárně."; /* Header text for the Exclude Local Networks setting item. */ -"network.protection.vpn.exclude.local.networks.setting.header" = "General"; +"network.protection.vpn.exclude.local.networks.setting.header" = "Hlavní"; /* Title for the Exclude Local Networks setting item. */ "network.protection.vpn.exclude.local.networks.setting.title" = "Vyloučení místních sítí"; /* Title for the VPN Location screen's All Countries section. */ -"network.protection.vpn.location.all.countries.section.title" = "All Countries"; +"network.protection.vpn.location.all.countries.section.title" = "Všechny země"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.connected" = "Připojené umístění"; @@ -1734,7 +1827,7 @@ "network.protection.vpn.location.recommended.section.footer" = "Automatické připojení k nejbližšímu nalezenému serveru."; /* Title for the VPN Location screen's Recommended section. */ -"network.protection.vpn.location.recommended.section.title" = "Recommended"; +"network.protection.vpn.location.recommended.section.title" = "Doporučeno"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.selected" = "Vybrané umístění"; @@ -1752,13 +1845,13 @@ "network.protection.vpn.preferred.location.nearest" = "Nejbližší umístění"; /* Title for the Preferred Location VPN Settings item. */ -"network.protection.vpn.preferred.location.title" = "Preferred Location"; +"network.protection.vpn.preferred.location.title" = "Preferovaná lokalita"; /* Footer text for the DNS server setting item. */ -"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo routes DNS queries through our DNS servers so your internet provider can't see what websites you visit."; +"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo směruje dotazy DNS přes naše DNS servery, takže tvůj poskytovatel internetu nevidí, jaké webové stránky navštěvuješ."; /* Title for the FAQ row in the VPN status screen. */ -"network.protection.vpn.settings.faq" = "FAQs and Support"; +"network.protection.vpn.settings.faq" = "Časté dotazy a podpora"; /* Title for the feedback row in the VPN status screen. */ "network.protection.vpn.settings.share-feedback" = "Podělte se o zpětnou vazbu k VPN"; @@ -1940,6 +2033,66 @@ /* Deactivate button */ "pm.deactivate" = "Deaktivovat"; +/* Label for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.label" = "E-mail (nepovinné)"; + +/* Placeholder for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.placeholder" = "jmeno@email.com"; + +/* Placeholder for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.placeholder" = "Pošli nám svou zpětnou vazbu..."; + +/* Title for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.title" = "Obecná zpětná vazba"; + +/* Title for the ITR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-itr-problem.title" = "Identity Theft Restoration"; + +/* Title for the PIR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-pir-problem.title" = "Personal Information Removal"; + +/* Title for the Subscriptions and Payments category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-ppro-problem.title" = "Předplatné a platby"; + +/* Placeholder for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.placeholder" = "Řekni nám, o co jde..."; + +/* Title for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.title" = "Nahlásit problém"; + +/* Title for the VPN category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-vpn-problem.title" = "VPN"; + +/* Placeholder for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.placeholder" = "Jaká funkce by se ti líbila?"; + +/* Title for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.title" = "Požadavek na funkci"; + +/* Title for the category selection section in the Privacy Pro feedback form */ +"ppro.feedback-form.select-category.title" = "VYBER KATEGORII"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-1" = "Víš o problému, který není popsaný v našem [centru nápovědy](duck://)? Řekni nám o něm!\n\nZadej e-mail, pokud chceš, abychom tě ohledně tohoto problému kontaktovali (možná ale nezvládneme reagovat na všechny problémy):"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-2" = "Kromě údajů zadaných nahoře posíláme s tvojí zpětnou vazbou i některé anonymizované informace:"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-3" = "• Jestli jsou aktivní některé funkce prohlížeče"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-4" = "• Agregovaná diagnostika aplikace (např. chybové kódy)"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-5" = "Klepnutím na Odeslat vyjadřuješ souhlas s tím, že DuckDuckGo může použít poskytnuté informace ke zlepšení aplikace."; + +/* Title for the text box in the Privacy Pro feedback form */ +"ppro.feedback-form.text-box.title" = "Zpětná vazba"; + +/* Title for the Privacy Pro feedback form */ +"ppro.feedback-form.title" = "Odeslat zpětnou vazbu"; + /* Button title for sync bookmarks limits exceeded warning to go to manage bookmarks */ "prefrences.sync.bookmarks-limit-exceeded-action" = "Spravovat záložky"; @@ -2029,6 +2182,18 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Oblíbené"; +/* Name of the option the user can chose to give general browser feedback */ +"send.browser.feedback.general-feedback" = "Obecná zpětná vazba"; + +/* Name of the option the user can chose to give browser feedback about a problem they enountered */ +"send.browser.feedback.report-problem" = "Nahlásit problém"; + +/* Name of the option the user can chose to give browser feedback about a feature they would like */ +"send.browser.feedback.request-feature" = "Požádat o novou funkci"; + +/* Title of the picker where the user can chose the category of the feedback they want ot send. */ +"send.browser.feedback.select-category" = "VYBER KATEGORII"; + /* Settings cell for About DDG */ "settings.about.ddg" = "O DuckDuckGo"; @@ -2084,6 +2249,9 @@ /* Section footer Autolock description */ "settings.autolock.description" = "Pokud je zapnuté Touch ID nebo Face ID, případně nastavený přístupový kód k systému, zobrazí se při otevírání aplikace výzva k jejímu odemknutí."; +/* Settings cell for Browser Feedback */ +"settings.browser.feedback" = "Zpětná vazba k prohlížeči"; + /* Settings screen cell text for Automatically Clearing Data */ "settings.clear.data" = "Automaticky vymazat data"; @@ -2182,7 +2350,7 @@ "settings.ppro" = "Privacy Pro"; /* Title for Link in the Footer of Privacy Pro section */ -"settings.ppro.footer" = "Privacy Policy and Terms of Service"; +"settings.ppro.footer" = "Zásady ochrany osobních údajů a podmínky služby"; /* Settings screen cell for long press previews */ "settings.previews" = "Náhledy dlouhého stisknutí"; @@ -2208,23 +2376,20 @@ /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Nastavte pozici adresního řádku"; -/* Subscription activation pending description */ -"settings.subscription.activation.pending.description" = "This is taking longer than usual, please check back later."; +/* Privacy pro description subtitle in settings when the is activating */ +"settings.subscription.activating" = "Aktivuje se"; -/* Subscription activation pending title */ -"settings.subscription.activation.pending.title" = "Tvoje předplatné se aktivuje"; +/* Subscription activation pending description */ +"settings.subscription.activation.pending.description" = "Akce trvá déle než obvykle. Stav se tady prosím později."; /* Data Broker protection cell subtitle for privacy pro */ -"settings.subscription.DBP.subtitle" = "Remove your info from sites that sell it"; +"settings.subscription.DBP.subtitle" = "Odstraň své údaje z webů, které je prodávají"; /* Data Broker protection cell title for privacy pro */ "settings.subscription.DBP.title" = "Personal Information Removal"; -/* Privacy pro description subtext */ -"settings.subscription.description" = "Tři nové ochranné funkce pro ještě větší soukromí:"; - /* I have a Subscription button text for privacy pro */ -"settings.subscription.existing.subscription" = "I Have a Subscription"; +"settings.subscription.existing.subscription" = "Mám předplatné"; /* Subscription expired description */ "settings.subscription.expired.comment" = "Pokud chceš dál používat službu Privacy Pro, znovu si založ předplatné"; @@ -2232,23 +2397,26 @@ /* Subscription expired tittle message */ "settings.subscription.expired.title" = "Tvoje předplatné Privacy Pro vypršelo"; -/* Privacy pro features list */ -"settings.subscription.features" = " • VPN\n • Personal Information Removal\n • Identity Theft Restoration"; - /* Identity theft restoration cell subtitle for privacy pro */ -"settings.subscription.ITR.subtitle" = "If your identity is stolen, we'll help restore it"; +"settings.subscription.ITR.subtitle" = "Pokud dojde ke krádeži tvé identity, pomůžeme ti ji obnovit."; /* Identity theft restoration cell title for privacy pro */ "settings.subscription.ITR.title" = "Identity Theft Restoration"; /* Get Privacy Pro button text for privacy pro */ -"settings.subscription.learn.more" = "Get Privacy Pro"; +"settings.subscription.learn.more" = "Pořiď si službu Privacy Pro"; /* Subscription Settings button text for privacy pro */ -"settings.subscription.manage" = "Subscription Settings"; +"settings.subscription.manage" = "Nastavení předplatného"; + +/* Privacy Pro description subtitle in settings */ +"settings.subscription.row.description" = "Zahrnuje naši VPN a službu Identity Theft Restoration."; -/* Call to action title for Privacy Pro */ -"settings.subscription.subscribe" = "Subscribe to Privacy Pro"; +/* Call to action title for Privacy Pro settings */ +"settings.subscription.subscribe" = "Chraň své připojení a identitu se službou Privacy Pro"; + +/* Privacy pro description subtitle in settings */ +"settings.subscription.us.description" = "Zahrnuje naše funkce VPN, Personal Information Removal a Identity Theft Restoration."; /* VPN cell title for privacy pro */ "settings.subscription.VPN.title" = "VPN"; @@ -2317,67 +2485,64 @@ "siteFeedback.urlPlaceholder" = "Které webové stránky jsou poškozené?"; /* Subscription Activation Info */ -"subscription.activate..header.description" = "Access your Privacy Pro subscription on this device via Apple ID or an email address."; +"subscription.activate..header.description" = "Získej na tomto zařízení přístup ke svému předplatnému Privacy Pro přes Apple ID nebo e-mailovou adresu."; /* Button for adding email address to subscription */ -"subscription.activate.add.email.button" = "Add Email"; +"subscription.activate.add.email.button" = "Přidat e-mail"; /* Apple ID option for activation */ "subscription.activate.appleid" = "Apple ID"; /* Button text for restoring purchase via Apple ID */ -"subscription.activate.appleid.button" = "Restore Purchase"; +"subscription.activate.appleid.button" = "Obnovit nákup"; /* Description for Apple ID activation */ -"subscription.activate.appleid.description" = "Restore your purchase to activate your subscription on this device."; +"subscription.activate.appleid.description" = "Pokud chceš na tomhle zařízení aktivovat svoje předplatné, musíš si obnovit nákup."; /* Subscription Activation Info */ -"subscription.activate.description" = "Your subscription is automatically available in DuckDuckGo on any device signed in to your Apple ID."; +"subscription.activate.description" = "Tvoje předplatné se na DuckDuckGo automaticky aktivuje na jakémkoli zařízení přihlášeném ke tvému účtu Apple ID."; /* Button for editing email address added to subscription */ -"subscription.activate.edit.email.button" = "Edit Email"; +"subscription.activate.edit.email.button" = "Upravit e-mail"; /* View Title for editing your email account */ -"subscription.activate.edit.email.title" = "Edit Email"; +"subscription.activate.edit.email.title" = "Upravit e-mail"; /* Email option for activation */ -"subscription.activate.email" = "Email"; +"subscription.activate.email" = "Emailová adresa"; /* Restore button title for Email */ -"subscription.activate.email.button" = "Enter Email"; +"subscription.activate.email.button" = "Zadej e-mail"; /* Description for Email activation */ -"subscription.activate.email.description" = "Use your email to activate your subscription on this device."; +"subscription.activate.email.description" = "Pokud chceš na tomhle zařízení aktivovat svoje předplatné, musíš nejdřív do e-mailu."; /* Activate subscription title */ "subscription.activate.email.title" = "Aktivace předplatného"; /* Button title for cancelling email deletion */ -"subscription.activate.manage.email.cancel" = "Cancel"; +"subscription.activate.manage.email.cancel" = "Zrušit"; /* Button title for confirming email deletion */ -"subscription.activate.manage.email.OK" = "OK"; +"subscription.activate.manage.email.OK" = "DOBŘE"; /* Restore button title for AppleID */ -"subscription.activate.restore.apple" = "Restore Purchase"; +"subscription.activate.restore.apple" = "Obnovit nákup"; /* Subscription Activation Title */ -"subscription.activate.title" = "Activate your subscription on this device"; +"subscription.activate.title" = "Aktivuj si předplatné na tomhle zařízení"; /* Resend activation instructions button */ -"subscription.add.device.resend.instructions" = "Resend Instructions"; +"subscription.add.device.resend.instructions" = "Znovu poslat instrukce"; /* Add email to an existing subscription */ -"subscription.add.email" = "Add an email address to activate your subscription on your other devices. We’ll only use this address to verify your subscription."; +"subscription.add.email" = "Pokud chceš své předplatné aktivovat na dalších zařízeních, přidej e-mailovou adresu. Použijeme ji jen k ověření tvého předplatného."; /* View title for adding email to subscription */ -"subscription.add.email.title" = "Add Email"; - -/* Title for Alert messages */ -"subscription.alert.title" = "subscription.alert.title"; +"subscription.add.email.title" = "Přidat e-mail"; /* Subscription availability message on Apple devices */ -"subscription.available.apple" = "Privacy Pro is available on any device signed in to the same Apple ID."; +"subscription.available.apple" = "Služba Privacy Pro je dostupná na jakémkoli zařízení přihlášeném ke stejnému Apple ID."; /* Text for the manage billing page */ "subscription.billing.google.text" = "Předplatné bylo zakoupeno prostřednictvím obchodu Google Play. Předplatné se dá obnovit tak, že otevřeš nastavení předplatného obchodu Google Play na zařízení přihlášeném ke stejnému účtu Google, který byl původně použit k nákupu předplatného."; @@ -2385,89 +2550,86 @@ /* Title for the manage billing page */ "subscription.billing.google.title" = "Plány předplatného"; -/* Subscription annual billing period type */ -"subscription.billing.period.annual" = "annual"; - -/* Subscription monthly billing period type */ -"subscription.billing.period.monthly" = "monthly"; - /* Subscription Removal confirmation message */ -"subscription.cancel.message" = "Your subscription has been removed from this device."; +"subscription.cancel.message" = "Tvoje předplatné bylo z tohohle zařízení odstraněno."; /* Change plan or cancel title */ -"subscription.change.plan" = "Update Plan or Cancel"; +"subscription.change.plan" = "Aktualizace nebo zrušení tarifu"; /* Navigation Button for closing subscription view */ -"subscription.close" = "Close"; +"subscription.close" = "Zavřít"; /* Title for Confirm messages */ -"subscription.confirm.title" = "Are you sure?"; +"subscription.confirm.title" = "Opravdu to chceš udělat?"; /* Header for section for activating subscription on other devices */ -"subscription.devices.header" = "Activate on Other Devices"; +"subscription.devices.header" = "Aktivovat na jiných zařízeních"; /* Footer for section for activating subscription on other devices when email was not yet added */ -"subscription.devices.no.email.footer" = "Add an optional email to your subscription or use your Apple ID to access Privacy Pro on other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.no.email.footer" = "Přidej si k předplatnému nepovinný e-mail, ať můžeš službu Privacy Pro využívat i na jiných zařízeních. **[Další informace](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Footer for section for activating subscription on other devices when email is added */ -"subscription.devices.with.email.footer" = "Use this email to activate your subscription in Settings > Privacy Pro in the DuckDuckGo app on your other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.with.email.footer" = "Pomocí tohohle e-mailu si v aplikaci DuckDuckGo aktivuješ předplatné i na jiných zařízeních. Stačí přejít do Nastavení > Privacy Pro. **[Další informace](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Alert content for not found subscription */ -"subscription.email.inactive.alert.message" = "The subscription associated with this email is no longer active."; +"subscription.email.inactive.alert.message" = "Předplatné spojené s tímhle e-mailem už není aktivní."; /* Alert title for not found subscription */ -"subscription.email.inactive.alert.title" = "Subscription Not Found"; +"subscription.email.inactive.alert.title" = "Předplatné nenalezeno"; /* Alert content for not found subscription */ -"subscription.expired.alert.message" = "The subscription associated with this Apple ID is no longer active."; +"subscription.expired.alert.message" = "Předplatné spojené s tímhle Apple ID už není aktivní."; /* FAQ Button */ -"subscription.faq" = "FAQs and Support"; +"subscription.faq" = "Časté dotazy a podpora"; /* FAQ Description */ -"subscription.faq.description" = "Get answers to frequently asked questions or contact Privacy Pro support from our help pages."; +"subscription.faq.description" = "Získej odpovědi na časté otázky nebo přes naše stránky nápovědy kontaktuj podporu služby Privacy Pro."; + +/* Send Feedback Button */ +"subscription.feedback" = "Odeslat zpětnou vazbu"; /* Cancel action for the existing subscription dialog */ -"subscription.found.cancel" = "Cancel"; +"subscription.found.cancel" = "Zrušit"; /* Restore action for the existing subscription dialog */ -"subscription.found.restore" = "Restore"; +"subscription.found.restore" = "Obnovit"; /* Message for the existing subscription dialog */ -"subscription.found.text" = "We found a subscription associated with this Apple ID."; +"subscription.found.text" = "Našli jsme předplatné spojené s tímhle Apple ID."; /* Title for the existing subscription dialog */ -"subscription.found.title" = "Subscription Found"; +"subscription.found.title" = "Našli jsme předplatné"; /* Help and support Section header */ -"subscription.help" = "Help and support"; +"subscription.help" = "Nápověda a podpora"; /* Manage Plan header */ -"subscription.manage.plan" = "Manage Plan"; +"subscription.manage.plan" = "Spravovat tarif"; /* Header for the subscription section */ -"subscription.manage.title" = "Subscription"; +"subscription.manage.title" = "Předplatné"; /* Alert content for not found subscription */ -"subscription.notFound.alert.message" = "There is no subscription associated with this Apple ID."; +"subscription.notFound.alert.message" = "S tímhle Apple ID není spojené žádné předplatné."; /* Alert title for not found subscription */ -"subscription.notFound.alert.title" = "Subscription Not Found"; +"subscription.notFound.alert.title" = "Předplatné nenalezeno"; /* View plans button text */ -"subscription.notFound.view.plans" = "View Plans"; +"subscription.notFound.view.plans" = "Zobrazit tarify"; /* Hero Text for Personal information removal */ -"subscription.pir.hero" = "Activate Privacy Pro on desktop to set up Personal Information Removal"; +"subscription.pir.hero" = "Aktivuj si na počítači službu Privacy Pro a nastav si v ní funkci Personal Information Removal"; /* Description on how to use Personal information removal in desktop. The first placeholder references a location in the Desktop application. Privacy Pro>, and the second, the menu entry. i.e. */ -"subscription.pir.heroText" = "In the DuckDuckGo browser for desktop, go to %1$@ and click %2$@ to get started."; +"subscription.pir.heroText" = "V prohlížeči DuckDuckGo pro počítače přejdi na %1$@ a klikni na %2$@."; /* Settings references a menu in the Desktop app, Privacy Pro, references our product name */ -"subscription.pir.heroTextLocation" = "Settings > Privacy Pro"; +"subscription.pir.heroTextLocation" = "Nastavení > Privacy Pro"; /* Menu item for enabling Personal Information Removal on Desktop */ -"subscription.pir.heroTextMenyEntry" = "I have a subscription"; +"subscription.pir.heroTextMenyEntry" = "Mám předplatné"; /* Text for the 'macOS' button */ "subscription.pir.macos" = "Mac"; @@ -2476,28 +2638,28 @@ "subscription.pir.windows" = "Windows"; /* Progress view title when completing the purchase */ -"subscription.progress.view.completing.purchase" = "Completing purchase..."; +"subscription.progress.view.completing.purchase" = "Dokončuji nákup..."; /* Progress view title when starting the purchase */ -"subscription.progress.view.purchasing.subscription" = "Purchase in progress..."; +"subscription.progress.view.purchasing.subscription" = "Probíhá nákup..."; /* Progress view title when restoring past subscription purchase */ -"subscription.progress.view.restoring.subscription" = "Restoring subscription..."; +"subscription.progress.view.restoring.subscription" = "Obnovování předplatného..."; /* Remove from this device button */ -"subscription.remove.from.device.button" = "Remove From This Device"; +"subscription.remove.from.device.button" = "Odebrat z tohohle zařízení"; /* Remove from device confirmation dialog text */ -"subscription.remove.from.device.text" = "You will no longer be able to access your Privacy Pro subscription on this device. This will not cancel your subscription, and it will remain active on your other devices."; +"subscription.remove.from.device.text" = "Na tomhle zařízení už nebudeš mít přístup k předplatnému Privacy Pro. Tím se tvoje předplatné nezruší, na tvých ostatních zařízeních zůstane aktivní."; /* Remove from device confirmation dialog title */ -"subscription.remove.from.device.title" = "Remove from this device?"; +"subscription.remove.from.device.title" = "Odebrat z tohohle zařízení?"; /* Remove subscription button text */ -"subscription.remove.subscription" = "Remove Subscription"; +"subscription.remove.subscription" = "Odebrat předplatné"; /* Remove subscription cancel button text */ -"subscription.remove.subscription.cancel" = "Cancel"; +"subscription.remove.subscription.cancel" = "Zrušit"; /* Button text for general error message */ "subscription.restore.backend.error.button" = "Zpět do nastavení"; @@ -2509,28 +2671,43 @@ "subscription.restore.backend.error.title" = "Něco se pokazilo"; /* Alert for general error message */ -"subscription.restore.general.error.message" = "The App Store was unable to process your purchase. Please try again later."; +"subscription.restore.general.error.message" = "App Store nemůže zpracovat tvůj nákup. Zkus to znovu později."; /* Alert for general error title */ -"subscription.restore.general.error.title" = "Something Went Wrong"; +"subscription.restore.general.error.title" = "Něco se pokazilo"; /* Alert button text for restored purchase alert */ -"subscription.restore.success.alert.button" = "OK"; +"subscription.restore.success.alert.button" = "DOBŘE"; /* Alert message for restored purchase */ -"subscription.restore.success.alert.message" = "Your purchases have been restored."; +"subscription.restore.success.alert.message" = "Tvoje nákupy byly obnovené."; /* Alert title for restored purchase */ -"subscription.restore.success.alert.title" = "You’re all set."; +"subscription.restore.success.alert.title" = "Všechno je hotové."; + +/* Subtitle in header when subscribed */ +"subscription.subscribed" = "Přihlášeno"; /* Subscription Expired Data. This reads as 'Your subscription expired on (date)' */ "subscription.subscription.expired.caption" = "Tvoje předplatné vypršelo %@"; -/* Subscription expiration info. This reads as 'Your (monthly or annual) subscription expires on (date)' */ -"subscription.subscription.expiring.caption" = "Your %1$@ subscription expires on %2$@."; +/* Monthly subscription expiration info where parameter is expiration date. This reads as 'Your monthly subscription expires on (date)' */ +"subscription.subscription.expiring.monthly.caption" = "Tvoje měsíční předplatné vyprší %@."; + +/* Unknown period subscription expiration info where parameter is expiration date. This reads as 'Your subscription expires on (date)' */ +"subscription.subscription.expiring.unknown.caption" = "Tvoje předplatné vyprší %@."; + +/* Annual subscription expiration info where parameter is expiration date. This reads as 'Your annual subscription expires on (date)' */ +"subscription.subscription.expiring.yearly.caption" = "Tvoje roční předplatné vyprší %@."; + +/* Monthly subscription renewal info where parameter is renewal date. This reads as 'Your monthly subscription renews on (date)' */ +"subscription.subscription.renewing.monthly.caption" = "Tvoje měsíční předplatné se obnoví %@."; + +/* Unknown period subscription renewal info where parameter is renewal date. This reads as 'Your subscription renews on (date)' */ +"subscription.subscription.renewing.unknown.caption" = "Tvoje předplatné se obnoví %@."; -/* Subscription renewal info. This reads as 'Your (monthly or annual) subscription renews on (date)' */ -"subscription.subscription.renewing.caption" = "Your %1$@ subscription renews on %2$@."; +/* Annual subscription renewal info where parameter is renewal date. This reads as 'Your annual subscription renews on (date)' */ +"subscription.subscription.renewing.yearly.caption" = "Tvoje roční předplatné se obnoví %@."; /* Navigation bar Title for subscriptions */ "subscription.title" = "Privacy Pro"; @@ -2677,118 +2854,118 @@ "voiceSearch.footer.note.old" = "Zvuk se zpracovává na zařízení. Nikde ho neukládáme a s nikým ho nesdílíme. Ani DuckDuckGo se k němu nedostane."; /* Cancel action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.cancel" = "Dismiss"; +"vpn.access-revoked.alert.action.cancel" = "Odmítnout"; /* Primary action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.subscribe" = "Subscribe"; +"vpn.access-revoked.alert.action.subscribe" = "Odebírat novinky"; /* Alert message for the alert when the Privacy Pro subscription expiress */ -"vpn.access-revoked.alert.message" = "Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"vpn.access-revoked.alert.message" = "Předplať si službu Privacy Pro, ať můžeš DuckDuckGo VPN dál používat."; /* Alert title for the alert when the Privacy Pro subscription expires */ -"vpn.access-revoked.alert.title" = "VPN disconnected due to expired subscription"; +"vpn.access-revoked.alert.title" = "VPN byla odpojena kvůli vypršení platnosti předplatného"; /* Title for the VPN widget onboarding screen */ "vpn.addWidget.settings.title" = "Najdi aplikaci DuckDuckGo a vyber ji. Potom se posuň na VPN a vyber Přidat widget."; /* Title for the Cancel button of the VPN feedback form */ -"vpn.feedback-form.button.cancel" = "Cancel"; +"vpn.feedback-form.button.cancel" = "Zrušit"; /* Title for the Done button of the VPN feedback form */ -"vpn.feedback-form.button.done" = "Done"; +"vpn.feedback-form.button.done" = "Hotovo"; /* Title for the Submit button of the VPN feedback form */ -"vpn.feedback-form.button.submit" = "Submit"; +"vpn.feedback-form.button.submit" = "Odeslat"; /* Title for the Submitting state of the VPN feedback form */ -"vpn.feedback-form.button.submitting" = "Submitting…"; +"vpn.feedback-form.button.submitting" = "Odesílání…"; /* Title for the browser crash/freeze category of the VPN feedback form */ -"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN causes browser to crash or freeze"; +"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN způsobuje selhání nebo zamrznutí prohlížeče"; /* Title for the 'VPN fails to connect' category of the VPN feedback form */ -"vpn.feedback-form.category.fails-to-connect" = "VPN fails to connect"; +"vpn.feedback-form.category.fails-to-connect" = "VPN se nedaří připojit"; /* Title for the 'VPN feature request' category of the VPN feedback form */ -"vpn.feedback-form.category.feature-request" = "VPN feature request"; +"vpn.feedback-form.category.feature-request" = "Požadavek na funkci VPN"; /* Title for the category 'VPN causes issues with other apps or websites' category of the VPN feedback form */ -"vpn.feedback-form.category.issues-with-apps" = "VPN causes issues with other apps or websites"; +"vpn.feedback-form.category.issues-with-apps" = "VPN způsobuje problémy s jinými aplikacemi nebo webovými stránkami"; /* Title for the local device connectivity category of the VPN feedback form */ -"vpn.feedback-form.category.local-device-connectivity" = "VPN won't let me connect to local device"; +"vpn.feedback-form.category.local-device-connectivity" = "VPN mi nedovolí připojit se k místnímu zařízení"; /* Title for the 'other VPN feedback' category of the VPN feedback form */ -"vpn.feedback-form.category.other" = "Other VPN feedback"; +"vpn.feedback-form.category.other" = "Jiná zpětná vazba k VPN"; /* Title for the category selection state of the VPN feedback form */ -"vpn.feedback-form.category.select-category" = "Select a category"; +"vpn.feedback-form.category.select-category" = "VYBER KATEGORII"; /* Title for the 'VPN is too slow' category of the VPN feedback form */ -"vpn.feedback-form.category.too-slow" = "VPN connection is too slow"; +"vpn.feedback-form.category.too-slow" = "Připojení k VPN je příliš pomalé"; /* Title for the 'unable to install' category of the VPN feedback form */ -"vpn.feedback-form.category.unable-to-install" = "Unable to install VPN"; +"vpn.feedback-form.category.unable-to-install" = "VPN není možné nainstalovat"; /* Title for the feedback sent view description of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.description" = "Your feedback will help us improve the\nDuckDuckGo VPN."; +"vpn.feedback-form.sending-confirmation.description" = "Tvoje zpětná vazba nám pomůže zlepšit\n DuckDuckGo VPN."; /* Title for the feedback sending error text of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.error" = "We couldn't send your feedback right now, please try again."; +"vpn.feedback-form.sending-confirmation.error" = "Tvoji zpětnou vazbu se nám nepodařilo odeslat, zkus to prosím znovu."; /* Title for the feedback sent view title of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.title" = "Thank you!"; +"vpn.feedback-form.sending-confirmation.title" = "Děkujeme!"; /* Toast message when the VPN feedback form is submitted successfully */ -"vpn.feedback-form.submitted.message" = "Thank You! Feedback submitted."; +"vpn.feedback-form.submitted.message" = "Děkujeme! Zpětná vazba odeslána."; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-1" = "Please describe what's happening, what you expected to happen, and the steps that led to the issue:"; +"vpn.feedback-form.text-1" = "Popiš, co se děje, co se mělo stát a jaké kroky k problému vedly:"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-2" = "In addition to the details entered into this form, your app issue report will contain:"; +"vpn.feedback-form.text-2" = "Kromě údajů zadaných do tohoto formuláře bude zpráva o problému s aplikací obsahovat:"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-3" = "• Whether specific DuckDuckGo features are enabled"; +"vpn.feedback-form.text-3" = "• Jestli jsou zapnuté konkrétní funkce DuckDuckGo"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-4" = "• Aggregate DuckDuckGo app diagnostics"; +"vpn.feedback-form.text-4" = "• Agregovanou diagnostiku aplikace DuckDuckGo"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-5" = "By tapping \"Submit\" I agree that DuckDuckGo may use the information in this report for purposes of improving the app's features."; +"vpn.feedback-form.text-5" = "Klepnutím na tlačítko Odeslat vyjadřuji souhlas s tím, aby služba DuckDuckGo mohla použít informace z této zprávy k vylepšování funkcí aplikace."; /* Title for each screen of the VPN feedback form */ -"vpn.feedback-form.title" = "Help Improve the DuckDuckGo VPN"; +"vpn.feedback-form.title" = "Pomoz zlepšit DuckDuckGo VPN"; /* VPN settings screen cell text for adding the VPN widget to the home screen */ "vpn.settings.add.widget" = "Přidat widget VPN na domovskou obrazovku"; /* Disclaimer for the DNS Server section on the DNS Server screen */ -"vpn.settings.dns.section-disclaimer" = "Using a custom DNS server can impact browsing speeds and expose your activity to third parties if the server isn't secure or reliable."; +"vpn.settings.dns.section-disclaimer" = "Používání vlastního serveru DNS může ovlivnit rychlost procházení a odhalit tvoji aktivitu třetím stranám, pokud server není bezpečný nebo spolehlivý."; /* Header text for the DNS section on the VPN Settings screen */ "vpn.settings.dns.section-header" = "DNS"; /* Title for the Apply button on the DNS Server setting screen */ -"vpn.settings.dns.server.apply.button.title" = "Apply"; +"vpn.settings.dns.server.apply.button.title" = "Aplikovat"; /* Default value for the DNS Server row on the VPN Settings screen */ "vpn.settings.dns.server.default.value" = "DuckDuckGo"; /* Title for the IPv4 Address setting */ -"vpn.settings.dns.server.ipv4.title" = "IPv4 Address"; +"vpn.settings.dns.server.ipv4.title" = "Adresa IPv4"; /* Custom option for the DNS Server setting */ -"vpn.settings.dns.server.option.custom" = "Custom"; +"vpn.settings.dns.server.option.custom" = "Vlastní"; /* Recommended option for the DNS Server setting */ -"vpn.settings.dns.server.option.default" = "DuckDuckGo (Recommended)"; +"vpn.settings.dns.server.option.default" = "DuckDuckGo (doporučeno)"; /* Title for the DNS Server setting screen */ -"vpn.settings.dns.server.screen.title" = "DNS Server"; +"vpn.settings.dns.server.screen.title" = "DNS server"; /* Title for the DNS Server row on the VPN Settings screen */ -"vpn.settings.dns.server.title" = "DNS Server"; +"vpn.settings.dns.server.title" = "DNS server"; /* Title for the button to enable push notifications in system settings */ "waitlist.allow-notifications" = "Povolit oznámení"; diff --git a/DuckDuckGo/cs.lproj/OmniBar.strings b/DuckDuckGo/cs.lproj/OmniBar.strings index dd249e47bf..11345734db 100644 --- a/DuckDuckGo/cs.lproj/OmniBar.strings +++ b/DuckDuckGo/cs.lproj/OmniBar.strings @@ -7,6 +7,9 @@ /* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "Dt8-We-5tV"; */ "Dt8-We-5tV.accessibilityLabel" = "Vymazat text"; +/* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "gb5-wL-4OW"; */ +"gb5-wL-4OW.accessibilityLabel" = "Vymazat text"; + /* Class = "UIButton"; accessibilityLabel = "Share"; ObjectID = "hor-Jd-3kw"; */ "hor-Jd-3kw.accessibilityLabel" = "Sdílet"; diff --git a/DuckDuckGo/cs.lproj/Settings.strings b/DuckDuckGo/cs.lproj/Settings.strings index 209b3e80b4..6fd0cc9e46 100644 --- a/DuckDuckGo/cs.lproj/Settings.strings +++ b/DuckDuckGo/cs.lproj/Settings.strings @@ -10,9 +10,6 @@ /* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "5YD-Od-uDU"; */ "5YD-Od-uDU.text" = "Automaticky vymazat data"; -/* Class = "UIButton"; normalTitle = "Settings"; ObjectID = "8YG-7Q-Czd"; */ -"8YG-7Q-Czd.normalTitle" = "Nastavení"; - /* Class = "UILabel"; text = "App Exit, Inactive for 1 Hour"; ObjectID = "9EC-lx-77p"; */ "9EC-lx-77p.text" = "Ukončení aplikace, neaktivní po dobu 1 hodiny"; @@ -76,9 +73,6 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Nechráněné stránky"; -/* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ -"ssa-zd-L3T.title" = "Velikost textu"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Zobrazit klávesnici na"; diff --git a/DuckDuckGo/da.lproj/DaxOnboarding.strings b/DuckDuckGo/da.lproj/DaxOnboarding.strings index aaec64e5fa..b9e0b85916 100644 --- a/DuckDuckGo/da.lproj/DaxOnboarding.strings +++ b/DuckDuckGo/da.lproj/DaxOnboarding.strings @@ -1,12 +1,6 @@ /* Class = "UIButton"; normalTitle = "Hide"; ObjectID = "hgy-75-OvJ"; */ "hgy-75-OvJ.normalTitle" = "Skjul"; -/* Class = "UILabel"; text = "Welcome to\nDuckDuckGo!"; ObjectID = "Lvy-9G-3gr"; */ -"Lvy-9G-3gr.text" = "Velkommen til\nDuckDuckGo!"; - -/* Class = "UIButton"; normalTitle = "Let’s Do It!"; ObjectID = "oAv-Zt-uIt"; */ -"oAv-Zt-uIt.normalTitle" = "Lad os gøre det!"; - /* Class = "UIButton"; normalTitle = "Button"; ObjectID = "zJt-F9-L48"; */ "zJt-F9-L48.normalTitle" = "Knap"; diff --git a/DuckDuckGo/da.lproj/Localizable.strings b/DuckDuckGo/da.lproj/Localizable.strings index 0f9f20718b..20065306df 100644 --- a/DuckDuckGo/da.lproj/Localizable.strings +++ b/DuckDuckGo/da.lproj/Localizable.strings @@ -341,7 +341,7 @@ "autofill.keep-enabled.alert.disable" = "Deaktiver"; /* A link that takes the user to the DuckDuckGo help pages explaining password managers */ -"autofill.learn.more.link.title" = "Learn More"; +"autofill.learn.more.link.title" = "Mere info"; /* Button displayed after saving/updating an autofill login that takes the user to the saved login */ "autofill.login-save-action-button.toast" = "Se"; @@ -977,9 +977,6 @@ /* ad = advertisment */ "dax.onboarding.home.subsequent" = "Du har den!\n\nHusk: hver gang du browser med mig, mister en uhyggelig annonce sine vinger. 👍"; -/* No comment provided by engineer. */ -"dax.onboarding.message" = "Internettet kan være ret uhyggeligt.\n\nBare rolig! Det er lettere at søge og browse privat, end du tror."; - /* No comment provided by engineer. */ "Debug" = "Fejlfinding"; @@ -1080,7 +1077,7 @@ "duckPlayer.never.label" = "Aldrig"; /* Body text for the modal feature explanation */ -"duckplayer.presentation.modal.body" = "Duck Player giver dig mulighed for at se YouTube uden målrettede annoncer i en biograflignende oplevelse i DuckDuckGo, og det, du ser, påvirker ikke dine anbefalinger."; +"duckplayer.presentation.modal.body" = "Duck Player giver dig mulighed for at se YouTube uden målrettede annoncer i DuckDuckGo, og det, du ser, påvirker ikke dine anbefalinger."; /* Button that will dismiss the modal */ "duckplayer.presentation.modal.dismiss-button" = "Forstået"; @@ -1088,17 +1085,23 @@ /* Two line title (separated by \n) for the feature explanation */ "duckplayer.presentation.modal.title" = "Drukner du i annoncer på YouTube? Prøv Duck Player!"; +/* Two line title (separated by \n) for the feature explanation */ +"duckplayer.presentation.modal.title.youtube" = "Drukner du i annoncer på YouTube? Ikke med Duck Player!"; + /* Footer label in the settings screen for Duck Player */ "duckplayer.settings.footer" = "DuckDuckGo giver dig de Privacy Essentials, du har brug for for at beskytte dig selv, når du surfer på nettet."; /* Text explaining what Duck Player is in the settings screen. */ -"duckplayer.settings.info-text" = "Duck Player giver en ren seeroplevelse uden målrettede annoncer og forhindrer, at visningsaktivitet påvirker dine YouTube-anbefalinger."; +"duckplayer.settings.info-text" = "Duck Player giver dig mulighed for at se YouTube uden målrettede annoncer i DuckDuckGo, og det, du ser, påvirker ikke dine anbefalinger."; /* Button that takes the user to learn more about Duck Player. */ "duckplayer.settings.learn-more" = "Mere info"; +/* Settings screen cell text for DuckPlayer settings to open in new tab */ +"duckplayer.settings.open-new-tab-label" = "Åbn Duck Player i en ny fane"; + /* Settings screen cell text for DuckPlayer settings */ -"duckplayer.settings.open-videos-in" = "Åbn videoer i Duck Player"; +"duckplayer.settings.open-videos-in" = "Åbn YouTube-videoer i Duck Player"; /* Settings screen cell text for DuckPlayer settings */ "duckplayer.settings.title" = "Duck Player"; @@ -1169,9 +1172,6 @@ /* Title for prompt to sign up for email protection */ "email.signup-prompt.title" = "Skjul din e-mail og \n bloker trackere"; -/* No comment provided by engineer. */ -"empty!" = "tom!"; - /* Empty list state placholder */ "empty.bookmarks" = "Ingen bogmærker tilføjet endnu"; @@ -1322,6 +1322,39 @@ /* Confirmation button */ "feedback.form.submit" = "Indsend"; +/* Title of the feedback form */ +"feedback.form.title" = "Hjælp med at forbedre Privacy Pro"; + +/* Category for Identity Theft Restoration feedback */ +"feedback.general.category.itr" = "Identity Theft Restoration"; + +/* Category for Personal Info Removal feedback */ +"feedback.general.category.pir" = "Fjernelse af personlige oplysninger"; + +/* Category for subscription and payments feedback */ +"feedback.general.category.ppro" = "Abonnement og betalinger"; + +/* Prompt to select a category for general feedback */ +"feedback.general.category.select" = "VÆLG EN KATEGORI"; + +/* Category for VPN feedback */ +"feedback.general.category.vpn" = "VPN"; + +/* Category for access code issues */ +"feedback.itr.category.access-code" = "Problem med adgangskoden"; + +/* Category for issues contacting an advisor */ +"feedback.itr.category.cant-contact-advisor" = "Kan ikke kontakte rådgiver"; + +/* Prompt to select a category for Identity Theft Restoration feedback */ +"feedback.itr.category.select" = "VÆLG EN KATEGORI"; + +/* Category for other Identity Theft Restoration issues */ +"feedback.itr.category.something-else" = "Noget andet"; + +/* Category for unhelpful advisor calls */ +"feedback.itr.category.unhelpful" = "Opkald til rådgiver var ikke hjælpsomt"; + /* No comment provided by engineer. */ "feedback.negative.form.genericPlaceholder" = "Vær så specifik som muligt"; @@ -1361,6 +1394,24 @@ /* No comment provided by engineer. */ "feedback.performance.slowLoading" = "Websider eller søgeresultater indlæses langsomt"; +/* Category for when scan finds incorrect records */ +"feedback.pir.category.not-me" = "Scanningen fandt poster, der ikke vedrører mig"; + +/* Category for when scan doesn't find info on a specific site */ +"feedback.pir.category.nothing-on-site" = "Scanningen fandt ikke mine oplysninger på en specifik side"; + +/* Category for other Personal Info Removal issues */ +"feedback.pir.category.other" = "Noget andet"; + +/* Category for when the removal process is stuck */ +"feedback.pir.category.removal-stuck" = "Fjernelsesprocessen er gået i stå"; + +/* Category for when the scan is stuck */ +"feedback.pir.category.scan-stuck" = "Scanningen efter poster er gået i stå"; + +/* Prompt to select a category for Personal Info Removal feedback */ +"feedback.pir.category.select" = "VÆLG EN KATEGORI"; + /* Header above input field */ "feedback.positive.form.header" = "Del oplysninger"; @@ -1379,6 +1430,15 @@ /* Button encouraging uses to share details aboout their feedback */ "feedback.positive.submit" = "Del oplysninger"; +/* Category for other Privacy Pro issues */ +"feedback.ppro.category.other" = "Noget andet"; + +/* Category for one-time password issues */ +"feedback.ppro.category.otp" = "Problem med engangskode"; + +/* Prompt to select a category for Privacy Pro feedback */ +"feedback.ppro.category.select" = "VÆLG EN KATEGORI"; + /* No comment provided by engineer. */ "feedback.start.footer" = "Din anonyme feedback er vigtig for os."; @@ -1529,12 +1589,6 @@ /* No comment provided by engineer. */ "keyCommandShowAllTabs" = "Vis alle faner"; -/* Please preserve newline character */ -"launchscreenWelcomeMessage" = "Velkommen til\nDuckDuckGo!"; - -/* No comment provided by engineer. */ -"LOREM IPSUM" = "LOREM IPSUM"; - /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "DuckDuckGo til Mac har den hastighed, du har brug for, de browserfunktioner, du forventer, og leveres spækket med vores bedste privatlivsartikler i klassen."; @@ -1586,62 +1640,83 @@ /* Edit button */ "navigation.title.edit" = "Rediger"; -/* String indicating NetP is connected when viewed from the settings screen */ -"netP.cell.connected" = "Connected"; - -/* String indicating NetP is disconnected when viewed from the settings screen */ -"netP.cell.disconnected" = "Not connected"; - -/* Title for the DuckDuckGo VPN feature in settings */ -"netP.settings.title" = "VPN"; +/* String indicating NetP is snoozing when viewed from the settings screen */ +"netP.cell.snoozing" = "Sat på pause"; /* Title for the DuckDuckGo VPN feature */ "netP.title" = "DuckDuckGo VPN"; +/* Button title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.action" = "Tilføj widget"; + +/* Message for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.message" = "Slå VPN til og fra direkte fra startskærmen."; + +/* Title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.title" = "Tilføj VPN-widget"; + /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN er afbrudt på grund af et udløbet abonnement. Abonner på Privacy Pro for at genoprette forbindelsen til DuckDuckGo VPN."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Netværksbeskyttelse kunne ikke oprette forbindelse. Prøv igen senere."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "DuckDuckGo VPN kunne ikke oprette forbindelse. Prøv igen senere."; /* Title for the VPN FAQ screen. */ "network.protection.faq.title" = "Ofte stillede spørgsmål om DuckDuckGo VPN"; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Netværksbeskyttelse blev afbrudt. Forsøger at genoprette forbindelse ..."; +/* Message for tooltip about geoswitching */ +"network.protection.geoswitching.tip.message" = "Du kan vælge din VPN-placering ved at oprette forbindelse til hvilken som helst af vores servere i hele verden."; + +/* Title for tooltip about geoswitching */ +"network.protection.geoswitching.tip.title" = "Skift din placering"; + +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "DuckDuckGo VPN blev afbrudt. Forsøger at genoprette forbindelse ..."; /* Message for the network protection invite dialog */ -"network.protection.invite.dialog.message" = "Enter your invite code to get started."; +"network.protection.invite.dialog.message" = "Indtast din invitationskode for at komme i gang."; /* Title for the network protection invite screen */ "network.protection.invite.dialog.title" = "Du er inviteret til at prøve DuckDuckGo VPN"; /* Prompt for the network protection invite code text field */ -"network.protection.invite.field.prompt" = "Invite Code"; +"network.protection.invite.field.prompt" = "Invitationskode"; /* Message for the network protection invite success view */ -"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; +"network.protection.invite.success.message" = "Skjul din placering fra websteder, og skjul din onlineaktivitet for internetudbydere og andre på dit netværk."; /* Title for the network protection invite success view */ -"network.protection.invite.success.title" = "Success! You’re in."; +"network.protection.invite.success.title" = "Succes! Du er klar."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; /* Title text for an iOS quick action that opens VPN settings */ "network.protection.quick-action.open-vpn" = "Åbn VPN"; +/* Button title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.action" = "Mere info"; + +/* Message for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.message" = "Du kan bruge websteder eller apps, der blokerer VPN-trafik, ved at \"snooze\" VPN-forbindelsen."; + +/* Title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.title" = "Undgå VPN-konflikter"; + +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN er sat på pause i %@"; + /* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ "network.protection.status.connected.format" = "Forbundet · %@"; /* The label for the NetP VPN when connecting */ -"network.protection.status.connecting" = "Connecting..."; +"network.protection.status.connecting" = "Forbinder..."; /* The label for the NetP VPN when disconnected */ -"network.protection.status.disconnected" = "Not connected"; +"network.protection.status.disconnected" = "Ikke forbundet"; /* The label for the NetP VPN when disconnecting */ -"network.protection.status.disconnecting" = "Disconnecting..."; +"network.protection.status.disconnecting" = "Afbryder..."; /* Message label text for the status view when VPN is disconnected */ "network.protection.status.header.message.off" = "Opret forbindelse for at sikre al din enheds\ninternettrafik."; @@ -1655,26 +1730,41 @@ /* Header title label text for the status view when VPN is connected */ "network.protection.status.header.title.on" = "DuckDuckGo VPN er tændt"; +/* Header title label text for the status view when VPN is snoozing */ +"network.protection.status.header.title.snoozed" = "DuckDuckGo VPN er sat på pause"; + /* The status view 'Share Feedback' button which is shown inline on the status view after the temporary free use footer text */ -"network.protection.status.menu.share.feedback" = "Share Feedback"; +"network.protection.status.menu.share.feedback" = "Del feedback"; + +/* The label for the NetP VPN when paused */ +"network.protection.status.paused" = "Sat på pause"; + +/* The label for when NetP VPN is snoozing plus the length of time remaining formatted as '0:00' */ +"network.protection.status.snoozing.format" = "Sat på pause, %@ tilbage"; + +/* Snooze button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.snooze" = "Sat på pause i 20 minutter"; + +/* Wake Up button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.wake-up" = "Afbryd pause"; /* Connection details label shown in NetworkProtection's status view. */ -"network.protection.status.view.connection.details" = "Connection Details"; +"network.protection.status.view.connection.details" = "Forbindelsesdetaljer"; /* Custom DNS label shown in NetworkProtection's status view. */ -"network.protection.status.view.custom.dns" = "DNS Server"; +"network.protection.status.view.custom.dns" = "DNS-server"; /* Generic connection failed error message shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.message" = "Please try again later."; +"network.protection.status.view.error.connection.failed.message" = "Prøv igen senere."; /* Generic connection failed error title shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; +"network.protection.status.view.error.connection.failed.title" = "Kunne ikke oprette forbindelse."; /* IP Address label shown in NetworkProtection's status view. */ -"network.protection.status.view.ip.address" = "IP Address"; +"network.protection.status.view.ip.address" = "IP-adresse"; /* Location label shown in NetworkProtection's status view. */ -"network.protection.status.view.location" = "Location"; +"network.protection.status.view.location" = "Placering"; /* Label shown on the title of the settings section in NetworkProtection's status view. */ "network.protection.status.view.settings.section.title" = "Administrer"; @@ -1682,12 +1772,15 @@ /* Title label text for the status view when netP is disconnected */ "network.protection.status.view.title" = "VPN"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Netværksbeskyttelse er TIL. Din placering og onlineaktivitet er beskyttet."; +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "DuckDuckGo VPN er tændt. Din placering og onlineaktivitet er beskyttet."; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Router enhedens trafik gennem %@."; +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "VPN-pausen er afsluttet. Enhedens trafik routes gennem %@."; + /* Title for the button to link to the iOS app settings and enable notifications app-wide. */ "network.protection.turn.on.notifications.button.title" = "Slå notifikationer til"; @@ -1698,13 +1791,13 @@ "network.protection.vpn.about" = "Omkring"; /* Section header for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.section.header" = "Notifications"; +"network.protection.vpn.alerts.section.header" = "Meddelelser"; /* List section footer for the toggle for VPN alerts. */ "network.protection.vpn.alerts.toggle.section.footer" = "Få besked, hvis din forbindelse falder ud, eller VPN-status ændres."; /* Title for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.toggle.title" = "VPN Notifications"; +"network.protection.vpn.alerts.toggle.title" = "VPN-meddelelser"; /* Title for the data volume section in the VPN status screen */ "network.protection.vpn.data-volume" = "Datamængde"; @@ -1713,13 +1806,13 @@ "network.protection.vpn.exclude.local.networks.setting.footer" = "Lad lokal trafik omgå VPN'en og oprette forbindelse til enheder på dit lokale netværk, f.eks. en printer."; /* Header text for the Exclude Local Networks setting item. */ -"network.protection.vpn.exclude.local.networks.setting.header" = "General"; +"network.protection.vpn.exclude.local.networks.setting.header" = "Generelt"; /* Title for the Exclude Local Networks setting item. */ "network.protection.vpn.exclude.local.networks.setting.title" = "Ekskluder lokale netværk"; /* Title for the VPN Location screen's All Countries section. */ -"network.protection.vpn.location.all.countries.section.title" = "All Countries"; +"network.protection.vpn.location.all.countries.section.title" = "Alle lande"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.connected" = "Tilsluttet placering"; @@ -1734,7 +1827,7 @@ "network.protection.vpn.location.recommended.section.footer" = "Opret automatisk forbindelse til den nærmeste server, vi kan finde."; /* Title for the VPN Location screen's Recommended section. */ -"network.protection.vpn.location.recommended.section.title" = "Recommended"; +"network.protection.vpn.location.recommended.section.title" = "Anbefalet"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.selected" = "Valgt placering"; @@ -1752,13 +1845,13 @@ "network.protection.vpn.preferred.location.nearest" = "Nærmeste placering"; /* Title for the Preferred Location VPN Settings item. */ -"network.protection.vpn.preferred.location.title" = "Preferred Location"; +"network.protection.vpn.preferred.location.title" = "Foretrukket placering"; /* Footer text for the DNS server setting item. */ -"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo routes DNS queries through our DNS servers so your internet provider can't see what websites you visit."; +"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo dirigerer DNS-forespørgsler gennem vores DNS-servere, så din internetudbyder ikke kan se, hvilke hjemmesider du besøger."; /* Title for the FAQ row in the VPN status screen. */ -"network.protection.vpn.settings.faq" = "FAQs and Support"; +"network.protection.vpn.settings.faq" = "Ofte stillede spørgsmål og support"; /* Title for the feedback row in the VPN status screen. */ "network.protection.vpn.settings.share-feedback" = "Giv feedback om VPN"; @@ -1940,6 +2033,66 @@ /* Deactivate button */ "pm.deactivate" = "Deaktiver"; +/* Label for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.label" = "Emailadresse (valgfrit)"; + +/* Placeholder for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.placeholder" = "navn@e-mail.web"; + +/* Placeholder for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.placeholder" = "Giv os feedback..."; + +/* Title for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.title" = "Generel feedback"; + +/* Title for the ITR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-itr-problem.title" = "Identity Theft Restoration"; + +/* Title for the PIR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-pir-problem.title" = "Personal Information Removal"; + +/* Title for the Subscriptions and Payments category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-ppro-problem.title" = "Abonnementer og betalinger"; + +/* Placeholder for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.placeholder" = "Fortæl os, hvad der sker…"; + +/* Title for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.title" = "Anmeld et problem"; + +/* Title for the VPN category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-vpn-problem.title" = "VPN"; + +/* Placeholder for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.placeholder" = "Hvilken funktion kunne du tænke dig at se?"; + +/* Title for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.title" = "Anmodning om funktion"; + +/* Title for the category selection section in the Privacy Pro feedback form */ +"ppro.feedback-form.select-category.title" = "VÆLG EN KATEGORI"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-1" = "Har du fundet et problem, der ikke er dækket i vores [hjælpecenter](duck://)? Så vil vi helt sikkert gerne vide det.\n\nAngiv en e-mailadresse, hvis du gerne vil have, at vi kontakter dig angående dette problem (vi kan muligvis ikke svare på alle problemer):"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-2" = "Ud over de oplysninger, der er angivet ovenfor, sender vi nogle anonymiserede oplysninger med din feedback:"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-3" = "• Hvilke browserfunktioner, der er aktive"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-4" = "• Samlet appdiagnostik (f.eks. fejlkoder)"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-5" = "Ved at trykke på \"Send\" accepterer du, at DuckDuckGo må bruge de indsendte oplysninger til at forbedre appens funktioner."; + +/* Title for the text box in the Privacy Pro feedback form */ +"ppro.feedback-form.text-box.title" = "Feedback"; + +/* Title for the Privacy Pro feedback form */ +"ppro.feedback-form.title" = "Send tilbagemelding"; + /* Button title for sync bookmarks limits exceeded warning to go to manage bookmarks */ "prefrences.sync.bookmarks-limit-exceeded-action" = "Administrer bogmærker"; @@ -2029,6 +2182,18 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Favoritter"; +/* Name of the option the user can chose to give general browser feedback */ +"send.browser.feedback.general-feedback" = "Generel feedback"; + +/* Name of the option the user can chose to give browser feedback about a problem they enountered */ +"send.browser.feedback.report-problem" = "Anmeld et problem"; + +/* Name of the option the user can chose to give browser feedback about a feature they would like */ +"send.browser.feedback.request-feature" = "Foreslå en funktion"; + +/* Title of the picker where the user can chose the category of the feedback they want ot send. */ +"send.browser.feedback.select-category" = "VÆLG EN KATEGORI"; + /* Settings cell for About DDG */ "settings.about.ddg" = "Om DuckDuckGo"; @@ -2084,6 +2249,9 @@ /* Section footer Autolock description */ "settings.autolock.description" = "Hvis Touch ID, Face ID eller en systemadgangskode er aktiveret, bliver du bedt om at låse appen op, når du åbner den."; +/* Settings cell for Browser Feedback */ +"settings.browser.feedback" = "Browserfeedback"; + /* Settings screen cell text for Automatically Clearing Data */ "settings.clear.data" = "Ryd data automatisk"; @@ -2182,7 +2350,7 @@ "settings.ppro" = "Privacy Pro"; /* Title for Link in the Footer of Privacy Pro section */ -"settings.ppro.footer" = "Privacy Policy and Terms of Service"; +"settings.ppro.footer" = "Fortrolighedspolitik og servicevilkår"; /* Settings screen cell for long press previews */ "settings.previews" = "Eksempler med lang tryk"; @@ -2208,23 +2376,20 @@ /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Angiv placeringen af adresselinjen"; -/* Subscription activation pending description */ -"settings.subscription.activation.pending.description" = "This is taking longer than usual, please check back later."; +/* Privacy pro description subtitle in settings when the is activating */ +"settings.subscription.activating" = "Aktiverer"; -/* Subscription activation pending title */ -"settings.subscription.activation.pending.title" = "Dit abonnement er ved at blive aktiveret"; +/* Subscription activation pending description */ +"settings.subscription.activation.pending.description" = "Dette tager længere tid end normalt. Kom tilbage senere."; /* Data Broker protection cell subtitle for privacy pro */ -"settings.subscription.DBP.subtitle" = "Remove your info from sites that sell it"; +"settings.subscription.DBP.subtitle" = "Fjern dine oplysninger fra sider, der sælger dem"; /* Data Broker protection cell title for privacy pro */ "settings.subscription.DBP.title" = "Personal Information Removal"; -/* Privacy pro description subtext */ -"settings.subscription.description" = "Endnu mere fortrolighed med tre nye beskyttelser:"; - /* I have a Subscription button text for privacy pro */ -"settings.subscription.existing.subscription" = "I Have a Subscription"; +"settings.subscription.existing.subscription" = "Jeg har et abonnement"; /* Subscription expired description */ "settings.subscription.expired.comment" = "Abonner igen for at fortsætte med at bruge Privacy Pro"; @@ -2232,23 +2397,26 @@ /* Subscription expired tittle message */ "settings.subscription.expired.title" = "Dit Privacy Pro-abonnement er udløbet"; -/* Privacy pro features list */ -"settings.subscription.features" = " • VPN\n • Personal Information Removal\n • Identity Theft Restoration"; - /* Identity theft restoration cell subtitle for privacy pro */ -"settings.subscription.ITR.subtitle" = "If your identity is stolen, we'll help restore it"; +"settings.subscription.ITR.subtitle" = "Hvis din identitet bliver stjålet, hjælper vi med at genskabe den"; /* Identity theft restoration cell title for privacy pro */ "settings.subscription.ITR.title" = "Identity Theft Restoration"; /* Get Privacy Pro button text for privacy pro */ -"settings.subscription.learn.more" = "Get Privacy Pro"; +"settings.subscription.learn.more" = "Få Privacy Pro"; /* Subscription Settings button text for privacy pro */ -"settings.subscription.manage" = "Subscription Settings"; +"settings.subscription.manage" = "Abonnementsindstillinger"; + +/* Privacy Pro description subtitle in settings */ +"settings.subscription.row.description" = "Inkluderer vores VPN og Identity Theft Restoration."; -/* Call to action title for Privacy Pro */ -"settings.subscription.subscribe" = "Subscribe to Privacy Pro"; +/* Call to action title for Privacy Pro settings */ +"settings.subscription.subscribe" = "Beskyt din forbindelse og identitet med Privacy Pro"; + +/* Privacy pro description subtitle in settings */ +"settings.subscription.us.description" = "Inkluderer vores VPN, Personal Information Removal og Identity Theft Restoration."; /* VPN cell title for privacy pro */ "settings.subscription.VPN.title" = "VPN"; @@ -2317,67 +2485,64 @@ "siteFeedback.urlPlaceholder" = "Hvilket websted er ødelagt?"; /* Subscription Activation Info */ -"subscription.activate..header.description" = "Access your Privacy Pro subscription on this device via Apple ID or an email address."; +"subscription.activate..header.description" = "Få adgang til dit Privacy Pro-abonnement på denne enhed via Apple ID eller en e-mailadresse."; /* Button for adding email address to subscription */ -"subscription.activate.add.email.button" = "Add Email"; +"subscription.activate.add.email.button" = "Tilføj e-mailadresse"; /* Apple ID option for activation */ "subscription.activate.appleid" = "Apple ID"; /* Button text for restoring purchase via Apple ID */ -"subscription.activate.appleid.button" = "Restore Purchase"; +"subscription.activate.appleid.button" = "Gendan køb"; /* Description for Apple ID activation */ -"subscription.activate.appleid.description" = "Restore your purchase to activate your subscription on this device."; +"subscription.activate.appleid.description" = "Gendan dit køb for at aktivere dit abonnement på denne enhed."; /* Subscription Activation Info */ -"subscription.activate.description" = "Your subscription is automatically available in DuckDuckGo on any device signed in to your Apple ID."; +"subscription.activate.description" = "Dit abonnement er automatisk tilgængeligt i DuckDuckGo på enhver enhed, der er logget ind med dit Apple-id."; /* Button for editing email address added to subscription */ -"subscription.activate.edit.email.button" = "Edit Email"; +"subscription.activate.edit.email.button" = "Rediger e-mailadresse"; /* View Title for editing your email account */ -"subscription.activate.edit.email.title" = "Edit Email"; +"subscription.activate.edit.email.title" = "Rediger e-mailadresse"; /* Email option for activation */ -"subscription.activate.email" = "Email"; +"subscription.activate.email" = "E-Post"; /* Restore button title for Email */ -"subscription.activate.email.button" = "Enter Email"; +"subscription.activate.email.button" = "Indtast e-mailadresse"; /* Description for Email activation */ -"subscription.activate.email.description" = "Use your email to activate your subscription on this device."; +"subscription.activate.email.description" = "Brug din e-mailadresse til at aktivere dit abonnement på denne enhed."; /* Activate subscription title */ "subscription.activate.email.title" = "Aktivér abonnement"; /* Button title for cancelling email deletion */ -"subscription.activate.manage.email.cancel" = "Cancel"; +"subscription.activate.manage.email.cancel" = "Annullér"; /* Button title for confirming email deletion */ -"subscription.activate.manage.email.OK" = "OK"; +"subscription.activate.manage.email.OK" = "Okay"; /* Restore button title for AppleID */ -"subscription.activate.restore.apple" = "Restore Purchase"; +"subscription.activate.restore.apple" = "Gendan køb"; /* Subscription Activation Title */ -"subscription.activate.title" = "Activate your subscription on this device"; +"subscription.activate.title" = "Aktivér dit abonnement på denne enhed"; /* Resend activation instructions button */ -"subscription.add.device.resend.instructions" = "Resend Instructions"; +"subscription.add.device.resend.instructions" = "Send anvisninger igen"; /* Add email to an existing subscription */ -"subscription.add.email" = "Add an email address to activate your subscription on your other devices. We’ll only use this address to verify your subscription."; +"subscription.add.email" = "Tilføj en e-mailadresse for at aktivere dit abonnement på dine andre enheder. Vi vil kun bruge denne adresse til at bekræfte dit abonnement."; /* View title for adding email to subscription */ -"subscription.add.email.title" = "Add Email"; - -/* Title for Alert messages */ -"subscription.alert.title" = "subscription.alert.title"; +"subscription.add.email.title" = "Tilføj e-mailadresse"; /* Subscription availability message on Apple devices */ -"subscription.available.apple" = "Privacy Pro is available on any device signed in to the same Apple ID."; +"subscription.available.apple" = "Privacy Pro er tilgængelig på enhver enhed, der er logget ind med det samme Apple ID."; /* Text for the manage billing page */ "subscription.billing.google.text" = "Dit abonnement blev købt gennem Google Play-butikken. Hvis du vil forny dit abonnement, skal du åbne abonnementsindstillingerne for Google Play-butikken på en enhed, der er logget ind på den samme Google-konto, som oprindeligt blev brugt til at købe dit abonnement."; @@ -2385,89 +2550,86 @@ /* Title for the manage billing page */ "subscription.billing.google.title" = "Abonnementer"; -/* Subscription annual billing period type */ -"subscription.billing.period.annual" = "annual"; - -/* Subscription monthly billing period type */ -"subscription.billing.period.monthly" = "monthly"; - /* Subscription Removal confirmation message */ -"subscription.cancel.message" = "Your subscription has been removed from this device."; +"subscription.cancel.message" = "Dit abonnement er blevet fjernet fra denne enhed."; /* Change plan or cancel title */ -"subscription.change.plan" = "Update Plan or Cancel"; +"subscription.change.plan" = "Opdater abonnement eller annuller"; /* Navigation Button for closing subscription view */ -"subscription.close" = "Close"; +"subscription.close" = "Luk"; /* Title for Confirm messages */ -"subscription.confirm.title" = "Are you sure?"; +"subscription.confirm.title" = "Er du sikker?"; /* Header for section for activating subscription on other devices */ -"subscription.devices.header" = "Activate on Other Devices"; +"subscription.devices.header" = "Aktivér på andre enheder"; /* Footer for section for activating subscription on other devices when email was not yet added */ -"subscription.devices.no.email.footer" = "Add an optional email to your subscription or use your Apple ID to access Privacy Pro on other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.no.email.footer" = "Tilføj en valgfri e-mailadresse til dit abonnement for at få adgang til Privacy Pro på andre enheder. **[Læs mere](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Footer for section for activating subscription on other devices when email is added */ -"subscription.devices.with.email.footer" = "Use this email to activate your subscription in Settings > Privacy Pro in the DuckDuckGo app on your other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.with.email.footer" = "Brug denne e-mailadresse til at aktivere dit abonnement i Indstillinger > Privacy Pro i DuckDuckGo-appen på dine andre enheder. **[Læs mere](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Alert content for not found subscription */ -"subscription.email.inactive.alert.message" = "The subscription associated with this email is no longer active."; +"subscription.email.inactive.alert.message" = "Abonnementet, der er knyttet til denne e-mailadresse, er ikke længere aktivt."; /* Alert title for not found subscription */ -"subscription.email.inactive.alert.title" = "Subscription Not Found"; +"subscription.email.inactive.alert.title" = "Abonnement ikke fundet"; /* Alert content for not found subscription */ -"subscription.expired.alert.message" = "The subscription associated with this Apple ID is no longer active."; +"subscription.expired.alert.message" = "Abonnementet, der er knyttet til dette Apple-id, er ikke længere aktivt."; /* FAQ Button */ -"subscription.faq" = "FAQs and Support"; +"subscription.faq" = "Ofte stillede spørgsmål og support"; /* FAQ Description */ -"subscription.faq.description" = "Get answers to frequently asked questions or contact Privacy Pro support from our help pages."; +"subscription.faq.description" = "Få svar på ofte stillede spørgsmål, eller kontakt Privacy Pro-support fra vores hjælpesider."; + +/* Send Feedback Button */ +"subscription.feedback" = "Send tilbagemelding"; /* Cancel action for the existing subscription dialog */ -"subscription.found.cancel" = "Cancel"; +"subscription.found.cancel" = "Annullér"; /* Restore action for the existing subscription dialog */ -"subscription.found.restore" = "Restore"; +"subscription.found.restore" = "Gendan"; /* Message for the existing subscription dialog */ -"subscription.found.text" = "We found a subscription associated with this Apple ID."; +"subscription.found.text" = "Vi fandt et abonnement tilknyttet dette Apple-id."; /* Title for the existing subscription dialog */ -"subscription.found.title" = "Subscription Found"; +"subscription.found.title" = "Abonnement fundet"; /* Help and support Section header */ -"subscription.help" = "Help and support"; +"subscription.help" = "Hjælp og support"; /* Manage Plan header */ -"subscription.manage.plan" = "Manage Plan"; +"subscription.manage.plan" = "Administrer abonnement"; /* Header for the subscription section */ -"subscription.manage.title" = "Subscription"; +"subscription.manage.title" = "Abonnement"; /* Alert content for not found subscription */ -"subscription.notFound.alert.message" = "There is no subscription associated with this Apple ID."; +"subscription.notFound.alert.message" = "Der er ikke noget abonnement tilknyttet dette Apple ID."; /* Alert title for not found subscription */ -"subscription.notFound.alert.title" = "Subscription Not Found"; +"subscription.notFound.alert.title" = "Abonnement ikke fundet"; /* View plans button text */ -"subscription.notFound.view.plans" = "View Plans"; +"subscription.notFound.view.plans" = "Se abonnementer"; /* Hero Text for Personal information removal */ -"subscription.pir.hero" = "Activate Privacy Pro on desktop to set up Personal Information Removal"; +"subscription.pir.hero" = "Aktivér Privacy Pro på en computer for at opsætte Personal Information Removal"; /* Description on how to use Personal information removal in desktop. The first placeholder references a location in the Desktop application. Privacy Pro>, and the second, the menu entry. i.e. */ -"subscription.pir.heroText" = "In the DuckDuckGo browser for desktop, go to %1$@ and click %2$@ to get started."; +"subscription.pir.heroText" = "I DuckDuckGo-browseren til desktop, gå til %1$@ og klik på %2$@ for at komme i gang."; /* Settings references a menu in the Desktop app, Privacy Pro, references our product name */ -"subscription.pir.heroTextLocation" = "Settings > Privacy Pro"; +"subscription.pir.heroTextLocation" = "Indstillinger > Privacy Pro"; /* Menu item for enabling Personal Information Removal on Desktop */ -"subscription.pir.heroTextMenyEntry" = "I have a subscription"; +"subscription.pir.heroTextMenyEntry" = "Jeg har et abonnement"; /* Text for the 'macOS' button */ "subscription.pir.macos" = "Mac"; @@ -2476,28 +2638,28 @@ "subscription.pir.windows" = "Windows"; /* Progress view title when completing the purchase */ -"subscription.progress.view.completing.purchase" = "Completing purchase..."; +"subscription.progress.view.completing.purchase" = "Afslutter køb..."; /* Progress view title when starting the purchase */ -"subscription.progress.view.purchasing.subscription" = "Purchase in progress..."; +"subscription.progress.view.purchasing.subscription" = "Køb er i gang ..."; /* Progress view title when restoring past subscription purchase */ -"subscription.progress.view.restoring.subscription" = "Restoring subscription..."; +"subscription.progress.view.restoring.subscription" = "Gendanner abonnement..."; /* Remove from this device button */ -"subscription.remove.from.device.button" = "Remove From This Device"; +"subscription.remove.from.device.button" = "Fjern fra denne enhed"; /* Remove from device confirmation dialog text */ -"subscription.remove.from.device.text" = "You will no longer be able to access your Privacy Pro subscription on this device. This will not cancel your subscription, and it will remain active on your other devices."; +"subscription.remove.from.device.text" = "Du vil ikke længere kunne få adgang til dit Privacy Pro-abonnement på denne enhed. Dette vil ikke opsige dit abonnement, og det vil forblive aktivt på dine andre enheder."; /* Remove from device confirmation dialog title */ -"subscription.remove.from.device.title" = "Remove from this device?"; +"subscription.remove.from.device.title" = "Fjern fra denne enhed?"; /* Remove subscription button text */ -"subscription.remove.subscription" = "Remove Subscription"; +"subscription.remove.subscription" = "Fjern abonnement"; /* Remove subscription cancel button text */ -"subscription.remove.subscription.cancel" = "Cancel"; +"subscription.remove.subscription.cancel" = "Annullér"; /* Button text for general error message */ "subscription.restore.backend.error.button" = "Tilbage til indstillinger"; @@ -2509,28 +2671,43 @@ "subscription.restore.backend.error.title" = "Noget gik galt"; /* Alert for general error message */ -"subscription.restore.general.error.message" = "The App Store was unable to process your purchase. Please try again later."; +"subscription.restore.general.error.message" = "App Store kunne ikke behandle dit køb. Prøv igen senere."; /* Alert for general error title */ -"subscription.restore.general.error.title" = "Something Went Wrong"; +"subscription.restore.general.error.title" = "Noget gik galt"; /* Alert button text for restored purchase alert */ -"subscription.restore.success.alert.button" = "OK"; +"subscription.restore.success.alert.button" = "Okay"; /* Alert message for restored purchase */ -"subscription.restore.success.alert.message" = "Your purchases have been restored."; +"subscription.restore.success.alert.message" = "Dine køb er blevet gendannet."; /* Alert title for restored purchase */ -"subscription.restore.success.alert.title" = "You’re all set."; +"subscription.restore.success.alert.title" = "Så er du klar."; + +/* Subtitle in header when subscribed */ +"subscription.subscribed" = "Abonneret"; /* Subscription Expired Data. This reads as 'Your subscription expired on (date)' */ "subscription.subscription.expired.caption" = "Dit abonnement udløb den %@"; -/* Subscription expiration info. This reads as 'Your (monthly or annual) subscription expires on (date)' */ -"subscription.subscription.expiring.caption" = "Your %1$@ subscription expires on %2$@."; +/* Monthly subscription expiration info where parameter is expiration date. This reads as 'Your monthly subscription expires on (date)' */ +"subscription.subscription.expiring.monthly.caption" = "Dit månedlige abonnement udløber den %@."; + +/* Unknown period subscription expiration info where parameter is expiration date. This reads as 'Your subscription expires on (date)' */ +"subscription.subscription.expiring.unknown.caption" = "Dit abonnement udløber den %@."; + +/* Annual subscription expiration info where parameter is expiration date. This reads as 'Your annual subscription expires on (date)' */ +"subscription.subscription.expiring.yearly.caption" = "Dit årlige abonnement udløber den %@."; + +/* Monthly subscription renewal info where parameter is renewal date. This reads as 'Your monthly subscription renews on (date)' */ +"subscription.subscription.renewing.monthly.caption" = "Dit månedlige abonnement fornys den %@."; + +/* Unknown period subscription renewal info where parameter is renewal date. This reads as 'Your subscription renews on (date)' */ +"subscription.subscription.renewing.unknown.caption" = "Dit abonnement fornys den %@."; -/* Subscription renewal info. This reads as 'Your (monthly or annual) subscription renews on (date)' */ -"subscription.subscription.renewing.caption" = "Your %1$@ subscription renews on %2$@."; +/* Annual subscription renewal info where parameter is renewal date. This reads as 'Your annual subscription renews on (date)' */ +"subscription.subscription.renewing.yearly.caption" = "Dit årlige abonnement fornys den %@."; /* Navigation bar Title for subscriptions */ "subscription.title" = "Privacy Pro"; @@ -2677,118 +2854,118 @@ "voiceSearch.footer.note.old" = "Lyden behandles på enheden. Den hverken gemmes eller deles med nogen, inklusive DuckDuckGo."; /* Cancel action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.cancel" = "Dismiss"; +"vpn.access-revoked.alert.action.cancel" = "Afvis"; /* Primary action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.subscribe" = "Subscribe"; +"vpn.access-revoked.alert.action.subscribe" = "Abonner"; /* Alert message for the alert when the Privacy Pro subscription expiress */ -"vpn.access-revoked.alert.message" = "Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"vpn.access-revoked.alert.message" = "Abonner på Privacy Pro for at genoprette forbindelsen til DuckDuckGo VPN."; /* Alert title for the alert when the Privacy Pro subscription expires */ -"vpn.access-revoked.alert.title" = "VPN disconnected due to expired subscription"; +"vpn.access-revoked.alert.title" = "VPN er afbrudt på grund af et udløbet abonnement"; /* Title for the VPN widget onboarding screen */ "vpn.addWidget.settings.title" = "Find og vælg DuckDuckGo. Stryg derefter til VPN, og vælg Tilføj widget."; /* Title for the Cancel button of the VPN feedback form */ -"vpn.feedback-form.button.cancel" = "Cancel"; +"vpn.feedback-form.button.cancel" = "Annullér"; /* Title for the Done button of the VPN feedback form */ -"vpn.feedback-form.button.done" = "Done"; +"vpn.feedback-form.button.done" = "Færdig"; /* Title for the Submit button of the VPN feedback form */ -"vpn.feedback-form.button.submit" = "Submit"; +"vpn.feedback-form.button.submit" = "Indsend"; /* Title for the Submitting state of the VPN feedback form */ -"vpn.feedback-form.button.submitting" = "Submitting…"; +"vpn.feedback-form.button.submitting" = "Indsender…"; /* Title for the browser crash/freeze category of the VPN feedback form */ -"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN causes browser to crash or freeze"; +"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN får browseren til at gå ned eller fryse"; /* Title for the 'VPN fails to connect' category of the VPN feedback form */ -"vpn.feedback-form.category.fails-to-connect" = "VPN fails to connect"; +"vpn.feedback-form.category.fails-to-connect" = "VPN kan ikke oprette forbindelse"; /* Title for the 'VPN feature request' category of the VPN feedback form */ -"vpn.feedback-form.category.feature-request" = "VPN feature request"; +"vpn.feedback-form.category.feature-request" = "Anmodning om VPN-funktion"; /* Title for the category 'VPN causes issues with other apps or websites' category of the VPN feedback form */ -"vpn.feedback-form.category.issues-with-apps" = "VPN causes issues with other apps or websites"; +"vpn.feedback-form.category.issues-with-apps" = "VPN forårsager problemer med andre apps eller websteder"; /* Title for the local device connectivity category of the VPN feedback form */ -"vpn.feedback-form.category.local-device-connectivity" = "VPN won't let me connect to local device"; +"vpn.feedback-form.category.local-device-connectivity" = "VPN lader mig ikke oprette forbindelse til en lokal enhed"; /* Title for the 'other VPN feedback' category of the VPN feedback form */ -"vpn.feedback-form.category.other" = "Other VPN feedback"; +"vpn.feedback-form.category.other" = "Anden feedback om VPN"; /* Title for the category selection state of the VPN feedback form */ -"vpn.feedback-form.category.select-category" = "Select a category"; +"vpn.feedback-form.category.select-category" = "VÆLG EN KATEGORI"; /* Title for the 'VPN is too slow' category of the VPN feedback form */ -"vpn.feedback-form.category.too-slow" = "VPN connection is too slow"; +"vpn.feedback-form.category.too-slow" = "VPN-forbindelsen er for langsom"; /* Title for the 'unable to install' category of the VPN feedback form */ -"vpn.feedback-form.category.unable-to-install" = "Unable to install VPN"; +"vpn.feedback-form.category.unable-to-install" = "Kan ikke installere VPN"; /* Title for the feedback sent view description of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.description" = "Your feedback will help us improve the\nDuckDuckGo VPN."; +"vpn.feedback-form.sending-confirmation.description" = "Din feedback vil hjælpe os med at forbedre DuckDuckGo VPN."; /* Title for the feedback sending error text of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.error" = "We couldn't send your feedback right now, please try again."; +"vpn.feedback-form.sending-confirmation.error" = "Vi kunne ikke sende din feedback lige nu. Prøv igen."; /* Title for the feedback sent view title of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.title" = "Thank you!"; +"vpn.feedback-form.sending-confirmation.title" = "Tak!"; /* Toast message when the VPN feedback form is submitted successfully */ -"vpn.feedback-form.submitted.message" = "Thank You! Feedback submitted."; +"vpn.feedback-form.submitted.message" = "Mange tak! Feedback sendt."; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-1" = "Please describe what's happening, what you expected to happen, and the steps that led to the issue:"; +"vpn.feedback-form.text-1" = "Beskriv, hvad der sker, hvad du forventede ville ske, og de trin, der førte til problemet:"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-2" = "In addition to the details entered into this form, your app issue report will contain:"; +"vpn.feedback-form.text-2" = "Ud over de oplysninger, der er indtastet i denne formular, vil din app-problemrapport indeholde:"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-3" = "• Whether specific DuckDuckGo features are enabled"; +"vpn.feedback-form.text-3" = "• Om specifikke DuckDuckGo-funktioner er aktiveret"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-4" = "• Aggregate DuckDuckGo app diagnostics"; +"vpn.feedback-form.text-4" = "• Samlet DuckDuckGo-appdiagnostik"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-5" = "By tapping \"Submit\" I agree that DuckDuckGo may use the information in this report for purposes of improving the app's features."; +"vpn.feedback-form.text-5" = "Ved at trykke på \"Indsend\" accepterer jeg, at DuckDuckGo må bruge oplysningerne i denne rapport til forbedring af appens funktioner."; /* Title for each screen of the VPN feedback form */ -"vpn.feedback-form.title" = "Help Improve the DuckDuckGo VPN"; +"vpn.feedback-form.title" = "Hjælp med at forbedre DuckDuckGo VPN"; /* VPN settings screen cell text for adding the VPN widget to the home screen */ "vpn.settings.add.widget" = "Tilføj VPN-widget til startskærmen"; /* Disclaimer for the DNS Server section on the DNS Server screen */ -"vpn.settings.dns.section-disclaimer" = "Using a custom DNS server can impact browsing speeds and expose your activity to third parties if the server isn't secure or reliable."; +"vpn.settings.dns.section-disclaimer" = "Brug af en brugerdefineret DNS-server kan påvirke browsinghastigheder og afsløre din aktivitet for tredjeparter, hvis serveren ikke er sikker eller pålidelig."; /* Header text for the DNS section on the VPN Settings screen */ "vpn.settings.dns.section-header" = "DNS"; /* Title for the Apply button on the DNS Server setting screen */ -"vpn.settings.dns.server.apply.button.title" = "Apply"; +"vpn.settings.dns.server.apply.button.title" = "Ansøg"; /* Default value for the DNS Server row on the VPN Settings screen */ "vpn.settings.dns.server.default.value" = "DuckDuckGo"; /* Title for the IPv4 Address setting */ -"vpn.settings.dns.server.ipv4.title" = "IPv4 Address"; +"vpn.settings.dns.server.ipv4.title" = "IPv4-adresse"; /* Custom option for the DNS Server setting */ -"vpn.settings.dns.server.option.custom" = "Custom"; +"vpn.settings.dns.server.option.custom" = "Tilpasset"; /* Recommended option for the DNS Server setting */ -"vpn.settings.dns.server.option.default" = "DuckDuckGo (Recommended)"; +"vpn.settings.dns.server.option.default" = "DuckDuckGo (Anbefalet)"; /* Title for the DNS Server setting screen */ -"vpn.settings.dns.server.screen.title" = "DNS Server"; +"vpn.settings.dns.server.screen.title" = "DNS-server"; /* Title for the DNS Server row on the VPN Settings screen */ -"vpn.settings.dns.server.title" = "DNS Server"; +"vpn.settings.dns.server.title" = "DNS-server"; /* Title for the button to enable push notifications in system settings */ "waitlist.allow-notifications" = "Tillad meddelelser"; diff --git a/DuckDuckGo/da.lproj/OmniBar.strings b/DuckDuckGo/da.lproj/OmniBar.strings index ce1860bd24..89f458dff4 100644 --- a/DuckDuckGo/da.lproj/OmniBar.strings +++ b/DuckDuckGo/da.lproj/OmniBar.strings @@ -7,6 +7,9 @@ /* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "Dt8-We-5tV"; */ "Dt8-We-5tV.accessibilityLabel" = "Slet teksten"; +/* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "gb5-wL-4OW"; */ +"gb5-wL-4OW.accessibilityLabel" = "Slet teksten"; + /* Class = "UIButton"; accessibilityLabel = "Share"; ObjectID = "hor-Jd-3kw"; */ "hor-Jd-3kw.accessibilityLabel" = "Del"; diff --git a/DuckDuckGo/da.lproj/Settings.strings b/DuckDuckGo/da.lproj/Settings.strings index f4ed615e79..056400670b 100644 --- a/DuckDuckGo/da.lproj/Settings.strings +++ b/DuckDuckGo/da.lproj/Settings.strings @@ -10,9 +10,6 @@ /* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "5YD-Od-uDU"; */ "5YD-Od-uDU.text" = "Ryd data automatisk"; -/* Class = "UIButton"; normalTitle = "Settings"; ObjectID = "8YG-7Q-Czd"; */ -"8YG-7Q-Czd.normalTitle" = "Indstillinger"; - /* Class = "UILabel"; text = "App Exit, Inactive for 1 Hour"; ObjectID = "9EC-lx-77p"; */ "9EC-lx-77p.text" = "App-afslutning, inaktiv i 1 time"; @@ -76,9 +73,6 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Ubeskyttede websteder"; -/* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ -"ssa-zd-L3T.title" = "Tekststørrelse"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Vis tastaturet på"; diff --git a/DuckDuckGo/de.lproj/DaxOnboarding.strings b/DuckDuckGo/de.lproj/DaxOnboarding.strings index 2d4c0c8010..35cbba71f0 100644 --- a/DuckDuckGo/de.lproj/DaxOnboarding.strings +++ b/DuckDuckGo/de.lproj/DaxOnboarding.strings @@ -1,12 +1,6 @@ /* Class = "UIButton"; normalTitle = "Hide"; ObjectID = "hgy-75-OvJ"; */ "hgy-75-OvJ.normalTitle" = "Ausblenden"; -/* Class = "UILabel"; text = "Welcome to\nDuckDuckGo!"; ObjectID = "Lvy-9G-3gr"; */ -"Lvy-9G-3gr.text" = "Willkommen bei\nDuckDuckGo!"; - -/* Class = "UIButton"; normalTitle = "Let’s Do It!"; ObjectID = "oAv-Zt-uIt"; */ -"oAv-Zt-uIt.normalTitle" = "Los geht's!"; - /* Class = "UIButton"; normalTitle = "Button"; ObjectID = "zJt-F9-L48"; */ "zJt-F9-L48.normalTitle" = "Schaltfläche"; diff --git a/DuckDuckGo/de.lproj/Localizable.strings b/DuckDuckGo/de.lproj/Localizable.strings index 0c992c1ffd..6d6b3fe1b7 100644 --- a/DuckDuckGo/de.lproj/Localizable.strings +++ b/DuckDuckGo/de.lproj/Localizable.strings @@ -341,7 +341,7 @@ "autofill.keep-enabled.alert.disable" = "Deaktivieren"; /* A link that takes the user to the DuckDuckGo help pages explaining password managers */ -"autofill.learn.more.link.title" = "Learn More"; +"autofill.learn.more.link.title" = "Mehr erfahren"; /* Button displayed after saving/updating an autofill login that takes the user to the saved login */ "autofill.login-save-action-button.toast" = "Ansehen"; @@ -977,9 +977,6 @@ /* ad = advertisment */ "dax.onboarding.home.subsequent" = "Gut gemacht!\n\nHinweis: Jedes Mal, wenn du mit mir browst, verliert eine gruselige Werbung ihren Schrecken. 👍"; -/* No comment provided by engineer. */ -"dax.onboarding.message" = "Das Internet kann ein bisschen gruselig sein.\n\nKeine Sorge! Privat zu suchen und zu browsen ist einfacher als du denkst."; - /* No comment provided by engineer. */ "Debug" = "Fehlerbehebung"; @@ -1080,7 +1077,7 @@ "duckPlayer.never.label" = "Nie"; /* Body text for the modal feature explanation */ -"duckplayer.presentation.modal.body" = "Mit dem Duck Player kannst du YouTube ohne gezielte Werbung in einem kinoähnlichen Erlebnis in DuckDuckGo ansehen und was du dir ansiehst, hat keinen Einfluss auf deine Empfehlungen."; +"duckplayer.presentation.modal.body" = "Mit dem Duck Player kannst du YouTube ohne gezielte Werbung in DuckDuckGo ansehen und was du dir ansiehst, hat keinen Einfluss auf deine Empfehlungen."; /* Button that will dismiss the modal */ "duckplayer.presentation.modal.dismiss-button" = "Verstanden."; @@ -1088,17 +1085,23 @@ /* Two line title (separated by \n) for the feature explanation */ "duckplayer.presentation.modal.title" = "Ertrinkst du in Werbung auf YouTube? Teste Duck Player!"; +/* Two line title (separated by \n) for the feature explanation */ +"duckplayer.presentation.modal.title.youtube" = "Ertrinkst du in Werbung auf YouTube? Nicht mit Duck Player!"; + /* Footer label in the settings screen for Duck Player */ "duckplayer.settings.footer" = "DuckDuckGo bietet alle wichtigen Datenschutzfunktionen, die du benötigst, um dich beim Surfen im Internet zu schützen."; /* Text explaining what Duck Player is in the settings screen. */ -"duckplayer.settings.info-text" = "Mit Duck Player kannst du dir ungestört und ohne personalisierte Werbung Inhalte ansehen. Er verhindert, dass das, was du dir ansiehst, deine YouTube-Empfehlungen beeinflussen."; +"duckplayer.settings.info-text" = "Mit dem Duck Player kannst du YouTube ohne gezielte Werbung in DuckDuckGo ansehen und was du dir ansiehst, hat keinen Einfluss auf deine Empfehlungen."; /* Button that takes the user to learn more about Duck Player. */ "duckplayer.settings.learn-more" = "Mehr erfahren"; +/* Settings screen cell text for DuckPlayer settings to open in new tab */ +"duckplayer.settings.open-new-tab-label" = "Duck Player in neuem Tab öffnen"; + /* Settings screen cell text for DuckPlayer settings */ -"duckplayer.settings.open-videos-in" = "Videos in Duck Player öffnen"; +"duckplayer.settings.open-videos-in" = "YouTube-Videos in Duck Player öffnen"; /* Settings screen cell text for DuckPlayer settings */ "duckplayer.settings.title" = "Duck Player"; @@ -1169,9 +1172,6 @@ /* Title for prompt to sign up for email protection */ "email.signup-prompt.title" = "E-Mail-Adresse verbergen und Tracker blockieren"; -/* No comment provided by engineer. */ -"empty!" = "empty!"; - /* Empty list state placholder */ "empty.bookmarks" = "Noch keine Lesezeichen hinzugefügt"; @@ -1322,6 +1322,39 @@ /* Confirmation button */ "feedback.form.submit" = "Abschicken"; +/* Title of the feedback form */ +"feedback.form.title" = "Hilf mit, Privacy Pro zu verbessern"; + +/* Category for Identity Theft Restoration feedback */ +"feedback.general.category.itr" = "Identity Theft Restoration"; + +/* Category for Personal Info Removal feedback */ +"feedback.general.category.pir" = "Entfernen personenbezogener Daten"; + +/* Category for subscription and payments feedback */ +"feedback.general.category.ppro" = "Abonnement und Zahlungen"; + +/* Prompt to select a category for general feedback */ +"feedback.general.category.select" = "EINE KATEGORIE AUSWÄHLEN"; + +/* Category for VPN feedback */ +"feedback.general.category.vpn" = "VPN"; + +/* Category for access code issues */ +"feedback.itr.category.access-code" = "Problem mit dem Zugangscode"; + +/* Category for issues contacting an advisor */ +"feedback.itr.category.cant-contact-advisor" = "Berater kann nicht kontaktiert werden"; + +/* Prompt to select a category for Identity Theft Restoration feedback */ +"feedback.itr.category.select" = "EINE KATEGORIE AUSWÄHLEN"; + +/* Category for other Identity Theft Restoration issues */ +"feedback.itr.category.something-else" = "Etwas anderes"; + +/* Category for unhelpful advisor calls */ +"feedback.itr.category.unhelpful" = "Anruf beim Berater war nicht hilfreich"; + /* No comment provided by engineer. */ "feedback.negative.form.genericPlaceholder" = "Bitte sei so genau wie möglich"; @@ -1361,6 +1394,24 @@ /* No comment provided by engineer. */ "feedback.performance.slowLoading" = "Webseiten oder Suchergebnisse laden langsam"; +/* Category for when scan finds incorrect records */ +"feedback.pir.category.not-me" = "Scan hat Datensätze gefunden, die nicht von mir sind"; + +/* Category for when scan doesn't find info on a specific site */ +"feedback.pir.category.nothing-on-site" = "Der Scan hat meine Informationen auf einer bestimmten Website nicht gefunden"; + +/* Category for other Personal Info Removal issues */ +"feedback.pir.category.other" = "Etwas anderes"; + +/* Category for when the removal process is stuck */ +"feedback.pir.category.removal-stuck" = "Der Entfernungsprozess kommt nicht voran"; + +/* Category for when the scan is stuck */ +"feedback.pir.category.scan-stuck" = "Der Scan für Datensätze wurde nicht abgeschlossen"; + +/* Prompt to select a category for Personal Info Removal feedback */ +"feedback.pir.category.select" = "EINE KATEGORIE AUSWÄHLEN"; + /* Header above input field */ "feedback.positive.form.header" = "Weitere Informationen teilen"; @@ -1379,6 +1430,15 @@ /* Button encouraging uses to share details aboout their feedback */ "feedback.positive.submit" = "Weitere Informationen teilen"; +/* Category for other Privacy Pro issues */ +"feedback.ppro.category.other" = "Etwas anderes"; + +/* Category for one-time password issues */ +"feedback.ppro.category.otp" = "Problem mit dem Einmal-Passwort"; + +/* Prompt to select a category for Privacy Pro feedback */ +"feedback.ppro.category.select" = "EINE KATEGORIE AUSWÄHLEN"; + /* No comment provided by engineer. */ "feedback.start.footer" = "Dein anonymes Feedback ist uns wichtig."; @@ -1529,12 +1589,6 @@ /* No comment provided by engineer. */ "keyCommandShowAllTabs" = "Alle Tabs anzeigen"; -/* Please preserve newline character */ -"launchscreenWelcomeMessage" = "Willkommen bei\nDuckDuckGo!"; - -/* No comment provided by engineer. */ -"LOREM IPSUM" = "LOREM IPSUM"; - /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "DuckDuckGo für Mac hat die Geschwindigkeit, die du brauchst, die Browserfunktionen, die du erwartest, und ist vollgepackt mit unseren grundlegenden Funktionen für den Datenschutz."; @@ -1586,62 +1640,83 @@ /* Edit button */ "navigation.title.edit" = "Bearbeiten"; -/* String indicating NetP is connected when viewed from the settings screen */ -"netP.cell.connected" = "Connected"; - -/* String indicating NetP is disconnected when viewed from the settings screen */ -"netP.cell.disconnected" = "Not connected"; - -/* Title for the DuckDuckGo VPN feature in settings */ -"netP.settings.title" = "VPN"; +/* String indicating NetP is snoozing when viewed from the settings screen */ +"netP.cell.snoozing" = "Pausiert"; /* Title for the DuckDuckGo VPN feature */ "netP.title" = "DuckDuckGo-VPN"; +/* Button title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.action" = "Widget hinzufügen"; + +/* Message for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.message" = "Schalte das VPN direkt vom Startbildschirm ein und aus."; + +/* Title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.title" = "VPN-Widget hinzufügen"; + /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN aufgrund abgelaufenen Abonnements getrennt. Abonniere Privacy Pro, um die Verbindung zum DuckDuckGo-VPN wiederherzustellen."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Network Protection konnte keine Verbindung herstellen. Bitte versuche es zu einem späteren Zeitpunkt erneut."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "DuckDuckGo VPN konnte keine Verbindung herstellen. Bitte versuche es zu einem späteren Zeitpunkt erneut."; /* Title for the VPN FAQ screen. */ "network.protection.faq.title" = "Häufig gestellte Fragen zu DuckDuckGo-VPN"; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Network Protection wurde unterbrochen. Versuche jetzt, die Verbindung wiederherzustellen ..."; +/* Message for tooltip about geoswitching */ +"network.protection.geoswitching.tip.message" = "Du kannst deinen VPN-Standort anpassen, indem du dich mit einem unserer Server weltweit verbindest."; + +/* Title for tooltip about geoswitching */ +"network.protection.geoswitching.tip.title" = "Standort ändern"; + +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "DuckDuckGo VPN wurde unterbrochen. Verbindung wird wiederhergestellt ..."; /* Message for the network protection invite dialog */ -"network.protection.invite.dialog.message" = "Enter your invite code to get started."; +"network.protection.invite.dialog.message" = "Gib deinen Einladungscode ein, um loszulegen."; /* Title for the network protection invite screen */ "network.protection.invite.dialog.title" = "Du bist herzlich eingeladen, DuckDuckGo-VPN auszuprobieren"; /* Prompt for the network protection invite code text field */ -"network.protection.invite.field.prompt" = "Invite Code"; +"network.protection.invite.field.prompt" = "Einladungscode"; /* Message for the network protection invite success view */ -"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; +"network.protection.invite.success.message" = "Verbirg deinen Standort vor Websites und deine Online-Aktivitäten vor Internetanbietern und anderen Personen in deinem Netzwerk."; /* Title for the network protection invite success view */ -"network.protection.invite.success.title" = "Success! You’re in."; +"network.protection.invite.success.title" = "Es hat geklappt! Du bist dabei."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; /* Title text for an iOS quick action that opens VPN settings */ "network.protection.quick-action.open-vpn" = "VPN öffnen"; +/* Button title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.action" = "Mehr erfahren"; + +/* Message for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.message" = "Du kannst Websites oder Apps verwenden, die VPN-Verkehr blockieren, indem du die VPN-Verbindung stumm schaltest."; + +/* Title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.title" = "VPN-Konflikte vermeiden"; + +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN ist für dich für %@ pausiert"; + /* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ "network.protection.status.connected.format" = "Verbunden · %@"; /* The label for the NetP VPN when connecting */ -"network.protection.status.connecting" = "Connecting..."; +"network.protection.status.connecting" = "Verbinden ..."; /* The label for the NetP VPN when disconnected */ -"network.protection.status.disconnected" = "Not connected"; +"network.protection.status.disconnected" = "Nicht verbunden"; /* The label for the NetP VPN when disconnecting */ -"network.protection.status.disconnecting" = "Disconnecting..."; +"network.protection.status.disconnecting" = "Wird getrennt ..."; /* Message label text for the status view when VPN is disconnected */ "network.protection.status.header.message.off" = "Stell eine Verbindung her, um den gesamten Internetverkehr deines Geräts zu sichern."; @@ -1655,26 +1730,41 @@ /* Header title label text for the status view when VPN is connected */ "network.protection.status.header.title.on" = "DuckDuckGo-VPN ist An"; +/* Header title label text for the status view when VPN is snoozing */ +"network.protection.status.header.title.snoozed" = "DuckDuckGo VPN ist pausiert"; + /* The status view 'Share Feedback' button which is shown inline on the status view after the temporary free use footer text */ -"network.protection.status.menu.share.feedback" = "Share Feedback"; +"network.protection.status.menu.share.feedback" = "Feedback teilen"; + +/* The label for the NetP VPN when paused */ +"network.protection.status.paused" = "Pausiert"; + +/* The label for when NetP VPN is snoozing plus the length of time remaining formatted as '0:00' */ +"network.protection.status.snoozing.format" = "Pausiert, %@ verbleibend"; + +/* Snooze button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.snooze" = "Pausieren für 20 Minuten"; + +/* Wake Up button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.wake-up" = "Aufwachen"; /* Connection details label shown in NetworkProtection's status view. */ -"network.protection.status.view.connection.details" = "Connection Details"; +"network.protection.status.view.connection.details" = "Verbindungsdetails"; /* Custom DNS label shown in NetworkProtection's status view. */ -"network.protection.status.view.custom.dns" = "DNS Server"; +"network.protection.status.view.custom.dns" = "DNS-Server"; /* Generic connection failed error message shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.message" = "Please try again later."; +"network.protection.status.view.error.connection.failed.message" = "Bitte versuche es zu einem späteren Zeitpunkt erneut."; /* Generic connection failed error title shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; +"network.protection.status.view.error.connection.failed.title" = "Verbindung konnte nicht hergestellt werden."; /* IP Address label shown in NetworkProtection's status view. */ -"network.protection.status.view.ip.address" = "IP Address"; +"network.protection.status.view.ip.address" = "IP-Adresse"; /* Location label shown in NetworkProtection's status view. */ -"network.protection.status.view.location" = "Location"; +"network.protection.status.view.location" = "Standort"; /* Label shown on the title of the settings section in NetworkProtection's status view. */ "network.protection.status.view.settings.section.title" = "Verwalten"; @@ -1682,12 +1772,15 @@ /* Title label text for the status view when netP is disconnected */ "network.protection.status.view.title" = "VPN"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Network Protection ist aktiviert.Dein Standort und deine Online-Aktivitäten sind geschützt."; +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "DuckDuckGo VPN ist aktiviert. Dein Standort und deine Online-Aktivitäten sind geschützt."; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Geräteverkehr wird über %@ geleitet."; +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "Die VPN-Pause ist vorbei. Geräteverkehr wird über %@ geleitet."; + /* Title for the button to link to the iOS app settings and enable notifications app-wide. */ "network.protection.turn.on.notifications.button.title" = "Benachrichtigungen aktivieren"; @@ -1698,13 +1791,13 @@ "network.protection.vpn.about" = "Über"; /* Section header for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.section.header" = "Notifications"; +"network.protection.vpn.alerts.section.header" = "Benachrichtigungen"; /* List section footer for the toggle for VPN alerts. */ "network.protection.vpn.alerts.toggle.section.footer" = "Lass dich benachrichtigen, wenn deine Verbindung abbricht oder sich der VPN-Status ändert."; /* Title for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.toggle.title" = "VPN Notifications"; +"network.protection.vpn.alerts.toggle.title" = "VPN-Benachrichtigungen"; /* Title for the data volume section in the VPN status screen */ "network.protection.vpn.data-volume" = "Datenvolumen"; @@ -1713,13 +1806,13 @@ "network.protection.vpn.exclude.local.networks.setting.footer" = "Lass den lokalen Datenverkehr das VPN umgehen und dich mit Geräten in deinem lokalen Netzwerk verbinden, beispielsweise einem Drucker."; /* Header text for the Exclude Local Networks setting item. */ -"network.protection.vpn.exclude.local.networks.setting.header" = "General"; +"network.protection.vpn.exclude.local.networks.setting.header" = "Allgemein"; /* Title for the Exclude Local Networks setting item. */ "network.protection.vpn.exclude.local.networks.setting.title" = "Lokale Netzwerke ausschließen"; /* Title for the VPN Location screen's All Countries section. */ -"network.protection.vpn.location.all.countries.section.title" = "All Countries"; +"network.protection.vpn.location.all.countries.section.title" = "Alle Länder"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.connected" = "Verbundener Standort"; @@ -1734,7 +1827,7 @@ "network.protection.vpn.location.recommended.section.footer" = "Verbinde dich automatisch mit dem nächstgelegenen Server, den wir finden können."; /* Title for the VPN Location screen's Recommended section. */ -"network.protection.vpn.location.recommended.section.title" = "Recommended"; +"network.protection.vpn.location.recommended.section.title" = "Empfohlen"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.selected" = "Ausgewählter Standort"; @@ -1752,13 +1845,13 @@ "network.protection.vpn.preferred.location.nearest" = "Nächstgelegener Standort"; /* Title for the Preferred Location VPN Settings item. */ -"network.protection.vpn.preferred.location.title" = "Preferred Location"; +"network.protection.vpn.preferred.location.title" = "Bevorzugter Standort"; /* Footer text for the DNS server setting item. */ -"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo routes DNS queries through our DNS servers so your internet provider can't see what websites you visit."; +"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo leitet DNS-Anfragen über seine eigenen DNS-Server, damit dein Internetanbieter nicht sehen kann, welche Websites du besuchst."; /* Title for the FAQ row in the VPN status screen. */ -"network.protection.vpn.settings.faq" = "FAQs and Support"; +"network.protection.vpn.settings.faq" = "F&A und Support"; /* Title for the feedback row in the VPN status screen. */ "network.protection.vpn.settings.share-feedback" = "VPN-Feedback teilen"; @@ -1940,6 +2033,66 @@ /* Deactivate button */ "pm.deactivate" = "Deaktivieren"; +/* Label for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.label" = "E-Mail (optional)"; + +/* Placeholder for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.placeholder" = "name@email.com"; + +/* Placeholder for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.placeholder" = "Bitte gib uns dein Feedback …"; + +/* Title for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.title" = "Allgemeines Feedback"; + +/* Title for the ITR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-itr-problem.title" = "Identity Theft Restoration"; + +/* Title for the PIR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-pir-problem.title" = "Personal Information Removal"; + +/* Title for the Subscriptions and Payments category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-ppro-problem.title" = "Abonnements und Zahlungen"; + +/* Placeholder for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.placeholder" = "Sag uns, worum es geht …"; + +/* Title for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.title" = "Problem melden"; + +/* Title for the VPN category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-vpn-problem.title" = "VPN"; + +/* Placeholder for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.placeholder" = "Welche Funktion möchtest du sehen?"; + +/* Title for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.title" = "Funktionsanfrage"; + +/* Title for the category selection section in the Privacy Pro feedback form */ +"ppro.feedback-form.select-category.title" = "EINE KATEGORIE AUSWÄHLEN"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-1" = "Hast du ein Problem, das in unserem [Hilfecenter](duck://) nicht behandelt wird? Wir wollen auf jeden Fall davon erfahren.\n\nGib eine E-Mail-Adresse an, wenn du möchtest, dass wir dich zu diesem Problem kontaktieren (wir können möglicherweise nicht auf alle Anfragen antworten):"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-2" = "Zusätzlich zu den oben eingegebenen Daten senden wir einige anonymisierte Informationen mit deinem Feedback:"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-3" = "• Ob einige Browserfunktionen aktiv sind"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-4" = "Aggregierte App-Diagnose (z. B. Fehlercodes)"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-5" = "Indem du auf „Absenden“ tippst, erklärst du dich damit einverstanden, dass DuckDuckGo die übermittelten Informationen zur Verbesserung der App verwenden darf."; + +/* Title for the text box in the Privacy Pro feedback form */ +"ppro.feedback-form.text-box.title" = "Feedback"; + +/* Title for the Privacy Pro feedback form */ +"ppro.feedback-form.title" = "Rückmeldung senden"; + /* Button title for sync bookmarks limits exceeded warning to go to manage bookmarks */ "prefrences.sync.bookmarks-limit-exceeded-action" = "Lesezeichen verwalten"; @@ -2029,6 +2182,18 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Favoriten"; +/* Name of the option the user can chose to give general browser feedback */ +"send.browser.feedback.general-feedback" = "Allgemeines Feedback"; + +/* Name of the option the user can chose to give browser feedback about a problem they enountered */ +"send.browser.feedback.report-problem" = "Problem melden"; + +/* Name of the option the user can chose to give browser feedback about a feature they would like */ +"send.browser.feedback.request-feature" = "Eine Funktion anfordern"; + +/* Title of the picker where the user can chose the category of the feedback they want ot send. */ +"send.browser.feedback.select-category" = "EINE KATEGORIE AUSWÄHLEN"; + /* Settings cell for About DDG */ "settings.about.ddg" = "Über DuckDuckGo"; @@ -2084,6 +2249,9 @@ /* Section footer Autolock description */ "settings.autolock.description" = "Wenn Touch ID, Face ID oder ein Systempasswort aktiviert ist, wirst du aufgefordert, die App beim Öffnen zu entsperren."; +/* Settings cell for Browser Feedback */ +"settings.browser.feedback" = "Browser-Feedback"; + /* Settings screen cell text for Automatically Clearing Data */ "settings.clear.data" = "Daten automatisch löschen"; @@ -2182,7 +2350,7 @@ "settings.ppro" = "Privacy Pro"; /* Title for Link in the Footer of Privacy Pro section */ -"settings.ppro.footer" = "Privacy Policy and Terms of Service"; +"settings.ppro.footer" = "Datenschutzerklärung und Nutzungsbedingungen"; /* Settings screen cell for long press previews */ "settings.previews" = "Vorschau durch langes Tippen"; @@ -2208,23 +2376,20 @@ /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Position deiner Adressleiste festlegen"; -/* Subscription activation pending description */ -"settings.subscription.activation.pending.description" = "This is taking longer than usual, please check back later."; +/* Privacy pro description subtitle in settings when the is activating */ +"settings.subscription.activating" = "Wird aktiviert"; -/* Subscription activation pending title */ -"settings.subscription.activation.pending.title" = "Dein Abonnement wird aktiviert"; +/* Subscription activation pending description */ +"settings.subscription.activation.pending.description" = "Das dauert länger als gewöhnlich, bitte versuche es später noch einmal."; /* Data Broker protection cell subtitle for privacy pro */ -"settings.subscription.DBP.subtitle" = "Remove your info from sites that sell it"; +"settings.subscription.DBP.subtitle" = "Entferne deine Infos von Websites, die sie verkaufen"; /* Data Broker protection cell title for privacy pro */ "settings.subscription.DBP.title" = "Personal Information Removal"; -/* Privacy pro description subtext */ -"settings.subscription.description" = "Nahtloserer Datenschutz mit drei neuen Schutzmaßnahmen:"; - /* I have a Subscription button text for privacy pro */ -"settings.subscription.existing.subscription" = "I Have a Subscription"; +"settings.subscription.existing.subscription" = "Ich habe ein Abonnement"; /* Subscription expired description */ "settings.subscription.expired.comment" = "Abonniere erneut, um Privacy Pro weiter zu nutzen"; @@ -2232,23 +2397,26 @@ /* Subscription expired tittle message */ "settings.subscription.expired.title" = "Dein Privacy Pro Abonnement ist abgelaufen"; -/* Privacy pro features list */ -"settings.subscription.features" = " • VPN\n • Personal Information Removal\n • Identity Theft Restoration"; - /* Identity theft restoration cell subtitle for privacy pro */ -"settings.subscription.ITR.subtitle" = "If your identity is stolen, we'll help restore it"; +"settings.subscription.ITR.subtitle" = "Wenn deine Identität gestohlen wird, helfen wir dir, sie wiederherzustellen."; /* Identity theft restoration cell title for privacy pro */ "settings.subscription.ITR.title" = "Identity Theft Restoration"; /* Get Privacy Pro button text for privacy pro */ -"settings.subscription.learn.more" = "Get Privacy Pro"; +"settings.subscription.learn.more" = "Privacy Pro kaufen"; /* Subscription Settings button text for privacy pro */ -"settings.subscription.manage" = "Subscription Settings"; +"settings.subscription.manage" = "Abonnementeinstellungen"; + +/* Privacy Pro description subtitle in settings */ +"settings.subscription.row.description" = "Dazu gehört unser VPN und Identity Theft Restoration."; -/* Call to action title for Privacy Pro */ -"settings.subscription.subscribe" = "Subscribe to Privacy Pro"; +/* Call to action title for Privacy Pro settings */ +"settings.subscription.subscribe" = "Schütze deine Verbindung und Identität mit Privacy Pro"; + +/* Privacy pro description subtitle in settings */ +"settings.subscription.us.description" = "Dazu gehört unsere VPN, Personal Information Removal und Identity Theft Restoration."; /* VPN cell title for privacy pro */ "settings.subscription.VPN.title" = "VPN"; @@ -2317,67 +2485,64 @@ "siteFeedback.urlPlaceholder" = "Welche Website ist fehlerhaft?"; /* Subscription Activation Info */ -"subscription.activate..header.description" = "Access your Privacy Pro subscription on this device via Apple ID or an email address."; +"subscription.activate..header.description" = "Greife auf diesem Gerät über Apple ID oder eine E-Mail Adresse auf dein Privacy-Pro-Abonnement zu."; /* Button for adding email address to subscription */ -"subscription.activate.add.email.button" = "Add Email"; +"subscription.activate.add.email.button" = "E-Mail-Adresse hinzufügen"; /* Apple ID option for activation */ "subscription.activate.appleid" = "Apple ID"; /* Button text for restoring purchase via Apple ID */ -"subscription.activate.appleid.button" = "Restore Purchase"; +"subscription.activate.appleid.button" = "Kauf wiederherstellen"; /* Description for Apple ID activation */ -"subscription.activate.appleid.description" = "Restore your purchase to activate your subscription on this device."; +"subscription.activate.appleid.description" = "Stelle deinen Kauf wieder her, um dein Abonnement auf diesem Gerät zu aktivieren."; /* Subscription Activation Info */ -"subscription.activate.description" = "Your subscription is automatically available in DuckDuckGo on any device signed in to your Apple ID."; +"subscription.activate.description" = "Dein Abonnement ist automatisch in DuckDuckGo auf jedem Gerät verfügbar, das bei deinem Apple ID-Konto angemeldet ist."; /* Button for editing email address added to subscription */ -"subscription.activate.edit.email.button" = "Edit Email"; +"subscription.activate.edit.email.button" = "E-Mail bearbeiten"; /* View Title for editing your email account */ -"subscription.activate.edit.email.title" = "Edit Email"; +"subscription.activate.edit.email.title" = "E-Mail bearbeiten"; /* Email option for activation */ -"subscription.activate.email" = "Email"; +"subscription.activate.email" = "E-Mail"; /* Restore button title for Email */ -"subscription.activate.email.button" = "Enter Email"; +"subscription.activate.email.button" = "E-Mail-Adresse eingeben"; /* Description for Email activation */ -"subscription.activate.email.description" = "Use your email to activate your subscription on this device."; +"subscription.activate.email.description" = "Verwende deine E-Mail-Adresse, um dein Abonnement auf diesem Gerät zu aktivieren."; /* Activate subscription title */ "subscription.activate.email.title" = "Abonnement aktivieren"; /* Button title for cancelling email deletion */ -"subscription.activate.manage.email.cancel" = "Cancel"; +"subscription.activate.manage.email.cancel" = "Abbrechen"; /* Button title for confirming email deletion */ "subscription.activate.manage.email.OK" = "OK"; /* Restore button title for AppleID */ -"subscription.activate.restore.apple" = "Restore Purchase"; +"subscription.activate.restore.apple" = "Kauf wiederherstellen"; /* Subscription Activation Title */ -"subscription.activate.title" = "Activate your subscription on this device"; +"subscription.activate.title" = "Abonnement auf diesem Gerät aktivieren"; /* Resend activation instructions button */ -"subscription.add.device.resend.instructions" = "Resend Instructions"; +"subscription.add.device.resend.instructions" = "Anweisungen erneut senden"; /* Add email to an existing subscription */ -"subscription.add.email" = "Add an email address to activate your subscription on your other devices. We’ll only use this address to verify your subscription."; +"subscription.add.email" = "Füge eine E-Mail-Adresse hinzu, um dein Abonnement auf deinen anderen Geräten zu aktivieren. Wir verwenden diese Adresse nur, um dein Abonnement zu verifizieren."; /* View title for adding email to subscription */ -"subscription.add.email.title" = "Add Email"; - -/* Title for Alert messages */ -"subscription.alert.title" = "subscription.alert.title"; +"subscription.add.email.title" = "E-Mail-Adresse hinzufügen"; /* Subscription availability message on Apple devices */ -"subscription.available.apple" = "Privacy Pro is available on any device signed in to the same Apple ID."; +"subscription.available.apple" = "Privacy Pro ist auf jedem Gerät verfügbar, das mit derselben Apple-ID angemeldet ist."; /* Text for the manage billing page */ "subscription.billing.google.text" = "Dein Abonnement wurde über den Google Play Store erworben. Um dein Abonnement zu verlängern, öffne bitte die Abonnementeinstellungen im Google Play Store auf einem Gerät, das mit demselben Google-Konto angemeldet ist, mit dem du dein Abonnement ursprünglich gekauft hast."; @@ -2385,89 +2550,86 @@ /* Title for the manage billing page */ "subscription.billing.google.title" = "Abonnementpläne"; -/* Subscription annual billing period type */ -"subscription.billing.period.annual" = "annual"; - -/* Subscription monthly billing period type */ -"subscription.billing.period.monthly" = "monthly"; - /* Subscription Removal confirmation message */ -"subscription.cancel.message" = "Your subscription has been removed from this device."; +"subscription.cancel.message" = "Dein Abonnement wurde von diesem Gerät entfernt."; /* Change plan or cancel title */ -"subscription.change.plan" = "Update Plan or Cancel"; +"subscription.change.plan" = "Tarif aktualisieren oder kündigen"; /* Navigation Button for closing subscription view */ -"subscription.close" = "Close"; +"subscription.close" = "Schließen"; /* Title for Confirm messages */ -"subscription.confirm.title" = "Are you sure?"; +"subscription.confirm.title" = "Bist du sicher?"; /* Header for section for activating subscription on other devices */ -"subscription.devices.header" = "Activate on Other Devices"; +"subscription.devices.header" = "Auf anderen Geräten aktivieren"; /* Footer for section for activating subscription on other devices when email was not yet added */ -"subscription.devices.no.email.footer" = "Add an optional email to your subscription or use your Apple ID to access Privacy Pro on other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.no.email.footer" = "Füge eine optionale E-Mail-Adresse zu deinem Abonnement hinzu, um auf anderen Geräten auf Privacy Pro zuzugreifen. **[Mehr erfahren](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Footer for section for activating subscription on other devices when email is added */ -"subscription.devices.with.email.footer" = "Use this email to activate your subscription in Settings > Privacy Pro in the DuckDuckGo app on your other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.with.email.footer" = "Verwende diese E-Mail-Adresse, um dein Abonnement in der DuckDuckGo-App unter Einstellungen > Privacy Pro auf deinen anderen Geräten zu aktivieren. **[Mehr erfahren](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Alert content for not found subscription */ -"subscription.email.inactive.alert.message" = "The subscription associated with this email is no longer active."; +"subscription.email.inactive.alert.message" = "Das mit dieser E-Mail verbundene Abonnement ist nicht mehr aktiv."; /* Alert title for not found subscription */ -"subscription.email.inactive.alert.title" = "Subscription Not Found"; +"subscription.email.inactive.alert.title" = "Abonnement nicht gefunden"; /* Alert content for not found subscription */ -"subscription.expired.alert.message" = "The subscription associated with this Apple ID is no longer active."; +"subscription.expired.alert.message" = "Das mit dieser Apple ID verknüpfte Abonnement ist nicht mehr aktiv."; /* FAQ Button */ -"subscription.faq" = "FAQs and Support"; +"subscription.faq" = "F&A und Support"; /* FAQ Description */ -"subscription.faq.description" = "Get answers to frequently asked questions or contact Privacy Pro support from our help pages."; +"subscription.faq.description" = "Erhalte Antworten auf häufig gestellte Fragen oder kontaktiere den Privacy-Pro-Support über unsere Hilfeseiten."; + +/* Send Feedback Button */ +"subscription.feedback" = "Rückmeldung senden"; /* Cancel action for the existing subscription dialog */ -"subscription.found.cancel" = "Cancel"; +"subscription.found.cancel" = "Abbrechen"; /* Restore action for the existing subscription dialog */ -"subscription.found.restore" = "Restore"; +"subscription.found.restore" = "Wiederherstellen"; /* Message for the existing subscription dialog */ -"subscription.found.text" = "We found a subscription associated with this Apple ID."; +"subscription.found.text" = "Wir haben ein Abonnement gefunden, das mit dieser Apple ID verknüpft ist."; /* Title for the existing subscription dialog */ -"subscription.found.title" = "Subscription Found"; +"subscription.found.title" = "Abonnement gefunden"; /* Help and support Section header */ -"subscription.help" = "Help and support"; +"subscription.help" = "Hilfe und Support"; /* Manage Plan header */ -"subscription.manage.plan" = "Manage Plan"; +"subscription.manage.plan" = "Tarif verwalten"; /* Header for the subscription section */ -"subscription.manage.title" = "Subscription"; +"subscription.manage.title" = "Abonnement"; /* Alert content for not found subscription */ -"subscription.notFound.alert.message" = "There is no subscription associated with this Apple ID."; +"subscription.notFound.alert.message" = "Mit dieser Apple-ID ist kein Abonnement verknüpft."; /* Alert title for not found subscription */ -"subscription.notFound.alert.title" = "Subscription Not Found"; +"subscription.notFound.alert.title" = "Abonnement nicht gefunden"; /* View plans button text */ -"subscription.notFound.view.plans" = "View Plans"; +"subscription.notFound.view.plans" = "Tarife anzeigen"; /* Hero Text for Personal information removal */ -"subscription.pir.hero" = "Activate Privacy Pro on desktop to set up Personal Information Removal"; +"subscription.pir.hero" = "Aktiviere Privacy Pro auf dem Desktop, um Personal Information Removal einzurichten"; /* Description on how to use Personal information removal in desktop. The first placeholder references a location in the Desktop application. Privacy Pro>, and the second, the menu entry. i.e. */ -"subscription.pir.heroText" = "In the DuckDuckGo browser for desktop, go to %1$@ and click %2$@ to get started."; +"subscription.pir.heroText" = "Gehe im DuckDuckGo-Browser für den Desktop zu %1$@ und klicke auf %2$@, um zu beginnen."; /* Settings references a menu in the Desktop app, Privacy Pro, references our product name */ -"subscription.pir.heroTextLocation" = "Settings > Privacy Pro"; +"subscription.pir.heroTextLocation" = "Einstellungen > Privacy Pro"; /* Menu item for enabling Personal Information Removal on Desktop */ -"subscription.pir.heroTextMenyEntry" = "I have a subscription"; +"subscription.pir.heroTextMenyEntry" = "Ich habe ein Abonnement"; /* Text for the 'macOS' button */ "subscription.pir.macos" = "Mac"; @@ -2476,28 +2638,28 @@ "subscription.pir.windows" = "Windows"; /* Progress view title when completing the purchase */ -"subscription.progress.view.completing.purchase" = "Completing purchase..."; +"subscription.progress.view.completing.purchase" = "Kauf wird abgeschlossen ..."; /* Progress view title when starting the purchase */ -"subscription.progress.view.purchasing.subscription" = "Purchase in progress..."; +"subscription.progress.view.purchasing.subscription" = "Kauf in Bearbeitung ..."; /* Progress view title when restoring past subscription purchase */ -"subscription.progress.view.restoring.subscription" = "Restoring subscription..."; +"subscription.progress.view.restoring.subscription" = "Abonnement wird wiederhergestellt ..."; /* Remove from this device button */ -"subscription.remove.from.device.button" = "Remove From This Device"; +"subscription.remove.from.device.button" = "Von diesem Gerät entfernen"; /* Remove from device confirmation dialog text */ -"subscription.remove.from.device.text" = "You will no longer be able to access your Privacy Pro subscription on this device. This will not cancel your subscription, and it will remain active on your other devices."; +"subscription.remove.from.device.text" = "Dies hat zur Folge, dass du mit diesem Gerät nicht mehr auf dein Privacy-Pro-Abonnement zugreifen kannst. Dein Abonnement wird dadurch nicht gekündigt; es bleibt auf deinen anderen Geräten aktiv."; /* Remove from device confirmation dialog title */ -"subscription.remove.from.device.title" = "Remove from this device?"; +"subscription.remove.from.device.title" = "Von diesem Gerät entfernen?"; /* Remove subscription button text */ -"subscription.remove.subscription" = "Remove Subscription"; +"subscription.remove.subscription" = "Abonnement entfernen"; /* Remove subscription cancel button text */ -"subscription.remove.subscription.cancel" = "Cancel"; +"subscription.remove.subscription.cancel" = "Abbrechen"; /* Button text for general error message */ "subscription.restore.backend.error.button" = "Zurück zu den Einstellungen"; @@ -2509,28 +2671,43 @@ "subscription.restore.backend.error.title" = "Etwas ist schiefgelaufen"; /* Alert for general error message */ -"subscription.restore.general.error.message" = "The App Store was unable to process your purchase. Please try again later."; +"subscription.restore.general.error.message" = "Der App Store konnte deinen Kauf nicht verarbeiten. Bitte versuche es zu einem späteren Zeitpunkt erneut."; /* Alert for general error title */ -"subscription.restore.general.error.title" = "Something Went Wrong"; +"subscription.restore.general.error.title" = "Etwas ist schiefgelaufen"; /* Alert button text for restored purchase alert */ "subscription.restore.success.alert.button" = "OK"; /* Alert message for restored purchase */ -"subscription.restore.success.alert.message" = "Your purchases have been restored."; +"subscription.restore.success.alert.message" = "Deine Käufe wurden wiederhergestellt."; /* Alert title for restored purchase */ -"subscription.restore.success.alert.title" = "You’re all set."; +"subscription.restore.success.alert.title" = "Alles bereit."; + +/* Subtitle in header when subscribed */ +"subscription.subscribed" = "Abonniert"; /* Subscription Expired Data. This reads as 'Your subscription expired on (date)' */ "subscription.subscription.expired.caption" = "Dein Abonnement ist am %@ abgelaufen"; -/* Subscription expiration info. This reads as 'Your (monthly or annual) subscription expires on (date)' */ -"subscription.subscription.expiring.caption" = "Your %1$@ subscription expires on %2$@."; +/* Monthly subscription expiration info where parameter is expiration date. This reads as 'Your monthly subscription expires on (date)' */ +"subscription.subscription.expiring.monthly.caption" = "Dein Monatsabonnement läuft am %@ ab."; + +/* Unknown period subscription expiration info where parameter is expiration date. This reads as 'Your subscription expires on (date)' */ +"subscription.subscription.expiring.unknown.caption" = "Dein Abonnement läuft am %@ ab."; + +/* Annual subscription expiration info where parameter is expiration date. This reads as 'Your annual subscription expires on (date)' */ +"subscription.subscription.expiring.yearly.caption" = "Dein Jahresabonnement läuft am %@ ab."; + +/* Monthly subscription renewal info where parameter is renewal date. This reads as 'Your monthly subscription renews on (date)' */ +"subscription.subscription.renewing.monthly.caption" = "Dein monatliches Abonnement verlängert sich am %@."; + +/* Unknown period subscription renewal info where parameter is renewal date. This reads as 'Your subscription renews on (date)' */ +"subscription.subscription.renewing.unknown.caption" = "Dein Abonnement verlängert sich am %@."; -/* Subscription renewal info. This reads as 'Your (monthly or annual) subscription renews on (date)' */ -"subscription.subscription.renewing.caption" = "Your %1$@ subscription renews on %2$@."; +/* Annual subscription renewal info where parameter is renewal date. This reads as 'Your annual subscription renews on (date)' */ +"subscription.subscription.renewing.yearly.caption" = "Dein Jahresabonnement verlängert sich am %@."; /* Navigation bar Title for subscriptions */ "subscription.title" = "Privacy Pro"; @@ -2677,118 +2854,118 @@ "voiceSearch.footer.note.old" = "Audio wird auf dem Gerät verarbeitet. Es wird weder gespeichert noch an Dritte weitergegeben, auch nicht an DuckDuckGo."; /* Cancel action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.cancel" = "Dismiss"; +"vpn.access-revoked.alert.action.cancel" = "Verwerfen"; /* Primary action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.subscribe" = "Subscribe"; +"vpn.access-revoked.alert.action.subscribe" = "Abonnieren"; /* Alert message for the alert when the Privacy Pro subscription expiress */ -"vpn.access-revoked.alert.message" = "Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"vpn.access-revoked.alert.message" = "Abonniere Privacy Pro, um die Verbindung zum DuckDuckGo-VPN wiederherzustellen."; /* Alert title for the alert when the Privacy Pro subscription expires */ -"vpn.access-revoked.alert.title" = "VPN disconnected due to expired subscription"; +"vpn.access-revoked.alert.title" = "VPN aufgrund abgelaufenen Abonnements getrennt"; /* Title for the VPN widget onboarding screen */ "vpn.addWidget.settings.title" = "Finde DuckDuckGo und wähle es aus. Wische dann zu VPN und wähle „Widget hinzufügen“."; /* Title for the Cancel button of the VPN feedback form */ -"vpn.feedback-form.button.cancel" = "Cancel"; +"vpn.feedback-form.button.cancel" = "Abbrechen"; /* Title for the Done button of the VPN feedback form */ -"vpn.feedback-form.button.done" = "Done"; +"vpn.feedback-form.button.done" = "Fertig"; /* Title for the Submit button of the VPN feedback form */ -"vpn.feedback-form.button.submit" = "Submit"; +"vpn.feedback-form.button.submit" = "Abschicken"; /* Title for the Submitting state of the VPN feedback form */ -"vpn.feedback-form.button.submitting" = "Submitting…"; +"vpn.feedback-form.button.submitting" = "Wird gesendet ..."; /* Title for the browser crash/freeze category of the VPN feedback form */ -"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN causes browser to crash or freeze"; +"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN führt zum Absturz oder Einfrieren des Browsers"; /* Title for the 'VPN fails to connect' category of the VPN feedback form */ -"vpn.feedback-form.category.fails-to-connect" = "VPN fails to connect"; +"vpn.feedback-form.category.fails-to-connect" = "VPN kann keine Verbindung herstellen"; /* Title for the 'VPN feature request' category of the VPN feedback form */ -"vpn.feedback-form.category.feature-request" = "VPN feature request"; +"vpn.feedback-form.category.feature-request" = "Anfrage zu VPN-Funktionen"; /* Title for the category 'VPN causes issues with other apps or websites' category of the VPN feedback form */ -"vpn.feedback-form.category.issues-with-apps" = "VPN causes issues with other apps or websites"; +"vpn.feedback-form.category.issues-with-apps" = "VPN verursacht Probleme mit anderen Apps oder Websites"; /* Title for the local device connectivity category of the VPN feedback form */ -"vpn.feedback-form.category.local-device-connectivity" = "VPN won't let me connect to local device"; +"vpn.feedback-form.category.local-device-connectivity" = "VPN lässt mich keine Verbindung zu lokalem Gerät herstellen"; /* Title for the 'other VPN feedback' category of the VPN feedback form */ -"vpn.feedback-form.category.other" = "Other VPN feedback"; +"vpn.feedback-form.category.other" = "Weiteres VPN-Feedback"; /* Title for the category selection state of the VPN feedback form */ -"vpn.feedback-form.category.select-category" = "Select a category"; +"vpn.feedback-form.category.select-category" = "EINE KATEGORIE AUSWÄHLEN"; /* Title for the 'VPN is too slow' category of the VPN feedback form */ -"vpn.feedback-form.category.too-slow" = "VPN connection is too slow"; +"vpn.feedback-form.category.too-slow" = "VPN-Verbindung ist zu langsam"; /* Title for the 'unable to install' category of the VPN feedback form */ -"vpn.feedback-form.category.unable-to-install" = "Unable to install VPN"; +"vpn.feedback-form.category.unable-to-install" = "VPN lässt sich nicht installieren"; /* Title for the feedback sent view description of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.description" = "Your feedback will help us improve the\nDuckDuckGo VPN."; +"vpn.feedback-form.sending-confirmation.description" = "Dein Feedback hilft uns, das DuckDuckGo-VPN zu verbessern."; /* Title for the feedback sending error text of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.error" = "We couldn't send your feedback right now, please try again."; +"vpn.feedback-form.sending-confirmation.error" = "Dein Feedback konnte nicht gesendet werden. Bitte versuche es erneut."; /* Title for the feedback sent view title of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.title" = "Thank you!"; +"vpn.feedback-form.sending-confirmation.title" = "Vielen Dank!"; /* Toast message when the VPN feedback form is submitted successfully */ -"vpn.feedback-form.submitted.message" = "Thank You! Feedback submitted."; +"vpn.feedback-form.submitted.message" = "Vielen Dank! Feedback wurde gesendet."; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-1" = "Please describe what's happening, what you expected to happen, and the steps that led to the issue:"; +"vpn.feedback-form.text-1" = "Bitte beschreibe, was passiert, was du erwartet hast und welche Schritte zu dem Problem geführt haben:"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-2" = "In addition to the details entered into this form, your app issue report will contain:"; +"vpn.feedback-form.text-2" = "Zusätzlich zu den in diesem Formular angegebenen Details enthält dein App-Fehlerbericht:"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-3" = "• Whether specific DuckDuckGo features are enabled"; +"vpn.feedback-form.text-3" = "• Ob bestimmte DuckDuckGo-Funktionen aktiviert sind"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-4" = "• Aggregate DuckDuckGo app diagnostics"; +"vpn.feedback-form.text-4" = "• Aggregierte DuckDuckGo-App-Diagnose"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-5" = "By tapping \"Submit\" I agree that DuckDuckGo may use the information in this report for purposes of improving the app's features."; +"vpn.feedback-form.text-5" = "Durch Klicken auf „Absenden“ erkläre ich mich damit einverstanden, dass DuckDuckGo die Informationen in diesem Bericht zur Verbesserung der Funktionen der App verwenden darf."; /* Title for each screen of the VPN feedback form */ -"vpn.feedback-form.title" = "Help Improve the DuckDuckGo VPN"; +"vpn.feedback-form.title" = "Hilf mit, das DuckDuckGo-VPN zu verbessern"; /* VPN settings screen cell text for adding the VPN widget to the home screen */ "vpn.settings.add.widget" = "VPN-Widget zum Startbildschirm hinzufügen"; /* Disclaimer for the DNS Server section on the DNS Server screen */ -"vpn.settings.dns.section-disclaimer" = "Using a custom DNS server can impact browsing speeds and expose your activity to third parties if the server isn't secure or reliable."; +"vpn.settings.dns.section-disclaimer" = "Die Verwendung eines benutzerdefinierten DNS-Servers kann die Geschwindigkeit beim Browsen beeinträchtigen und deine Aktivitäten Dritten preisgeben, wenn der Server nicht sicher oder zuverlässig ist."; /* Header text for the DNS section on the VPN Settings screen */ "vpn.settings.dns.section-header" = "DNS"; /* Title for the Apply button on the DNS Server setting screen */ -"vpn.settings.dns.server.apply.button.title" = "Apply"; +"vpn.settings.dns.server.apply.button.title" = "Übernehmen"; /* Default value for the DNS Server row on the VPN Settings screen */ "vpn.settings.dns.server.default.value" = "DuckDuckGo"; /* Title for the IPv4 Address setting */ -"vpn.settings.dns.server.ipv4.title" = "IPv4 Address"; +"vpn.settings.dns.server.ipv4.title" = "IPv4-Adresse"; /* Custom option for the DNS Server setting */ -"vpn.settings.dns.server.option.custom" = "Custom"; +"vpn.settings.dns.server.option.custom" = "Benutzerdefiniert"; /* Recommended option for the DNS Server setting */ -"vpn.settings.dns.server.option.default" = "DuckDuckGo (Recommended)"; +"vpn.settings.dns.server.option.default" = "DuckDuckGo (empfohlen)"; /* Title for the DNS Server setting screen */ -"vpn.settings.dns.server.screen.title" = "DNS Server"; +"vpn.settings.dns.server.screen.title" = "DNS-Server"; /* Title for the DNS Server row on the VPN Settings screen */ -"vpn.settings.dns.server.title" = "DNS Server"; +"vpn.settings.dns.server.title" = "DNS-Server"; /* Title for the button to enable push notifications in system settings */ "waitlist.allow-notifications" = "Benachrichtigungen zulassen"; diff --git a/DuckDuckGo/de.lproj/OmniBar.strings b/DuckDuckGo/de.lproj/OmniBar.strings index 13fd96f2f7..bac2f7a49d 100644 --- a/DuckDuckGo/de.lproj/OmniBar.strings +++ b/DuckDuckGo/de.lproj/OmniBar.strings @@ -7,6 +7,9 @@ /* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "Dt8-We-5tV"; */ "Dt8-We-5tV.accessibilityLabel" = "Text löschen"; +/* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "gb5-wL-4OW"; */ +"gb5-wL-4OW.accessibilityLabel" = "Text löschen"; + /* Class = "UIButton"; accessibilityLabel = "Share"; ObjectID = "hor-Jd-3kw"; */ "hor-Jd-3kw.accessibilityLabel" = "Teilen"; diff --git a/DuckDuckGo/de.lproj/Settings.strings b/DuckDuckGo/de.lproj/Settings.strings index 1601928b75..bade943a6d 100644 --- a/DuckDuckGo/de.lproj/Settings.strings +++ b/DuckDuckGo/de.lproj/Settings.strings @@ -10,9 +10,6 @@ /* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "5YD-Od-uDU"; */ "5YD-Od-uDU.text" = "Daten automatisch löschen"; -/* Class = "UIButton"; normalTitle = "Settings"; ObjectID = "8YG-7Q-Czd"; */ -"8YG-7Q-Czd.normalTitle" = "Einstellungen"; - /* Class = "UILabel"; text = "App Exit, Inactive for 1 Hour"; ObjectID = "9EC-lx-77p"; */ "9EC-lx-77p.text" = "Beim Verlassen der App, 1 Stunde lang inaktiv"; @@ -76,9 +73,6 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Ungeschützte Websites"; -/* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ -"ssa-zd-L3T.title" = "Textgröße"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Tastatur anzeigen bei"; diff --git a/DuckDuckGo/el.lproj/DaxOnboarding.strings b/DuckDuckGo/el.lproj/DaxOnboarding.strings index dda3a4d53f..30f70cd911 100644 --- a/DuckDuckGo/el.lproj/DaxOnboarding.strings +++ b/DuckDuckGo/el.lproj/DaxOnboarding.strings @@ -1,12 +1,6 @@ /* Class = "UIButton"; normalTitle = "Hide"; ObjectID = "hgy-75-OvJ"; */ "hgy-75-OvJ.normalTitle" = "Απόκρυψη"; -/* Class = "UILabel"; text = "Welcome to\nDuckDuckGo!"; ObjectID = "Lvy-9G-3gr"; */ -"Lvy-9G-3gr.text" = "Καλώς ορίσατε στο\nDuckDuckGo!"; - -/* Class = "UIButton"; normalTitle = "Let’s Do It!"; ObjectID = "oAv-Zt-uIt"; */ -"oAv-Zt-uIt.normalTitle" = "Ας το δοκιμάσουμε!"; - /* Class = "UIButton"; normalTitle = "Button"; ObjectID = "zJt-F9-L48"; */ "zJt-F9-L48.normalTitle" = "Κουμπί"; diff --git a/DuckDuckGo/el.lproj/Localizable.strings b/DuckDuckGo/el.lproj/Localizable.strings index 41f550e73d..ab107a549f 100644 --- a/DuckDuckGo/el.lproj/Localizable.strings +++ b/DuckDuckGo/el.lproj/Localizable.strings @@ -341,7 +341,7 @@ "autofill.keep-enabled.alert.disable" = "Απενεργοποίηση"; /* A link that takes the user to the DuckDuckGo help pages explaining password managers */ -"autofill.learn.more.link.title" = "Learn More"; +"autofill.learn.more.link.title" = "Μάθετε περισσότερα"; /* Button displayed after saving/updating an autofill login that takes the user to the saved login */ "autofill.login-save-action-button.toast" = "Προβολή"; @@ -977,9 +977,6 @@ /* ad = advertisment */ "dax.onboarding.home.subsequent" = "Το έχετε!\n\nΝα θυμάστε: κάθε φορά που περιηγείστε μαζί μου μια ανατριχιαστική διαφήμιση χάνει τα φτερά της. 👍"; -/* No comment provided by engineer. */ -"dax.onboarding.message" = "Το Διαδίκτυο μπορεί να είναι κάπως ανατριχιαστικό.\n\nΜην ανησυχείτε! Το να πραγματοποιείτε αναζήτηση και περιήγηση ιδιωτικά είναι πιο εύκολο απ' όσο νομίζετε."; - /* No comment provided by engineer. */ "Debug" = "Επανόρθωση σφαλμάτων"; @@ -1080,7 +1077,7 @@ "duckPlayer.never.label" = "Ποτέ"; /* Body text for the modal feature explanation */ -"duckplayer.presentation.modal.body" = "Το Duck Player σάς επιτρέπει να παρακολουθείτε YouTube χωρίς στοχευμένες διαφημίσεις, χαρίζοντάς σας μια κινηματογραφική εμπειρία στο DuckDuckGo, ενώ το περιεχόμενο που παρακολουθείτε δεν θα επηρεάσει τις συστάσεις που θα λαμβάνετε."; +"duckplayer.presentation.modal.body" = "Το Duck Player σάς επιτρέπει να παρακολουθείτε YouTube χωρίς στοχευμένες διαφημίσεις στο DuckDuckGo, ενώ το περιεχόμενο που παρακολουθείτε δεν θα επηρεάσει τις συστάσεις που θα λαμβάνετε."; /* Button that will dismiss the modal */ "duckplayer.presentation.modal.dismiss-button" = "Το κατάλαβα!"; @@ -1088,17 +1085,23 @@ /* Two line title (separated by \n) for the feature explanation */ "duckplayer.presentation.modal.title" = "Πνίγεστε από τις διαφημίσεις στο YouTube; Δοκιμάστε το Duck Player!"; +/* Two line title (separated by \n) for the feature explanation */ +"duckplayer.presentation.modal.title.youtube" = "Πνίγεστε από τις διαφημίσεις στο YouTube; Όχι με το Duck Player!"; + /* Footer label in the settings screen for Duck Player */ "duckplayer.settings.footer" = "Το DuckDuckGo παρέχει όλα τα Privacy Essentials που χρειάζεστε για να προστατευτείτε καθώς περιηγείστε στον ιστό."; /* Text explaining what Duck Player is in the settings screen. */ -"duckplayer.settings.info-text" = "Το Duck Player παρέχει μια καθαρή εμπειρία προβολής χωρίς εξατομικευμένες διαφημίσεις, ενώ εμποδίζει τη δραστηριότητα προβολής να επηρεάσει τις συστάσεις που θα λαμβάνετε στο YouTube."; +"duckplayer.settings.info-text" = "Το Duck Player σάς επιτρέπει να παρακολουθείτε YouTube χωρίς στοχευμένες διαφημίσεις στο DuckDuckGo, ενώ το περιεχόμενο που παρακολουθείτε δεν θα επηρεάσει τις συστάσεις που θα λαμβάνετε."; /* Button that takes the user to learn more about Duck Player. */ "duckplayer.settings.learn-more" = "Μάθετε περισσότερα"; +/* Settings screen cell text for DuckPlayer settings to open in new tab */ +"duckplayer.settings.open-new-tab-label" = "Ανοίξτε το Duck Player σε νέα καρτέλα"; + /* Settings screen cell text for DuckPlayer settings */ -"duckplayer.settings.open-videos-in" = "Ανοίξτε τα βίντεο στο Duck Player"; +"duckplayer.settings.open-videos-in" = "Ανοίξτε τα βίντεο του YouTube στο Duck Player"; /* Settings screen cell text for DuckPlayer settings */ "duckplayer.settings.title" = "Duck Player"; @@ -1169,9 +1172,6 @@ /* Title for prompt to sign up for email protection */ "email.signup-prompt.title" = "Απόκρυψη του email σας και \n αποκλεισμός εφαρμογών παρακολούθησης"; -/* No comment provided by engineer. */ -"empty!" = "empty!"; - /* Empty list state placholder */ "empty.bookmarks" = "Δεν προστέθηκαν σελιδοδείκτες ακόμα"; @@ -1322,6 +1322,39 @@ /* Confirmation button */ "feedback.form.submit" = "Υποβολή"; +/* Title of the feedback form */ +"feedback.form.title" = "Βοηθήστε να βελτιώσουμε το Privacy Pro"; + +/* Category for Identity Theft Restoration feedback */ +"feedback.general.category.itr" = "Identity Theft Restoration"; + +/* Category for Personal Info Removal feedback */ +"feedback.general.category.pir" = "Αφαίρεση προσωπικών πληροφοριών"; + +/* Category for subscription and payments feedback */ +"feedback.general.category.ppro" = "Συνδρομές και πληρωμές"; + +/* Prompt to select a category for general feedback */ +"feedback.general.category.select" = "ΕΠΙΛΕΞΤΕ ΚΑΤΗΓΟΡΙΑ"; + +/* Category for VPN feedback */ +"feedback.general.category.vpn" = "VPN"; + +/* Category for access code issues */ +"feedback.itr.category.access-code" = "Πρόβλημα με τον κωδικό πρόσβασης"; + +/* Category for issues contacting an advisor */ +"feedback.itr.category.cant-contact-advisor" = "Αδυναμία επικοινωνίας με τον σύμβουλο"; + +/* Prompt to select a category for Identity Theft Restoration feedback */ +"feedback.itr.category.select" = "ΕΠΙΛΕΞΤΕ ΚΑΤΗΓΟΡΙΑ"; + +/* Category for other Identity Theft Restoration issues */ +"feedback.itr.category.something-else" = "Κάτι άλλο"; + +/* Category for unhelpful advisor calls */ +"feedback.itr.category.unhelpful" = "Η κλήση στον Σύμβουλο δεν βοήθησε"; + /* No comment provided by engineer. */ "feedback.negative.form.genericPlaceholder" = "Να είστε όσο το δυνατόν πιο συγκεκριμένοι"; @@ -1361,6 +1394,24 @@ /* No comment provided by engineer. */ "feedback.performance.slowLoading" = "Οι ιστοσελίδες ή τα αποτελέσματα αναζήτησης φορτώνουν αργά"; +/* Category for when scan finds incorrect records */ +"feedback.pir.category.not-me" = "Η σάρωση βρήκε εγγραφές που δεν με αφορούν"; + +/* Category for when scan doesn't find info on a specific site */ +"feedback.pir.category.nothing-on-site" = "Η σάρωση δεν βρήκε τις πληροφορίες μου σε έναν συγκεκριμένο ιστότοπο"; + +/* Category for other Personal Info Removal issues */ +"feedback.pir.category.other" = "Κάτι άλλο"; + +/* Category for when the removal process is stuck */ +"feedback.pir.category.removal-stuck" = "Η διαδικασία αφαίρεσης έχει κολλήσει"; + +/* Category for when the scan is stuck */ +"feedback.pir.category.scan-stuck" = "Η σάρωση για εγγραφές έχει κολλήσει"; + +/* Prompt to select a category for Personal Info Removal feedback */ +"feedback.pir.category.select" = "ΕΠΙΛΕΞΤΕ ΚΑΤΗΓΟΡΙΑ"; + /* Header above input field */ "feedback.positive.form.header" = "Μοιραστείτε τις λεπτομέρειες"; @@ -1379,6 +1430,15 @@ /* Button encouraging uses to share details aboout their feedback */ "feedback.positive.submit" = "Μοιραστείτε τις λεπτομέρειες"; +/* Category for other Privacy Pro issues */ +"feedback.ppro.category.other" = "Κάτι άλλο"; + +/* Category for one-time password issues */ +"feedback.ppro.category.otp" = "Πρόβλημα με τον κωδικό πρόσβασης μίας χρήσης"; + +/* Prompt to select a category for Privacy Pro feedback */ +"feedback.ppro.category.select" = "ΕΠΙΛΕΞΤΕ ΚΑΤΗΓΟΡΙΑ"; + /* No comment provided by engineer. */ "feedback.start.footer" = "Τα ανώνυμα σχόλιά σας είναι σημαντικά για εμάς."; @@ -1529,12 +1589,6 @@ /* No comment provided by engineer. */ "keyCommandShowAllTabs" = "Προβολή όλων των καρτελών"; -/* Please preserve newline character */ -"launchscreenWelcomeMessage" = "Καλώς ορίσατε στο\nDuckDuckGo!"; - -/* No comment provided by engineer. */ -"LOREM IPSUM" = "LOREM IPSUM"; - /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "Το DuckDuckGo για Mac έχει την ταχύτητα που χρειάζεστε, τις λειτουργίες περιήγησης που αναμένετε και περιλαμβάνει τις καλύτερες δυνατότητες απορρήτου στην κατηγορία μας."; @@ -1586,62 +1640,83 @@ /* Edit button */ "navigation.title.edit" = "Επεξεργασία"; -/* String indicating NetP is connected when viewed from the settings screen */ -"netP.cell.connected" = "Connected"; - -/* String indicating NetP is disconnected when viewed from the settings screen */ -"netP.cell.disconnected" = "Not connected"; - -/* Title for the DuckDuckGo VPN feature in settings */ -"netP.settings.title" = "VPN"; +/* String indicating NetP is snoozing when viewed from the settings screen */ +"netP.cell.snoozing" = "Αναβολή"; /* Title for the DuckDuckGo VPN feature */ "netP.title" = "DuckDuckGo VPN"; +/* Button title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.action" = "Προσθήκη γραφικού στοιχείου"; + +/* Message for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.message" = "Ενεργοποιήστε και απενεργοποιήστε το VPN απευθείας από την Αρχική οθόνη."; + +/* Title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.title" = "Προσθήκη μικροεφαρμογής VPN"; + /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "Το VPN αποσυνδέθηκε λόγω λήξης της συνδρομής. Εγγραφείτε στο Privacy Pro για να συνδέσετε εκ νέου το DuckDuckGo VPN."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Η σύνδεση της Προστασίας δικτύου απέτυχε. Ξαναδοκιμάστε αργότερα."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "Η σύνδεση του DuckDuckGo VPN απέτυχε. Ξαναδοκιμάστε αργότερα."; /* Title for the VPN FAQ screen. */ "network.protection.faq.title" = "Συχνές ερωτήσεις DuckDuckGo VPN"; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Η Προστασία δικτύου διακόπηκε. Γίνεται προσπάθεια επανασύνδεσης τώρα..."; +/* Message for tooltip about geoswitching */ +"network.protection.geoswitching.tip.message" = "Μπορείτε να προσαρμόσετε την τοποθεσία VPN σας μέσω σύνδεσης σε οποιονδήποτε από τους διακομιστές μας παγκοσμίως."; + +/* Title for tooltip about geoswitching */ +"network.protection.geoswitching.tip.title" = "Αλλαγή της τοποθεσίας σας"; + +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "Το DuckDuckGo VPN διακόπηκε. Γίνεται προσπάθεια επανασύνδεσης τώρα..."; /* Message for the network protection invite dialog */ -"network.protection.invite.dialog.message" = "Enter your invite code to get started."; +"network.protection.invite.dialog.message" = "Εισαγάγετε τον κωδικό πρόσκλησής σου για να αρχίσετε."; /* Title for the network protection invite screen */ "network.protection.invite.dialog.title" = "Έχετε προσκληθεί να δοκιμάσετε το DuckDuckGo VPN"; /* Prompt for the network protection invite code text field */ -"network.protection.invite.field.prompt" = "Invite Code"; +"network.protection.invite.field.prompt" = "Κωδικός πρόσκλησης"; /* Message for the network protection invite success view */ -"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; +"network.protection.invite.success.message" = "Κρύψτε την τοποθεσία σας από ιστότοπους και αποκρύψτε τη διαδικτυακή δραστηριότητά σας από τους παρόχους Internet και άλλους χρήστες του δικτύου σας."; /* Title for the network protection invite success view */ -"network.protection.invite.success.title" = "Success! You’re in."; +"network.protection.invite.success.title" = "Τέλεια! Τα καταφέρατε."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; /* Title text for an iOS quick action that opens VPN settings */ "network.protection.quick-action.open-vpn" = "Ανοικτό VPN"; +/* Button title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.action" = "Μάθετε περισσότερα"; + +/* Message for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.message" = "Μπορείτε να χρησιμοποιήσετε ιστότοπους ή εφαρμογές που εμποδίζουν την κυκλοφορία VPN, αποκλείοντας τη σύνδεση VPN."; + +/* Title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.title" = "Αποφυγή διενέξεων VPN"; + +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "Το VPN τέθηκε σε αναστολή για %@"; + /* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ "network.protection.status.connected.format" = "Συνδέθηκε · %@"; /* The label for the NetP VPN when connecting */ -"network.protection.status.connecting" = "Connecting..."; +"network.protection.status.connecting" = "Σύνδεση..."; /* The label for the NetP VPN when disconnected */ -"network.protection.status.disconnected" = "Not connected"; +"network.protection.status.disconnected" = "Δεν έχει συνδεθεί"; /* The label for the NetP VPN when disconnecting */ -"network.protection.status.disconnecting" = "Disconnecting..."; +"network.protection.status.disconnecting" = "Αποσύνδεση..."; /* Message label text for the status view when VPN is disconnected */ "network.protection.status.header.message.off" = "Συνδεθείτε για να διασφαλίσετε όλη την επισκεψιμότητά σας στο διαδίκτυο\nαπό τη συσκευή σας."; @@ -1655,26 +1730,41 @@ /* Header title label text for the status view when VPN is connected */ "network.protection.status.header.title.on" = "Το DuckDuckGo VPN είναι ενεργοποιημένο"; +/* Header title label text for the status view when VPN is snoozing */ +"network.protection.status.header.title.snoozed" = "Το DuckDuckGo VPN βρίσκεται σε κατάσταση αναστολής λειτουργίας"; + /* The status view 'Share Feedback' button which is shown inline on the status view after the temporary free use footer text */ -"network.protection.status.menu.share.feedback" = "Share Feedback"; +"network.protection.status.menu.share.feedback" = "Κοινοποίηση σχολίου"; + +/* The label for the NetP VPN when paused */ +"network.protection.status.paused" = "Σε παύση"; + +/* The label for when NetP VPN is snoozing plus the length of time remaining formatted as '0:00' */ +"network.protection.status.snoozing.format" = "Σε αναβολή, απομένει %@"; + +/* Snooze button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.snooze" = "Αναβολή για 20 λεπτά"; + +/* Wake Up button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.wake-up" = "Αφύπνιση"; /* Connection details label shown in NetworkProtection's status view. */ -"network.protection.status.view.connection.details" = "Connection Details"; +"network.protection.status.view.connection.details" = "Λεπτομέρειες σύνδεσης"; /* Custom DNS label shown in NetworkProtection's status view. */ -"network.protection.status.view.custom.dns" = "DNS Server"; +"network.protection.status.view.custom.dns" = "Διακομιστής DNS"; /* Generic connection failed error message shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.message" = "Please try again later."; +"network.protection.status.view.error.connection.failed.message" = "Ξαναδοκιμάστε αργότερα."; /* Generic connection failed error title shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; +"network.protection.status.view.error.connection.failed.title" = "Αποτυχία σύνδεσης."; /* IP Address label shown in NetworkProtection's status view. */ -"network.protection.status.view.ip.address" = "IP Address"; +"network.protection.status.view.ip.address" = "Διεύθυνση IP"; /* Location label shown in NetworkProtection's status view. */ -"network.protection.status.view.location" = "Location"; +"network.protection.status.view.location" = "Τοποθεσία"; /* Label shown on the title of the settings section in NetworkProtection's status view. */ "network.protection.status.view.settings.section.title" = "Διαχείριση"; @@ -1682,12 +1772,15 @@ /* Title label text for the status view when netP is disconnected */ "network.protection.status.view.title" = "VPN"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Η Προστασία δικτύου είναι Ενεργή. Η τοποθεσία και η διαδικτυακή δραστηριότητά σας προστατεύονται."; +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "Το DuckDuckGo VPN είναι ενεργοποιημένο. Η τοποθεσία και η διαδικτυακή δραστηριότητά σας προστατεύονται."; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Δρομολόγηση κυκλοφορίας της συσκευής μέσω %@."; +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "Η αναστολή του VPN έχει τελειώσει. Δρομολόγηση κυκλοφορίας της συσκευής μέσω %@."; + /* Title for the button to link to the iOS app settings and enable notifications app-wide. */ "network.protection.turn.on.notifications.button.title" = "Ενεργοποίηση ειδοποιήσεων"; @@ -1698,13 +1791,13 @@ "network.protection.vpn.about" = "Σχετικά"; /* Section header for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.section.header" = "Notifications"; +"network.protection.vpn.alerts.section.header" = "Ειδοποιήσεις"; /* List section footer for the toggle for VPN alerts. */ "network.protection.vpn.alerts.toggle.section.footer" = "Ειδοποιηθείτε εάν η ισχύς της σύνδεσής σας μειωθεί ή αλλάξει η κατάσταση του VPN."; /* Title for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.toggle.title" = "VPN Notifications"; +"network.protection.vpn.alerts.toggle.title" = "Ειδοποιήσεις VPN"; /* Title for the data volume section in the VPN status screen */ "network.protection.vpn.data-volume" = "Όγκος δεδομένων"; @@ -1713,13 +1806,13 @@ "network.protection.vpn.exclude.local.networks.setting.footer" = "Επιτρέψτε στην τοπική κυκλοφορία να παρακάμψει το VPN και να συνδεθεί σε συσκευές του τοπικού δικτύου σας, όπως ένας εκτυπωτής."; /* Header text for the Exclude Local Networks setting item. */ -"network.protection.vpn.exclude.local.networks.setting.header" = "General"; +"network.protection.vpn.exclude.local.networks.setting.header" = "Γενικά"; /* Title for the Exclude Local Networks setting item. */ "network.protection.vpn.exclude.local.networks.setting.title" = "Εξαίρεση τοπικών δικτύων"; /* Title for the VPN Location screen's All Countries section. */ -"network.protection.vpn.location.all.countries.section.title" = "All Countries"; +"network.protection.vpn.location.all.countries.section.title" = "Όλες οι χώρες"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.connected" = "Συνδεδεμένη τοποθεσία"; @@ -1734,7 +1827,7 @@ "network.protection.vpn.location.recommended.section.footer" = "Αυτόματη σύνδεση στον εγγύτερο διακομιστή που μπορούμε να βρούμε."; /* Title for the VPN Location screen's Recommended section. */ -"network.protection.vpn.location.recommended.section.title" = "Recommended"; +"network.protection.vpn.location.recommended.section.title" = "Προτείνεται"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.selected" = "Επιλεγμένη τοποθεσία"; @@ -1752,13 +1845,13 @@ "network.protection.vpn.preferred.location.nearest" = "Πλησιέστερη τοποθεσία"; /* Title for the Preferred Location VPN Settings item. */ -"network.protection.vpn.preferred.location.title" = "Preferred Location"; +"network.protection.vpn.preferred.location.title" = "Προτιμώμενη τοποθεσία"; /* Footer text for the DNS server setting item. */ -"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo routes DNS queries through our DNS servers so your internet provider can't see what websites you visit."; +"network.protection.vpn.secure.dns.setting.footer" = "Το DuckDuckGo δρομολογεί τα ερωτήματα DNS μέσω των διακομιστών DNS μας, ώστε ο πάροχος διαδικτύου σας να μην μπορεί να δει ποιους ιστότοπους επισκέπτεστε."; /* Title for the FAQ row in the VPN status screen. */ -"network.protection.vpn.settings.faq" = "FAQs and Support"; +"network.protection.vpn.settings.faq" = "Συχνές ερωτήσεις και υποστήριξη"; /* Title for the feedback row in the VPN status screen. */ "network.protection.vpn.settings.share-feedback" = "Κοινοποίηση σχολίου VPN"; @@ -1940,6 +2033,66 @@ /* Deactivate button */ "pm.deactivate" = "Απενεργοποίηση"; +/* Label for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.label" = "Email (προαιρετικό)"; + +/* Placeholder for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.placeholder" = "name@email.com"; + +/* Placeholder for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.placeholder" = "Στείλτε μας τα σχόλιά σας..."; + +/* Title for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.title" = "Γενικά σχόλια"; + +/* Title for the ITR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-itr-problem.title" = "Identity Theft Restoration"; + +/* Title for the PIR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-pir-problem.title" = "Personal Information Removal"; + +/* Title for the Subscriptions and Payments category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-ppro-problem.title" = "Συνδρομές και πληρωμές"; + +/* Placeholder for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.placeholder" = "Πείτε μας τι συμβαίνει..."; + +/* Title for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.title" = "Αναφέρετε ένα πρόβλημα"; + +/* Title for the VPN category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-vpn-problem.title" = "VPN"; + +/* Placeholder for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.placeholder" = "Ποιες λειτουργίες θα θέλατε να δείτε;"; + +/* Title for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.title" = "Αίτημα για λειτουργία"; + +/* Title for the category selection section in the Privacy Pro feedback form */ +"ppro.feedback-form.select-category.title" = "ΕΠΙΛΕΞΤΕ ΚΑΤΗΓΟΡΙΑ"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-1" = "Βρήκατε ένα πρόβλημα που δεν καλύπτεται στο [κέντρο βοήθειας](duck://); Θέλουμε σίγουρα να μάθουμε γι' αυτό.Παρέχετε ένα email αν θέλετε να επικοινωνήσουμε μαζί σας για το θέμα αυτό (ίσως δεν είμαστε σε θέση να απαντήσουμε σε όλα τα θέματα):"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-2" = "Εκτός από τις λεπτομέρειες που καταχωρήθηκαν παραπάνω, στέλνουμε κάποιες ανώνυμες πληροφορίες με τα σχόλιά σας:"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-3" = "• Εάν κάποιες λειτουργίες του προγράμματος περιήγησης είναι ενεργές"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-4" = "• Συγκεντρωτικά διαγνωστικά εφαρμογών (π.χ. κωδικοί σφαλμάτων)"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-5" = "Πατώντας «Υποβολή» συμφωνείτε ότι το DuckDuckGo μπορεί να χρησιμοποιήσει τις πληροφορίες που υποβάλλονται για βελτίωση της εφαρμογής."; + +/* Title for the text box in the Privacy Pro feedback form */ +"ppro.feedback-form.text-box.title" = "Σχόλια"; + +/* Title for the Privacy Pro feedback form */ +"ppro.feedback-form.title" = "Αποστολή σχολίου"; + /* Button title for sync bookmarks limits exceeded warning to go to manage bookmarks */ "prefrences.sync.bookmarks-limit-exceeded-action" = "Διαχείριση σελιδοδεικτών"; @@ -2029,6 +2182,18 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Αγαπημένα"; +/* Name of the option the user can chose to give general browser feedback */ +"send.browser.feedback.general-feedback" = "Γενικά σχόλια"; + +/* Name of the option the user can chose to give browser feedback about a problem they enountered */ +"send.browser.feedback.report-problem" = "Αναφέρετε ένα πρόβλημα"; + +/* Name of the option the user can chose to give browser feedback about a feature they would like */ +"send.browser.feedback.request-feature" = "Αιτηθείτε μια λειτουργία"; + +/* Title of the picker where the user can chose the category of the feedback they want ot send. */ +"send.browser.feedback.select-category" = "ΕΠΙΛΕΞΤΕ ΚΑΤΗΓΟΡΙΑ"; + /* Settings cell for About DDG */ "settings.about.ddg" = "Σχετικά με το DuckDuckGo"; @@ -2084,6 +2249,9 @@ /* Section footer Autolock description */ "settings.autolock.description" = "Εάν έχει ενεργοποιηθεί το Touch ID, το Face ID ή ο κωδικός πρόσβασης συστήματος, θα σας ζητηθεί να ξεκλειδώσετε την εφαρμογή κατά το άνοιγμά της."; +/* Settings cell for Browser Feedback */ +"settings.browser.feedback" = "Σχόλια για το πρόγραμμα περιήγησης"; + /* Settings screen cell text for Automatically Clearing Data */ "settings.clear.data" = "Αυτόματη απαλοιφή δεδομένων"; @@ -2182,7 +2350,7 @@ "settings.ppro" = "Privacy Pro"; /* Title for Link in the Footer of Privacy Pro section */ -"settings.ppro.footer" = "Privacy Policy and Terms of Service"; +"settings.ppro.footer" = "Πολιτική απορρήτου και Όροι χρήσης υπηρεσιών"; /* Settings screen cell for long press previews */ "settings.previews" = "Προεπισκοπήσεις με παρατεταμένο πάτημα"; @@ -2208,23 +2376,20 @@ /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Ορίστε τη θέση της γραμμής διευθύνσεών σας"; -/* Subscription activation pending description */ -"settings.subscription.activation.pending.description" = "This is taking longer than usual, please check back later."; +/* Privacy pro description subtitle in settings when the is activating */ +"settings.subscription.activating" = "Ενεργοποίηση"; -/* Subscription activation pending title */ -"settings.subscription.activation.pending.title" = "Η συνδρομή σας ενεργοποιείται"; +/* Subscription activation pending description */ +"settings.subscription.activation.pending.description" = "Αυτό παίρνει περισσότερο χρόνο από το συνηθισμένο. Ελέγξτε πάλι αργότερα."; /* Data Broker protection cell subtitle for privacy pro */ -"settings.subscription.DBP.subtitle" = "Remove your info from sites that sell it"; +"settings.subscription.DBP.subtitle" = "Αφαιρέστε τις πληροφορίες σας από ιστότοπους που τις πωλούν"; /* Data Broker protection cell title for privacy pro */ "settings.subscription.DBP.title" = "Personal Information Removal"; -/* Privacy pro description subtext */ -"settings.subscription.description" = "Πιο απρόσκοπτη προστασία του απορρήτου με τρεις νέες προστασίες:"; - /* I have a Subscription button text for privacy pro */ -"settings.subscription.existing.subscription" = "I Have a Subscription"; +"settings.subscription.existing.subscription" = "Έχω συνδρομή"; /* Subscription expired description */ "settings.subscription.expired.comment" = "Εγγραφείτε ξανά για να συνεχίσετε να χρησιμοποιείτε το Privacy Pro"; @@ -2232,23 +2397,26 @@ /* Subscription expired tittle message */ "settings.subscription.expired.title" = "Η συνδρομή σας στο Privacy Pro έληξε"; -/* Privacy pro features list */ -"settings.subscription.features" = " • VPN\n • Personal Information Removal\n • Identity Theft Restoration"; - /* Identity theft restoration cell subtitle for privacy pro */ -"settings.subscription.ITR.subtitle" = "If your identity is stolen, we'll help restore it"; +"settings.subscription.ITR.subtitle" = "Αν κλαπεί η ταυτότητά σας, θα σας βοηθήσουμε να την αποκαταστήσετε"; /* Identity theft restoration cell title for privacy pro */ "settings.subscription.ITR.title" = "Identity Theft Restoration"; /* Get Privacy Pro button text for privacy pro */ -"settings.subscription.learn.more" = "Get Privacy Pro"; +"settings.subscription.learn.more" = "Απόκτησε το Privacy Pro"; /* Subscription Settings button text for privacy pro */ -"settings.subscription.manage" = "Subscription Settings"; +"settings.subscription.manage" = "Ρυθμίσεις συνδρομής"; + +/* Privacy Pro description subtitle in settings */ +"settings.subscription.row.description" = "Περιλαμβάνει το VPN μας και τo Identity Theft Restoration."; -/* Call to action title for Privacy Pro */ -"settings.subscription.subscribe" = "Subscribe to Privacy Pro"; +/* Call to action title for Privacy Pro settings */ +"settings.subscription.subscribe" = "Προστατέψτε τη σύνδεση και την ταυτότητά σας με το Privacy Pro"; + +/* Privacy pro description subtitle in settings */ +"settings.subscription.us.description" = "Περιλαμβάνει το VPN μας και τα Personal Information Removal και Identity Theft Restoration."; /* VPN cell title for privacy pro */ "settings.subscription.VPN.title" = "VPN"; @@ -2317,67 +2485,64 @@ "siteFeedback.urlPlaceholder" = "Ποιος ιστότοπος είναι κατεστραμμένος;"; /* Subscription Activation Info */ -"subscription.activate..header.description" = "Access your Privacy Pro subscription on this device via Apple ID or an email address."; +"subscription.activate..header.description" = "Αποκτήστε πρόσβαση στη συνδρομή σας Privacy Pro σε αυτήν τη συσκευή μέσω Apple ID ή διεύθυνσης email."; /* Button for adding email address to subscription */ -"subscription.activate.add.email.button" = "Add Email"; +"subscription.activate.add.email.button" = "Προσθήκη email"; /* Apple ID option for activation */ "subscription.activate.appleid" = "Apple ID"; /* Button text for restoring purchase via Apple ID */ -"subscription.activate.appleid.button" = "Restore Purchase"; +"subscription.activate.appleid.button" = "Επαναφορά αγοράς"; /* Description for Apple ID activation */ -"subscription.activate.appleid.description" = "Restore your purchase to activate your subscription on this device."; +"subscription.activate.appleid.description" = "Κάντε επαναφορά της αγοράς σας για να ενεργοποιήσετε τη συνδρομή σας σε αυτήν τη συσκευή."; /* Subscription Activation Info */ -"subscription.activate.description" = "Your subscription is automatically available in DuckDuckGo on any device signed in to your Apple ID."; +"subscription.activate.description" = "Η συνδρομή σας είναι αυτόματα διαθέσιμη στο DuckDuckGo σε οποιαδήποτε συσκευή είναι συνδεδεμένη στον λογαριασμό σας στο Apple ID."; /* Button for editing email address added to subscription */ -"subscription.activate.edit.email.button" = "Edit Email"; +"subscription.activate.edit.email.button" = "Επεξεργασία email"; /* View Title for editing your email account */ -"subscription.activate.edit.email.title" = "Edit Email"; +"subscription.activate.edit.email.title" = "Επεξεργασία email"; /* Email option for activation */ -"subscription.activate.email" = "Email"; +"subscription.activate.email" = "ηλεκτρονικό ταχυδρομείο"; /* Restore button title for Email */ -"subscription.activate.email.button" = "Enter Email"; +"subscription.activate.email.button" = "Εισαγωγή email"; /* Description for Email activation */ -"subscription.activate.email.description" = "Use your email to activate your subscription on this device."; +"subscription.activate.email.description" = "Χρησιμοποιήστε το email σας για να ενεργοποιήσετε τη συνδρομή σας στη συσκευή αυτή."; /* Activate subscription title */ "subscription.activate.email.title" = "Ενεργοποίηση συνδρομής"; /* Button title for cancelling email deletion */ -"subscription.activate.manage.email.cancel" = "Cancel"; +"subscription.activate.manage.email.cancel" = "Ακύρωση"; /* Button title for confirming email deletion */ -"subscription.activate.manage.email.OK" = "OK"; +"subscription.activate.manage.email.OK" = "Εντάξει"; /* Restore button title for AppleID */ -"subscription.activate.restore.apple" = "Restore Purchase"; +"subscription.activate.restore.apple" = "Επαναφορά αγοράς"; /* Subscription Activation Title */ -"subscription.activate.title" = "Activate your subscription on this device"; +"subscription.activate.title" = "Ενεργοποιήστε τη συνδρομή σας σε αυτήν τη συσκευή"; /* Resend activation instructions button */ -"subscription.add.device.resend.instructions" = "Resend Instructions"; +"subscription.add.device.resend.instructions" = "Εκ νέου αποστολή οδηγιών"; /* Add email to an existing subscription */ -"subscription.add.email" = "Add an email address to activate your subscription on your other devices. We’ll only use this address to verify your subscription."; +"subscription.add.email" = "Πρόσθεσε μια διεύθυνση email για να ενεργοποιήσετε τη συνδρομή σας στις άλλες συσκευές σας. Θα χρησιμοποιήσουμε αυτήν τη διεύθυνση μόνο για να επαληθεύσουμε τη συνδρομή σας."; /* View title for adding email to subscription */ -"subscription.add.email.title" = "Add Email"; - -/* Title for Alert messages */ -"subscription.alert.title" = "subscription.alert.title"; +"subscription.add.email.title" = "Προσθήκη email"; /* Subscription availability message on Apple devices */ -"subscription.available.apple" = "Privacy Pro is available on any device signed in to the same Apple ID."; +"subscription.available.apple" = "Το Privacy Pro είναι διαθέσιμο σε οποιαδήποτε συσκευή που είναι συνδεδεμένη στο ίδιο Apple ID."; /* Text for the manage billing page */ "subscription.billing.google.text" = "Η συνδρομή σας αγοράστηκε μέσω του Google Play Store. Για να ανανεώσετε τη συνδρομή σας, ανοίξτε τις ρυθμίσεις συνδρομής στο Google Play Store σε συσκευή που είναι συνδεδεμένη στον ίδιο λογαριασμό Google που χρησιμοποιήθηκε για την αρχική αγορά της συνδρομής σας."; @@ -2385,89 +2550,86 @@ /* Title for the manage billing page */ "subscription.billing.google.title" = "Προγράμματα συνδρομής"; -/* Subscription annual billing period type */ -"subscription.billing.period.annual" = "annual"; - -/* Subscription monthly billing period type */ -"subscription.billing.period.monthly" = "monthly"; - /* Subscription Removal confirmation message */ -"subscription.cancel.message" = "Your subscription has been removed from this device."; +"subscription.cancel.message" = "Η συνδρομή σας έχει αφαιρεθεί από αυτήν τη συσκευή."; /* Change plan or cancel title */ -"subscription.change.plan" = "Update Plan or Cancel"; +"subscription.change.plan" = "Ενημέρωση προγράμματος ή ακύρωση"; /* Navigation Button for closing subscription view */ -"subscription.close" = "Close"; +"subscription.close" = "Κλείσιμο"; /* Title for Confirm messages */ -"subscription.confirm.title" = "Are you sure?"; +"subscription.confirm.title" = "Είστε σίγουροι;"; /* Header for section for activating subscription on other devices */ -"subscription.devices.header" = "Activate on Other Devices"; +"subscription.devices.header" = "Ενεργοποίηση σε άλλες συσκευές"; /* Footer for section for activating subscription on other devices when email was not yet added */ -"subscription.devices.no.email.footer" = "Add an optional email to your subscription or use your Apple ID to access Privacy Pro on other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.no.email.footer" = "Προσθέστε ένα προαιρετικό email στη συνδρομή σας για να αποκτήσετε πρόσβαση στο Privacy Pro από άλλες συσκευές. **[Μάθετε περισσότερα](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Footer for section for activating subscription on other devices when email is added */ -"subscription.devices.with.email.footer" = "Use this email to activate your subscription in Settings > Privacy Pro in the DuckDuckGo app on your other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.with.email.footer" = "Χρησιμοποιήστε αυτό το email για να ενεργοποιήσετε τη συνδρομή σας στις Ρυθμίσεις > Privacy Pro στην εφαρμογή DuckDuckGo στις άλλες συσκευές σας. **[Μάθε περισσότερα](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Alert content for not found subscription */ -"subscription.email.inactive.alert.message" = "The subscription associated with this email is no longer active."; +"subscription.email.inactive.alert.message" = "Η συνδρομή που συνδέεται με αυτό το email δεν είναι πλέον ενεργή."; /* Alert title for not found subscription */ -"subscription.email.inactive.alert.title" = "Subscription Not Found"; +"subscription.email.inactive.alert.title" = "Η συνδρομή δεν βρέθηκε"; /* Alert content for not found subscription */ -"subscription.expired.alert.message" = "The subscription associated with this Apple ID is no longer active."; +"subscription.expired.alert.message" = "Η συνδρομή που συνδέεται με αυτό το Apple ID δεν είναι πλέον ενεργή."; /* FAQ Button */ -"subscription.faq" = "FAQs and Support"; +"subscription.faq" = "Συχνές ερωτήσεις και υποστήριξη"; /* FAQ Description */ -"subscription.faq.description" = "Get answers to frequently asked questions or contact Privacy Pro support from our help pages."; +"subscription.faq.description" = "Πάρτε απαντήσεις σε συχνές ερωτήσεις ή επικοινωνήστε με την υποστήριξη του Privacy Pro από τις σελίδες βοήθειας που διαθέτουμε."; + +/* Send Feedback Button */ +"subscription.feedback" = "Αποστολή σχολίου"; /* Cancel action for the existing subscription dialog */ -"subscription.found.cancel" = "Cancel"; +"subscription.found.cancel" = "Ακύρωση"; /* Restore action for the existing subscription dialog */ -"subscription.found.restore" = "Restore"; +"subscription.found.restore" = "Επαναφορά"; /* Message for the existing subscription dialog */ -"subscription.found.text" = "We found a subscription associated with this Apple ID."; +"subscription.found.text" = "Βρήκαμε μια συνδρομή που σχετίζεται με αυτό το Apple ID."; /* Title for the existing subscription dialog */ -"subscription.found.title" = "Subscription Found"; +"subscription.found.title" = "Βρέθηκε συνδρομή"; /* Help and support Section header */ -"subscription.help" = "Help and support"; +"subscription.help" = "Βοήθεια και υποστήριξη"; /* Manage Plan header */ -"subscription.manage.plan" = "Manage Plan"; +"subscription.manage.plan" = "Διαχείριση προγράμματος"; /* Header for the subscription section */ -"subscription.manage.title" = "Subscription"; +"subscription.manage.title" = "Συνδρομή"; /* Alert content for not found subscription */ -"subscription.notFound.alert.message" = "There is no subscription associated with this Apple ID."; +"subscription.notFound.alert.message" = "Δεν υπάρχει συνδρομή που να συνδέεται με αυτό το Apple ID."; /* Alert title for not found subscription */ -"subscription.notFound.alert.title" = "Subscription Not Found"; +"subscription.notFound.alert.title" = "Η συνδρομή δεν βρέθηκε"; /* View plans button text */ -"subscription.notFound.view.plans" = "View Plans"; +"subscription.notFound.view.plans" = "Προβολή προγραμμάτων"; /* Hero Text for Personal information removal */ -"subscription.pir.hero" = "Activate Privacy Pro on desktop to set up Personal Information Removal"; +"subscription.pir.hero" = "Ενεργοποιήστε το Privacy Pro στην επιφάνεια εργασίας για να ρυθμίσετε το Personal Information Removal"; /* Description on how to use Personal information removal in desktop. The first placeholder references a location in the Desktop application. Privacy Pro>, and the second, the menu entry. i.e. */ -"subscription.pir.heroText" = "In the DuckDuckGo browser for desktop, go to %1$@ and click %2$@ to get started."; +"subscription.pir.heroText" = "Στο πρόγραμμα περιήγησης DuckDuckGo για επιτραπέζιους υπολογιστές, μεταβείτε στο %1$@ και κάντε κλικ στο %2$@ για να αρχίσετε."; /* Settings references a menu in the Desktop app, Privacy Pro, references our product name */ -"subscription.pir.heroTextLocation" = "Settings > Privacy Pro"; +"subscription.pir.heroTextLocation" = "Ρυθμίσεις > Privacy Pro"; /* Menu item for enabling Personal Information Removal on Desktop */ -"subscription.pir.heroTextMenyEntry" = "I have a subscription"; +"subscription.pir.heroTextMenyEntry" = "Έχω συνδρομή"; /* Text for the 'macOS' button */ "subscription.pir.macos" = "Mac"; @@ -2476,28 +2638,28 @@ "subscription.pir.windows" = "Windows"; /* Progress view title when completing the purchase */ -"subscription.progress.view.completing.purchase" = "Completing purchase..."; +"subscription.progress.view.completing.purchase" = "Ολοκλήρωση της αγοράς..."; /* Progress view title when starting the purchase */ -"subscription.progress.view.purchasing.subscription" = "Purchase in progress..."; +"subscription.progress.view.purchasing.subscription" = "Αγορά σε εξέλιξη..."; /* Progress view title when restoring past subscription purchase */ -"subscription.progress.view.restoring.subscription" = "Restoring subscription..."; +"subscription.progress.view.restoring.subscription" = "Επαναφορά συνδρομής..."; /* Remove from this device button */ -"subscription.remove.from.device.button" = "Remove From This Device"; +"subscription.remove.from.device.button" = "Αφαίρεση από αυτήν τη συσκευή"; /* Remove from device confirmation dialog text */ -"subscription.remove.from.device.text" = "You will no longer be able to access your Privacy Pro subscription on this device. This will not cancel your subscription, and it will remain active on your other devices."; +"subscription.remove.from.device.text" = "Δεν θα μπορείτε πλέον να έχετε πρόσβαση στη συνδρομή Privacy Pro σε αυτήν τη συσκευή. Αυτό δεν θα ακυρώσει τη συνδρομή σας και θα παραμείνει ενεργή στις υπόλοιπες συσκευές σας."; /* Remove from device confirmation dialog title */ -"subscription.remove.from.device.title" = "Remove from this device?"; +"subscription.remove.from.device.title" = "Αφαίρεση από αυτήν τη συσκευή;"; /* Remove subscription button text */ -"subscription.remove.subscription" = "Remove Subscription"; +"subscription.remove.subscription" = "Αφαίρεση συνδρομής"; /* Remove subscription cancel button text */ -"subscription.remove.subscription.cancel" = "Cancel"; +"subscription.remove.subscription.cancel" = "Ακύρωση"; /* Button text for general error message */ "subscription.restore.backend.error.button" = "Επιστροφή στις Ρυθμίσεις"; @@ -2509,28 +2671,43 @@ "subscription.restore.backend.error.title" = "Κάτι πήγε λάθος"; /* Alert for general error message */ -"subscription.restore.general.error.message" = "The App Store was unable to process your purchase. Please try again later."; +"subscription.restore.general.error.message" = "Το App Store δεν μπόρεσε να επεξεργαστεί την αγορά σας. Ξαναδοκιμάστε αργότερα."; /* Alert for general error title */ -"subscription.restore.general.error.title" = "Something Went Wrong"; +"subscription.restore.general.error.title" = "Κάτι πήγε λάθος"; /* Alert button text for restored purchase alert */ -"subscription.restore.success.alert.button" = "OK"; +"subscription.restore.success.alert.button" = "Εντάξει"; /* Alert message for restored purchase */ -"subscription.restore.success.alert.message" = "Your purchases have been restored."; +"subscription.restore.success.alert.message" = "Οι αγορές σας έχουν αποκατασταθεί."; /* Alert title for restored purchase */ -"subscription.restore.success.alert.title" = "You’re all set."; +"subscription.restore.success.alert.title" = "Είστε πανέτοιμοι."; + +/* Subtitle in header when subscribed */ +"subscription.subscribed" = "Έχει γίνει εγγραφή"; /* Subscription Expired Data. This reads as 'Your subscription expired on (date)' */ "subscription.subscription.expired.caption" = "Η συνδρομή σας έληξε %@"; -/* Subscription expiration info. This reads as 'Your (monthly or annual) subscription expires on (date)' */ -"subscription.subscription.expiring.caption" = "Your %1$@ subscription expires on %2$@."; +/* Monthly subscription expiration info where parameter is expiration date. This reads as 'Your monthly subscription expires on (date)' */ +"subscription.subscription.expiring.monthly.caption" = "Η μηνιαία συνδρομή σας λήγει %@."; + +/* Unknown period subscription expiration info where parameter is expiration date. This reads as 'Your subscription expires on (date)' */ +"subscription.subscription.expiring.unknown.caption" = "Η συνδρομή σας λήγει %@."; + +/* Annual subscription expiration info where parameter is expiration date. This reads as 'Your annual subscription expires on (date)' */ +"subscription.subscription.expiring.yearly.caption" = "Η ετήσια συνδρομή σας λήγει %@."; + +/* Monthly subscription renewal info where parameter is renewal date. This reads as 'Your monthly subscription renews on (date)' */ +"subscription.subscription.renewing.monthly.caption" = "Η μηνιαία συνδρομή σας ανανεώνεται %@."; + +/* Unknown period subscription renewal info where parameter is renewal date. This reads as 'Your subscription renews on (date)' */ +"subscription.subscription.renewing.unknown.caption" = "Η συνδρομή σας ανανεώνεται %@."; -/* Subscription renewal info. This reads as 'Your (monthly or annual) subscription renews on (date)' */ -"subscription.subscription.renewing.caption" = "Your %1$@ subscription renews on %2$@."; +/* Annual subscription renewal info where parameter is renewal date. This reads as 'Your annual subscription renews on (date)' */ +"subscription.subscription.renewing.yearly.caption" = "Η ετήσια συνδρομή σας ανανεώνεται %@."; /* Navigation bar Title for subscriptions */ "subscription.title" = "Privacy Pro"; @@ -2677,118 +2854,118 @@ "voiceSearch.footer.note.old" = "Η επεξεργασία του ήχου πραγματοποιείται στη συσκευή. Δεν αποθηκεύεται ούτε κοινοποιείται σε οποιονδήποτε, συμπεριλαμβανομένου του DuckDuckGo."; /* Cancel action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.cancel" = "Dismiss"; +"vpn.access-revoked.alert.action.cancel" = "Απόρριψη"; /* Primary action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.subscribe" = "Subscribe"; +"vpn.access-revoked.alert.action.subscribe" = "Εγγραφείτε"; /* Alert message for the alert when the Privacy Pro subscription expiress */ -"vpn.access-revoked.alert.message" = "Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"vpn.access-revoked.alert.message" = "Εγγραφείτε στο Privacy Pro για να συνδέσετε εκ νέου το DuckDuckGo VPN."; /* Alert title for the alert when the Privacy Pro subscription expires */ -"vpn.access-revoked.alert.title" = "VPN disconnected due to expired subscription"; +"vpn.access-revoked.alert.title" = "Το VPN αποσυνδέθηκε λόγω λήξης της συνδρομής"; /* Title for the VPN widget onboarding screen */ "vpn.addWidget.settings.title" = "Εύρεση και επιλογή του DuckDuckGo. Στη συνέχεια, σύρετε προς το VPN και επιλέξτε Προσθήκη μικροεφαρμογής."; /* Title for the Cancel button of the VPN feedback form */ -"vpn.feedback-form.button.cancel" = "Cancel"; +"vpn.feedback-form.button.cancel" = "Ακύρωση"; /* Title for the Done button of the VPN feedback form */ -"vpn.feedback-form.button.done" = "Done"; +"vpn.feedback-form.button.done" = "Ολοκληρώθηκε"; /* Title for the Submit button of the VPN feedback form */ -"vpn.feedback-form.button.submit" = "Submit"; +"vpn.feedback-form.button.submit" = "Υποβολή"; /* Title for the Submitting state of the VPN feedback form */ -"vpn.feedback-form.button.submitting" = "Submitting…"; +"vpn.feedback-form.button.submitting" = "Υποβολή..."; /* Title for the browser crash/freeze category of the VPN feedback form */ -"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN causes browser to crash or freeze"; +"vpn.feedback-form.category.browser-crash-or-freeze" = "Το VPN προκαλεί σφάλματα ή παγώνει το πρόγραμμα περιήγησης"; /* Title for the 'VPN fails to connect' category of the VPN feedback form */ -"vpn.feedback-form.category.fails-to-connect" = "VPN fails to connect"; +"vpn.feedback-form.category.fails-to-connect" = "Το VPN δεν μπορεί να συνδεθεί"; /* Title for the 'VPN feature request' category of the VPN feedback form */ -"vpn.feedback-form.category.feature-request" = "VPN feature request"; +"vpn.feedback-form.category.feature-request" = "Αίτημα για λειτουργία VPN"; /* Title for the category 'VPN causes issues with other apps or websites' category of the VPN feedback form */ -"vpn.feedback-form.category.issues-with-apps" = "VPN causes issues with other apps or websites"; +"vpn.feedback-form.category.issues-with-apps" = "Το VPN προκαλεί προβλήματα με άλλες εφαρμογές ή ιστότοπους"; /* Title for the local device connectivity category of the VPN feedback form */ -"vpn.feedback-form.category.local-device-connectivity" = "VPN won't let me connect to local device"; +"vpn.feedback-form.category.local-device-connectivity" = "Το VPN δε με αφήνει να συνδεθώ σε τοπική συσκευή"; /* Title for the 'other VPN feedback' category of the VPN feedback form */ -"vpn.feedback-form.category.other" = "Other VPN feedback"; +"vpn.feedback-form.category.other" = "Άλλα σχόλια για το VPN"; /* Title for the category selection state of the VPN feedback form */ -"vpn.feedback-form.category.select-category" = "Select a category"; +"vpn.feedback-form.category.select-category" = "ΕΠΙΛΕΞΤΕ ΚΑΤΗΓΟΡΙΑ"; /* Title for the 'VPN is too slow' category of the VPN feedback form */ -"vpn.feedback-form.category.too-slow" = "VPN connection is too slow"; +"vpn.feedback-form.category.too-slow" = "Η σύνδεση του VPN είναι πολύ αργή"; /* Title for the 'unable to install' category of the VPN feedback form */ -"vpn.feedback-form.category.unable-to-install" = "Unable to install VPN"; +"vpn.feedback-form.category.unable-to-install" = "Δεν είναι δυνατή η εγκατάσταση του VPN"; /* Title for the feedback sent view description of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.description" = "Your feedback will help us improve the\nDuckDuckGo VPN."; +"vpn.feedback-form.sending-confirmation.description" = "Τα σχόλιά σου θα μας βοηθήσουν να βελτιώσουμε το\n DuckDuckGo VPN."; /* Title for the feedback sending error text of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.error" = "We couldn't send your feedback right now, please try again."; +"vpn.feedback-form.sending-confirmation.error" = "Δεν μπορέσαμε να στείλουμε τα σχόλιά σας αυτήν τη στιγμή, προσπάθησε ξανά."; /* Title for the feedback sent view title of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.title" = "Thank you!"; +"vpn.feedback-form.sending-confirmation.title" = "Σε ευχαριστούμε!"; /* Toast message when the VPN feedback form is submitted successfully */ -"vpn.feedback-form.submitted.message" = "Thank You! Feedback submitted."; +"vpn.feedback-form.submitted.message" = "Σας ευχαριστούμε! Το σχόλιο υποβλήθηκε."; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-1" = "Please describe what's happening, what you expected to happen, and the steps that led to the issue:"; +"vpn.feedback-form.text-1" = "Περιγράψτε τι συμβαίνει, τι περιμένατε να συμβεί και ποια ήταν τα βήματα που οδήγησαν στο πρόβλημα:"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-2" = "In addition to the details entered into this form, your app issue report will contain:"; +"vpn.feedback-form.text-2" = "Εκτός από τα στοιχεία που καταχωρήσατε στη φόρμα αυτή, η αναφορά προβλήματος της εφαρμογής σας θα περιέχει:"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-3" = "• Whether specific DuckDuckGo features are enabled"; +"vpn.feedback-form.text-3" = "• Εάν είναι ενεργοποιημένες συγκεκριμένες λειτουργίες του DuckDuckGo"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-4" = "• Aggregate DuckDuckGo app diagnostics"; +"vpn.feedback-form.text-4" = "• Συγκεντρωτικά διαγνωστικά εφαρμογών DuckDuckGo"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-5" = "By tapping \"Submit\" I agree that DuckDuckGo may use the information in this report for purposes of improving the app's features."; +"vpn.feedback-form.text-5" = "Πατώντας «Υποβολή» συμφωνώ ότι το DuckDuckGo μπορεί να χρησιμοποιήσει τις πληροφορίες της αναφοράς αυτής για σκοπούς βελτίωσης των λειτουργιών της εφαρμογής."; /* Title for each screen of the VPN feedback form */ -"vpn.feedback-form.title" = "Help Improve the DuckDuckGo VPN"; +"vpn.feedback-form.title" = "Βοηθήστε μας να βελτιώσουμε το DuckDuckGo VPN"; /* VPN settings screen cell text for adding the VPN widget to the home screen */ "vpn.settings.add.widget" = "Προσθήκη μικροεφαρμογής VPN στην Αρχική οθόνη"; /* Disclaimer for the DNS Server section on the DNS Server screen */ -"vpn.settings.dns.section-disclaimer" = "Using a custom DNS server can impact browsing speeds and expose your activity to third parties if the server isn't secure or reliable."; +"vpn.settings.dns.section-disclaimer" = "Η χρήση ενός προσαρμοσμένου διακομιστή DNS μπορεί να επηρεάσει τις ταχύτητες περιήγησης και να εκθέσει τη δραστηριότητά σας σε τρίτους, αν ο διακομιστής δεν είναι ασφαλής ή αξιόπιστος."; /* Header text for the DNS section on the VPN Settings screen */ "vpn.settings.dns.section-header" = "DNS"; /* Title for the Apply button on the DNS Server setting screen */ -"vpn.settings.dns.server.apply.button.title" = "Apply"; +"vpn.settings.dns.server.apply.button.title" = "Εφαρμογή"; /* Default value for the DNS Server row on the VPN Settings screen */ "vpn.settings.dns.server.default.value" = "DuckDuckGo"; /* Title for the IPv4 Address setting */ -"vpn.settings.dns.server.ipv4.title" = "IPv4 Address"; +"vpn.settings.dns.server.ipv4.title" = "Διεύθυνση IPv4"; /* Custom option for the DNS Server setting */ -"vpn.settings.dns.server.option.custom" = "Custom"; +"vpn.settings.dns.server.option.custom" = "Προσαρμογή"; /* Recommended option for the DNS Server setting */ -"vpn.settings.dns.server.option.default" = "DuckDuckGo (Recommended)"; +"vpn.settings.dns.server.option.default" = "DuckDuckGo (Συνιστάται)"; /* Title for the DNS Server setting screen */ -"vpn.settings.dns.server.screen.title" = "DNS Server"; +"vpn.settings.dns.server.screen.title" = "Διακομιστής DNS"; /* Title for the DNS Server row on the VPN Settings screen */ -"vpn.settings.dns.server.title" = "DNS Server"; +"vpn.settings.dns.server.title" = "Διακομιστής DNS"; /* Title for the button to enable push notifications in system settings */ "waitlist.allow-notifications" = "Επιτρέψτε ειδοποιήσεις"; diff --git a/DuckDuckGo/el.lproj/OmniBar.strings b/DuckDuckGo/el.lproj/OmniBar.strings index df8e02de55..1d0e05dfd4 100644 --- a/DuckDuckGo/el.lproj/OmniBar.strings +++ b/DuckDuckGo/el.lproj/OmniBar.strings @@ -7,6 +7,9 @@ /* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "Dt8-We-5tV"; */ "Dt8-We-5tV.accessibilityLabel" = "Απαλοιφή κειμένου"; +/* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "gb5-wL-4OW"; */ +"gb5-wL-4OW.accessibilityLabel" = "Απαλοιφή κειμένου"; + /* Class = "UIButton"; accessibilityLabel = "Share"; ObjectID = "hor-Jd-3kw"; */ "hor-Jd-3kw.accessibilityLabel" = "Κοινή χρήση"; diff --git a/DuckDuckGo/el.lproj/Settings.strings b/DuckDuckGo/el.lproj/Settings.strings index 6f07da6585..dd68a89826 100644 --- a/DuckDuckGo/el.lproj/Settings.strings +++ b/DuckDuckGo/el.lproj/Settings.strings @@ -10,9 +10,6 @@ /* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "5YD-Od-uDU"; */ "5YD-Od-uDU.text" = "Αυτόματη απαλοιφή δεδομένων"; -/* Class = "UIButton"; normalTitle = "Settings"; ObjectID = "8YG-7Q-Czd"; */ -"8YG-7Q-Czd.normalTitle" = "Ρυθμίσεις"; - /* Class = "UILabel"; text = "App Exit, Inactive for 1 Hour"; ObjectID = "9EC-lx-77p"; */ "9EC-lx-77p.text" = "Έξοδος εφαρμογής, ανενεργή για 1 ώρα"; @@ -76,9 +73,6 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Μη προστατευόμενοι ιστότοποι"; -/* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ -"ssa-zd-L3T.title" = "Μέγεθος κειμένου"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Εμφάνιση πληκτρολογίου όταν"; diff --git a/DuckDuckGo/en.lproj/Localizable.strings b/DuckDuckGo/en.lproj/Localizable.strings index f12ff46fea..d1bf6015ea 100644 --- a/DuckDuckGo/en.lproj/Localizable.strings +++ b/DuckDuckGo/en.lproj/Localizable.strings @@ -25,6 +25,9 @@ /* Add action - button shown in alert */ "action.title.add" = "Add"; +/* Open AI Chat action in the menu list */ +"action.title.aichat" = "Chat"; + /* Autofill Logins menu item opening the login list */ "action.title.autofill.logins" = "Passwords"; @@ -79,9 +82,12 @@ /* Paste and Go action */ "action.title.pasteAndGo" = "Paste & Go"; -/* Print action */ +/* Print action in the menu header */ "action.title.print" = "Print"; +/* Print action in the menu list */ +"action.title.print.site" = "Print"; + /* Refresh action - button shown in alert */ "action.title.refresh" = "Refresh"; @@ -136,6 +142,25 @@ /* No comment provided by engineer. */ "addWidget.title" = "One tap to your favorite sites."; +/* Ai Chat preferences explanation with a markdown link. Do not translate what's inside [] and () */ +"ai-chat.preferences.text.markdown" = "AI Chat is an optional feature available at [duck.ai](ddgquicklink://duck.ai) that lets you have private conversations with popular 3rd-party AI chat models. Your chats are not used to train chat models. +[Learn More](ddgquicklink://duckduckgo.com/duckduckgo-help-pages/aichat/)"; + +/* Toggle text to enable/disable AI Chat in the address bar */ +"aichat.settings.enable.address-bar-toggle" = "Show AI Chat While Searching"; + +/* Toggle text to enable/disable AI Chat in the browsing menu */ +"aichat.settings.enable.browsing-menu-toggle" = "Show AI Chat in Browser Menu"; + +/* Footer text for AI Chat settings */ +"aichat.settings.enable.footer" = "Turning this off will hide the AI Chat feature in the DuckDuckGo app."; + +/* Settings screen cell text for AI Chat settings */ +"aichat.settings.title" = "AI Chat"; + +/* Title for DuckDuckGo AI Chat. Should not be translated */ +"aichat.title" = "DuckDuckGo AI Chat"; + /* No comment provided by engineer. */ "alert.message.bookmarkAll" = "Existing bookmarks will not be duplicated."; @@ -806,15 +831,9 @@ /* Button on the last screen of the onboarding, it will dismiss the onboarding screen. */ "contextual.onboarding.final-screen.button" = "High five!"; -/* Message of the last screen of the onboarding to the browser app. */ -"contextual.onboarding.final-screen.message" = "Remember: every time you browse with me a creepy ad loses its wings. 👌"; - /* Title of the last screen of the onboarding to the browser app */ "contextual.onboarding.final-screen.title" = "You’ve got this!"; -/* After the user performs their first search using the browser, this dialog explains the advantages of using DuckDuckGo */ -"contextual.onboarding.first-search-done.message" = "That’s DuckDuckGo Search. Private. Fast. Fewer ads."; - /* During onboarding steps this button is shown and takes either to the next steps or closes the onboarding. */ "contextual.onboarding.got-it.button" = "Got it!"; @@ -833,9 +852,6 @@ /* Title of a popover on the new tab page browser that invites the user to try a visiting a website */ "contextual.onboarding.ntp.try-a-site.title" = "Try visiting a site!"; -/* Message of a popover on the browser that invites the user to try a search explaining that their searches are anonymous */ -"contextual.onboarding.try-a-search.message" = "Your DuckDuckGo searches are always anonymous."; - /* Title of a popover on the browser that invites the user to try a search */ "contextual.onboarding.try-a-search.title" = "Ready to get started?\nTry a search!"; @@ -860,15 +876,6 @@ /* Search query for the cast of Avatar */ "contextual.onboarding.try-search.option2-international" = "cast of avatar"; -/* Browser Search query for local weather */ -"contextual.onboarding.try-search.option3" = "local weather"; - -/* Browser Search query for chocolate chip cookie recipes */ -"contextual.onboarding.try-search.surprise-me-english" = "chocolate chip cookie recipes"; - -/* Browser Search query for dinner recipes */ -"contextual.onboarding.try-search.surprise-me-international" = "dinner recipes"; - /* Title for a button that triggers an unknown search query for the user. */ "contextual.onboarding.try-search.surprise-me-title" = "Surprise me!"; @@ -1083,7 +1090,7 @@ "duckplayer.settings.learn-more" = "Learn More"; /* Settings screen cell text for DuckPlayer settings to open in new tab */ -"duckplayer.settings.open-new-tab-label" = "Open Duck Player in a new tab"; +"duckplayer.settings.open-new-tab-label" = "Open Duck Player in a New Tab"; /* Settings screen cell text for DuckPlayer settings */ "duckplayer.settings.open-videos-in" = "Open YouTube Videos in Duck Player"; @@ -1631,17 +1638,32 @@ https://duckduckgo.com/mac"; /* Title for the DuckDuckGo VPN feature */ "netP.title" = "DuckDuckGo VPN"; +/* Button title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.action" = "Add widget"; + +/* Message for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.message" = "Turn the VPN on and off right from the Home Screen."; + +/* Title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.title" = "Add VPN Widget"; + /* The body of the notification when Privacy Pro subscription expired */ "network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Network Protection failed to connect. Please try again later."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "DuckDuckGo VPN failed to connect. Please try again later."; /* Title for the VPN FAQ screen. */ "network.protection.faq.title" = "DuckDuckGo VPN FAQ"; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Network Protection was interrupted. Attempting to reconnect now..."; +/* Message for tooltip about geoswitching */ +"network.protection.geoswitching.tip.message" = "You can customize your VPN location by connecting to any of our servers worldwide."; + +/* Title for tooltip about geoswitching */ +"network.protection.geoswitching.tip.title" = "Change Your Location"; + +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "DuckDuckGo VPN was interrupted. Attempting to reconnect now..."; /* Message for the network protection invite dialog */ "network.protection.invite.dialog.message" = "Enter your invite code to get started."; @@ -1658,12 +1680,21 @@ https://duckduckgo.com/mac"; /* Title for the network protection invite success view */ "network.protection.invite.success.title" = "Success! You’re in."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; /* Title text for an iOS quick action that opens VPN settings */ "network.protection.quick-action.open-vpn" = "Open VPN"; +/* Button title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.action" = "Learn more"; + +/* Message for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.message" = "You can use sites or apps that block VPN traffic by snoozing the VPN connection."; + +/* Title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.title" = "Avoid VPN Conflicts"; + /* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ "network.protection.snoozed.notification.body" = "VPN snoozed for %@"; @@ -1733,13 +1764,13 @@ https://duckduckgo.com/mac"; /* Title label text for the status view when netP is disconnected */ "network.protection.status.view.title" = "VPN"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Network Protection is On. Your location and online activity are protected."; +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "DuckDuckGo VPN is On. Your location and online activity are protected."; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Routing device traffic through %@."; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "VPN snooze has ended. Routing device traffic through %@."; /* Title for the button to link to the iOS app settings and enable notifications app-wide. */ @@ -1877,24 +1908,9 @@ https://duckduckgo.com/mac"; /* Button to change the default browser */ "onboarding.browsers.cta" = "Choose Your Browser"; -/* Message to highlight how the browser allows you to block cookie pop-ups */ -"onboarding.browsers.features.cookiePopups.title" = "Block cookie pop-ups"; - -/* Message to highlight browser capability of blocking creepy ads */ -"onboarding.browsers.features.creepyAds.title" = "Block creepy ads"; - -/* Message to highlight browser capability of swiftly erase browsing data */ -"onboarding.browsers.features.eraseBrowsingData.title" = "Swiftly erase browsing data"; - /* Message to highlight browser capability of private searches */ "onboarding.browsers.features.privateSearch.title" = "Search privately by default"; -/* Message to highlight browser capability ofblocking 3rd party trackers */ -"onboarding.browsers.features.trackerBlocker.title" = "Block 3rd-party trackers"; - -/* The title of the dialog to show the privacy features that DuckDuckGo offers */ -"onboarding.browsers.title" = "Privacy protections activated!"; - /* The message of the option to set the address bar to the bottom. */ "onboarding.highlights.addressBarPosition.bottom.message" = "Easy to reach"; @@ -1949,9 +1965,6 @@ https://duckduckgo.com/mac"; /* Button to continue the onboarding process */ "onboarding.intro.cta" = "Let’s do it!"; -/* The title of the onboarding dialog popup */ -"onboarding.intro.title" = "Hi there.\n\nReady for a better, more private internet?"; - /* No comment provided by engineer. */ "onboarding.widgets.continueButton" = "Add Widget"; @@ -2352,9 +2365,6 @@ But if you *do* want a peek under the hood, you can find more information about /* Data Broker protection cell title for privacy pro */ "settings.subscription.DBP.title" = "Personal Information Removal"; -/* Privacy pro description subtitle in settings */ -"settings.subscription.description" = "Includes our VPN, Personal Information Removal, and Identity Theft Restoration."; - /* I have a Subscription button text for privacy pro */ "settings.subscription.existing.subscription" = "I Have a Subscription"; @@ -2382,6 +2392,9 @@ But if you *do* want a peek under the hood, you can find more information about /* Call to action title for Privacy Pro settings */ "settings.subscription.subscribe" = "Protect your connection and identity with Privacy Pro"; +/* Privacy pro description subtitle in settings */ +"settings.subscription.us.description" = "Includes our VPN, Personal Information Removal, and Identity Theft Restoration."; + /* VPN cell title for privacy pro */ "settings.subscription.VPN.title" = "VPN"; @@ -2505,9 +2518,6 @@ But if you *do* want a peek under the hood, you can find more information about /* View title for adding email to subscription */ "subscription.add.email.title" = "Add Email"; -/* Title for Alert messages */ -"subscription.alert.title" = "subscription.alert.title"; - /* Subscription availability message on Apple devices */ "subscription.available.apple" = "Privacy Pro is available on any device signed in to the same Apple ID."; @@ -2517,12 +2527,6 @@ But if you *do* want a peek under the hood, you can find more information about /* Title for the manage billing page */ "subscription.billing.google.title" = "Subscription Plans"; -/* Subscription annual billing period type */ -"subscription.billing.period.annual" = "annual"; - -/* Subscription monthly billing period type */ -"subscription.billing.period.monthly" = "monthly"; - /* Subscription Removal confirmation message */ "subscription.cancel.message" = "Your subscription has been removed from this device."; @@ -2664,11 +2668,23 @@ But if you *do* want a peek under the hood, you can find more information about /* Subscription Expired Data. This reads as 'Your subscription expired on (date)' */ "subscription.subscription.expired.caption" = "Your subscription expired on %@"; -/* Subscription expiration info. This reads as 'Your (monthly or annual) subscription expires on (date)' */ -"subscription.subscription.expiring.caption" = "Your %1$@ subscription expires on %2$@."; +/* Monthly subscription expiration info where parameter is expiration date. This reads as 'Your monthly subscription expires on (date)' */ +"subscription.subscription.expiring.monthly.caption" = "Your monthly subscription expires on %@."; + +/* Unknown period subscription expiration info where parameter is expiration date. This reads as 'Your subscription expires on (date)' */ +"subscription.subscription.expiring.unknown.caption" = "Your subscription expires on %@."; + +/* Annual subscription expiration info where parameter is expiration date. This reads as 'Your annual subscription expires on (date)' */ +"subscription.subscription.expiring.yearly.caption" = "Your annual subscription expires on %@."; + +/* Monthly subscription renewal info where parameter is renewal date. This reads as 'Your monthly subscription renews on (date)' */ +"subscription.subscription.renewing.monthly.caption" = "Your monthly subscription renews on %@."; + +/* Unknown period subscription renewal info where parameter is renewal date. This reads as 'Your subscription renews on (date)' */ +"subscription.subscription.renewing.unknown.caption" = "Your subscription renews on %@."; -/* Subscription renewal info. This reads as 'Your (monthly or annual) subscription renews on (date)' */ -"subscription.subscription.renewing.caption" = "Your %1$@ subscription renews on %2$@."; +/* Annual subscription renewal info where parameter is renewal date. This reads as 'Your annual subscription renews on (date)' */ +"subscription.subscription.renewing.yearly.caption" = "Your annual subscription renews on %@."; /* Navigation bar Title for subscriptions */ "subscription.title" = "Privacy Pro"; diff --git a/DuckDuckGo/es.lproj/DaxOnboarding.strings b/DuckDuckGo/es.lproj/DaxOnboarding.strings index 5a6d88c11b..580a37aa3b 100644 --- a/DuckDuckGo/es.lproj/DaxOnboarding.strings +++ b/DuckDuckGo/es.lproj/DaxOnboarding.strings @@ -1,12 +1,6 @@ /* Class = "UIButton"; normalTitle = "Hide"; ObjectID = "hgy-75-OvJ"; */ "hgy-75-OvJ.normalTitle" = "Ocultar"; -/* Class = "UILabel"; text = "Welcome to\nDuckDuckGo!"; ObjectID = "Lvy-9G-3gr"; */ -"Lvy-9G-3gr.text" = "¡Bienvenido a\nDuckDuckGo!"; - -/* Class = "UIButton"; normalTitle = "Let’s Do It!"; ObjectID = "oAv-Zt-uIt"; */ -"oAv-Zt-uIt.normalTitle" = "¡Adelante!"; - /* Class = "UIButton"; normalTitle = "Button"; ObjectID = "zJt-F9-L48"; */ "zJt-F9-L48.normalTitle" = "Botón"; diff --git a/DuckDuckGo/es.lproj/Localizable.strings b/DuckDuckGo/es.lproj/Localizable.strings index 15968c7091..832c808345 100644 --- a/DuckDuckGo/es.lproj/Localizable.strings +++ b/DuckDuckGo/es.lproj/Localizable.strings @@ -341,7 +341,7 @@ "autofill.keep-enabled.alert.disable" = "Deshabilitar"; /* A link that takes the user to the DuckDuckGo help pages explaining password managers */ -"autofill.learn.more.link.title" = "Learn More"; +"autofill.learn.more.link.title" = "Más información"; /* Button displayed after saving/updating an autofill login that takes the user to the saved login */ "autofill.login-save-action-button.toast" = "Ver"; @@ -977,9 +977,6 @@ /* ad = advertisment */ "dax.onboarding.home.subsequent" = "¡Lo estás haciendo muy bien!\n\nRecuerda: cada vez que navegas conmigo corto las alas a un anuncio horrible. 👍"; -/* No comment provided by engineer. */ -"dax.onboarding.message" = "Internet puede ser un lugar horrible.\n\nNo te preocupes. Buscar y navegar de forma privada es más fácil de lo que piensas."; - /* No comment provided by engineer. */ "Debug" = "Depurar"; @@ -1080,7 +1077,7 @@ "duckPlayer.never.label" = "Nunca"; /* Body text for the modal feature explanation */ -"duckplayer.presentation.modal.body" = "Duck Player te permite ver YouTube sin anuncios segmentados como si estuvieras en un cine en DuckDuckGo y lo que veas no influirá en tus recomendaciones."; +"duckplayer.presentation.modal.body" = "Duck Player te permite ver YouTube sin anuncios segmentados en DuckDuckGo y lo que veas no influirá en tus recomendaciones."; /* Button that will dismiss the modal */ "duckplayer.presentation.modal.dismiss-button" = "Entendido"; @@ -1088,17 +1085,23 @@ /* Two line title (separated by \n) for the feature explanation */ "duckplayer.presentation.modal.title" = "¿Te ahogas en anuncios en YouTube? ¡Prueba Duck Player!"; +/* Two line title (separated by \n) for the feature explanation */ +"duckplayer.presentation.modal.title.youtube" = "¿Te ahogas en anuncios en YouTube? ¡No con Duck Player!"; + /* Footer label in the settings screen for Duck Player */ "duckplayer.settings.footer" = "DuckDuckGo te ofrece todos los elementos esenciales de privacidad que necesitas para protegerte mientras navegas por la web."; /* Text explaining what Duck Player is in the settings screen. */ -"duckplayer.settings.info-text" = "Duck Player ofrece una experiencia de visualización limpia sin anuncios personalizados e impide que la actividad de visualización influya en tus recomendaciones de YouTube."; +"duckplayer.settings.info-text" = "Duck Player te permite ver YouTube sin anuncios segmentados en DuckDuckGo y lo que veas no influirá en tus recomendaciones."; /* Button that takes the user to learn more about Duck Player. */ "duckplayer.settings.learn-more" = "Más información"; +/* Settings screen cell text for DuckPlayer settings to open in new tab */ +"duckplayer.settings.open-new-tab-label" = "Abrir Duck Player en una nueva pestaña"; + /* Settings screen cell text for DuckPlayer settings */ -"duckplayer.settings.open-videos-in" = "Abre vídeos en Duck Player"; +"duckplayer.settings.open-videos-in" = "Abrir vídeos de YouTube en Duck Player"; /* Settings screen cell text for DuckPlayer settings */ "duckplayer.settings.title" = "Duck Player"; @@ -1169,9 +1172,6 @@ /* Title for prompt to sign up for email protection */ "email.signup-prompt.title" = "Ocultar tu correo electrónico y\nbloquear rastreadores"; -/* No comment provided by engineer. */ -"empty!" = "¡vacío!"; - /* Empty list state placholder */ "empty.bookmarks" = "Aún no se han añadido marcadores"; @@ -1322,6 +1322,39 @@ /* Confirmation button */ "feedback.form.submit" = "Enviar"; +/* Title of the feedback form */ +"feedback.form.title" = "Ayudar a mejorar Privacy Pro"; + +/* Category for Identity Theft Restoration feedback */ +"feedback.general.category.itr" = "Identity Theft Restoration"; + +/* Category for Personal Info Removal feedback */ +"feedback.general.category.pir" = "Eliminación de información personal"; + +/* Category for subscription and payments feedback */ +"feedback.general.category.ppro" = "Suscripción y pagos"; + +/* Prompt to select a category for general feedback */ +"feedback.general.category.select" = "SELECCIONA UNA CATEGORÍA"; + +/* Category for VPN feedback */ +"feedback.general.category.vpn" = "VPN"; + +/* Category for access code issues */ +"feedback.itr.category.access-code" = "Incidencia con código de acceso"; + +/* Category for issues contacting an advisor */ +"feedback.itr.category.cant-contact-advisor" = "No se puede contactar con el asesor"; + +/* Prompt to select a category for Identity Theft Restoration feedback */ +"feedback.itr.category.select" = "SELECCIONA UNA CATEGORÍA"; + +/* Category for other Identity Theft Restoration issues */ +"feedback.itr.category.something-else" = "Otro problema"; + +/* Category for unhelpful advisor calls */ +"feedback.itr.category.unhelpful" = "Llamar al asesor no ha ayudado"; + /* No comment provided by engineer. */ "feedback.negative.form.genericPlaceholder" = "Concreta lo máximo posible"; @@ -1361,6 +1394,24 @@ /* No comment provided by engineer. */ "feedback.performance.slowLoading" = "Los resultados de búsqueda o las páginas web tardan en cargarse"; +/* Category for when scan finds incorrect records */ +"feedback.pir.category.not-me" = "El escaneo ha encontrado registros que no son míos"; + +/* Category for when scan doesn't find info on a specific site */ +"feedback.pir.category.nothing-on-site" = "El escaneo no ha encontrado mi información en un sitio específico"; + +/* Category for other Personal Info Removal issues */ +"feedback.pir.category.other" = "Otro problema"; + +/* Category for when the removal process is stuck */ +"feedback.pir.category.removal-stuck" = "El proceso de eliminación está atascado"; + +/* Category for when the scan is stuck */ +"feedback.pir.category.scan-stuck" = "El escaneo de registros está atascado"; + +/* Prompt to select a category for Personal Info Removal feedback */ +"feedback.pir.category.select" = "SELECCIONA UNA CATEGORÍA"; + /* Header above input field */ "feedback.positive.form.header" = "Compartir detalles"; @@ -1379,6 +1430,15 @@ /* Button encouraging uses to share details aboout their feedback */ "feedback.positive.submit" = "Compartir detalles"; +/* Category for other Privacy Pro issues */ +"feedback.ppro.category.other" = "Otro problema"; + +/* Category for one-time password issues */ +"feedback.ppro.category.otp" = "Problema con la contraseña de un solo uso"; + +/* Prompt to select a category for Privacy Pro feedback */ +"feedback.ppro.category.select" = "SELECCIONA UNA CATEGORÍA"; + /* No comment provided by engineer. */ "feedback.start.footer" = "Tu opinión anónima nos importa."; @@ -1529,12 +1589,6 @@ /* No comment provided by engineer. */ "keyCommandShowAllTabs" = "Mostrar todas las pestañas"; -/* Please preserve newline character */ -"launchscreenWelcomeMessage" = "¡Bienvenido a\nDuckDuckGo!"; - -/* No comment provided by engineer. */ -"LOREM IPSUM" = "LOREM IPSUM"; - /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "DuckDuckGo para Mac tiene la velocidad que necesitas, las funciones de navegación que esperas y viene repleta de nuestros mejores Privacy Essentials."; @@ -1586,62 +1640,83 @@ /* Edit button */ "navigation.title.edit" = "Editar"; -/* String indicating NetP is connected when viewed from the settings screen */ -"netP.cell.connected" = "Connected"; - -/* String indicating NetP is disconnected when viewed from the settings screen */ -"netP.cell.disconnected" = "Not connected"; - -/* Title for the DuckDuckGo VPN feature in settings */ -"netP.settings.title" = "VPN"; +/* String indicating NetP is snoozing when viewed from the settings screen */ +"netP.cell.snoozing" = "En suspenso"; /* Title for the DuckDuckGo VPN feature */ "netP.title" = "DuckDuckGo VPN"; +/* Button title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.action" = "Añadir widget"; + +/* Message for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.message" = "Activa y desactiva la VPN directamente desde la pantalla de inicio."; + +/* Title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.title" = "Añadir widget de VPN"; + /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN desconectada por suscripción caducada. Suscríbete a Privacy Pro para volver a conectar la VPN de DuckDuckGo."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "No se ha podido conectar la protección de red. Inténtalo de nuevo más tarde."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "No se ha podido conectar la VPN de DuckDuckGo. Inténtalo de nuevo más tarde."; /* Title for the VPN FAQ screen. */ "network.protection.faq.title" = "Preguntas frecuentes sobre DuckDuckGo VPN"; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "La protección de red se ha interrumpido. Intentando volver a conectar ahora..."; +/* Message for tooltip about geoswitching */ +"network.protection.geoswitching.tip.message" = "Puedes personalizar la ubicación de tu VPN conectándote a cualquiera de nuestros servidores por todo el mundo."; + +/* Title for tooltip about geoswitching */ +"network.protection.geoswitching.tip.title" = "Cambiar tu ubicación"; + +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "La VPN de DuckDuckGo se ha interrumpido. Intentando volver a conectar ahora..."; /* Message for the network protection invite dialog */ -"network.protection.invite.dialog.message" = "Enter your invite code to get started."; +"network.protection.invite.dialog.message" = "Introduce tu código de invitación para empezar."; /* Title for the network protection invite screen */ "network.protection.invite.dialog.title" = "Te invitamos a probar DuckDuckGo VPN"; /* Prompt for the network protection invite code text field */ -"network.protection.invite.field.prompt" = "Invite Code"; +"network.protection.invite.field.prompt" = "Código de invitación"; /* Message for the network protection invite success view */ -"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; +"network.protection.invite.success.message" = "Oculta tu ubicación de los sitios web y disimula tu actividad en línea de los proveedores de Internet y de otros en tu red."; /* Title for the network protection invite success view */ -"network.protection.invite.success.title" = "Success! You’re in."; +"network.protection.invite.success.title" = "¡Conseguido! Estás dentro."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; /* Title text for an iOS quick action that opens VPN settings */ "network.protection.quick-action.open-vpn" = "Abrir VPN"; +/* Button title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.action" = "Más información"; + +/* Message for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.message" = "Puedes utilizar sitios o aplicaciones que bloquean el tráfico de VPN posponiendo la conexión VPN."; + +/* Title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.title" = "Evitar conflictos de VPN"; + +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "La VPN está en modo de suspenso durante %@"; + /* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ "network.protection.status.connected.format" = "Conectado · %@"; /* The label for the NetP VPN when connecting */ -"network.protection.status.connecting" = "Connecting..."; +"network.protection.status.connecting" = "Conectando..."; /* The label for the NetP VPN when disconnected */ -"network.protection.status.disconnected" = "Not connected"; +"network.protection.status.disconnected" = "No conectado"; /* The label for the NetP VPN when disconnecting */ -"network.protection.status.disconnecting" = "Disconnecting..."; +"network.protection.status.disconnecting" = "Desconectando..."; /* Message label text for the status view when VPN is disconnected */ "network.protection.status.header.message.off" = "Conéctate para proteger todo el tráfico de Internet\nde tu dispositivo."; @@ -1655,26 +1730,41 @@ /* Header title label text for the status view when VPN is connected */ "network.protection.status.header.title.on" = "DuckDuckGo VPN está activado"; +/* Header title label text for the status view when VPN is snoozing */ +"network.protection.status.header.title.snoozed" = "La VPN de DuckDuckGo está en modo de suspenso"; + /* The status view 'Share Feedback' button which is shown inline on the status view after the temporary free use footer text */ -"network.protection.status.menu.share.feedback" = "Share Feedback"; +"network.protection.status.menu.share.feedback" = "Compartir opiniones"; + +/* The label for the NetP VPN when paused */ +"network.protection.status.paused" = "En pausa"; + +/* The label for when NetP VPN is snoozing plus the length of time remaining formatted as '0:00' */ +"network.protection.status.snoozing.format" = "En suspenso, quedan %@"; + +/* Snooze button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.snooze" = "Suspender durante 20 minutos"; + +/* Wake Up button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.wake-up" = "Despertar"; /* Connection details label shown in NetworkProtection's status view. */ -"network.protection.status.view.connection.details" = "Connection Details"; +"network.protection.status.view.connection.details" = "Detalles de conexión"; /* Custom DNS label shown in NetworkProtection's status view. */ -"network.protection.status.view.custom.dns" = "DNS Server"; +"network.protection.status.view.custom.dns" = "Servidor DNS"; /* Generic connection failed error message shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.message" = "Please try again later."; +"network.protection.status.view.error.connection.failed.message" = "Inténtalo de nuevo más tarde."; /* Generic connection failed error title shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; +"network.protection.status.view.error.connection.failed.title" = "Error de conexión."; /* IP Address label shown in NetworkProtection's status view. */ -"network.protection.status.view.ip.address" = "IP Address"; +"network.protection.status.view.ip.address" = "Dirección IP"; /* Location label shown in NetworkProtection's status view. */ -"network.protection.status.view.location" = "Location"; +"network.protection.status.view.location" = "Ubicación"; /* Label shown on the title of the settings section in NetworkProtection's status view. */ "network.protection.status.view.settings.section.title" = "Gestionar"; @@ -1682,12 +1772,15 @@ /* Title label text for the status view when netP is disconnected */ "network.protection.status.view.title" = "VPN"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "La protección de red está activada. Tu ubicación y actividad en línea están protegidas."; +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "La VPN de DuckDuckGo está activada. Tu ubicación y actividad en línea están protegidas."; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Redirigiendo el tráfico de dispositivos a través de %@."; +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "El modo en suspenso de la VPN ha terminado. Redirigiendo el tráfico de dispositivos a través de %@."; + /* Title for the button to link to the iOS app settings and enable notifications app-wide. */ "network.protection.turn.on.notifications.button.title" = "Activar notificaciones"; @@ -1698,13 +1791,13 @@ "network.protection.vpn.about" = "Acerca de"; /* Section header for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.section.header" = "Notifications"; +"network.protection.vpn.alerts.section.header" = "Notificaciones"; /* List section footer for the toggle for VPN alerts. */ "network.protection.vpn.alerts.toggle.section.footer" = "Recibe notificaciones si tu conexión se cae o cambia el estado de la VPN."; /* Title for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.toggle.title" = "VPN Notifications"; +"network.protection.vpn.alerts.toggle.title" = "Notificaciones de VPN"; /* Title for the data volume section in the VPN status screen */ "network.protection.vpn.data-volume" = "Volumen de datos"; @@ -1719,7 +1812,7 @@ "network.protection.vpn.exclude.local.networks.setting.title" = "Excluir redes locales"; /* Title for the VPN Location screen's All Countries section. */ -"network.protection.vpn.location.all.countries.section.title" = "All Countries"; +"network.protection.vpn.location.all.countries.section.title" = "Todos los países"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.connected" = "Ubicación conectada"; @@ -1734,7 +1827,7 @@ "network.protection.vpn.location.recommended.section.footer" = "Conectarte automáticamente al servidor más cercano que encontremos."; /* Title for the VPN Location screen's Recommended section. */ -"network.protection.vpn.location.recommended.section.title" = "Recommended"; +"network.protection.vpn.location.recommended.section.title" = "Recomendado"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.selected" = "Ubicación seleccionada"; @@ -1752,13 +1845,13 @@ "network.protection.vpn.preferred.location.nearest" = "Ubicación más cercana"; /* Title for the Preferred Location VPN Settings item. */ -"network.protection.vpn.preferred.location.title" = "Preferred Location"; +"network.protection.vpn.preferred.location.title" = "Ubicación preferida"; /* Footer text for the DNS server setting item. */ -"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo routes DNS queries through our DNS servers so your internet provider can't see what websites you visit."; +"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo enruta las consultas de DNS a través de nuestros servidores DNS para que tu proveedor de Internet no pueda ver qué sitios web visitas."; /* Title for the FAQ row in the VPN status screen. */ -"network.protection.vpn.settings.faq" = "FAQs and Support"; +"network.protection.vpn.settings.faq" = "Preguntas frecuentes y asistencia"; /* Title for the feedback row in the VPN status screen. */ "network.protection.vpn.settings.share-feedback" = "Compartir comentarios de VPN"; @@ -1940,6 +2033,66 @@ /* Deactivate button */ "pm.deactivate" = "Desactivar"; +/* Label for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.label" = "Correo electrónico (opcional)"; + +/* Placeholder for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.placeholder" = "nombre@email.com"; + +/* Placeholder for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.placeholder" = "Danos tu opinión…"; + +/* Title for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.title" = "Comentarios generales"; + +/* Title for the ITR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-itr-problem.title" = "Identity Theft Restoration"; + +/* Title for the PIR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-pir-problem.title" = "Personal Information Removal"; + +/* Title for the Subscriptions and Payments category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-ppro-problem.title" = "Suscripciones y pagos"; + +/* Placeholder for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.placeholder" = "Cuéntanos qué está pasando..."; + +/* Title for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.title" = "Informar de un problema"; + +/* Title for the VPN category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-vpn-problem.title" = "VPN"; + +/* Placeholder for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.placeholder" = "¿Qué función te gustaría ver?"; + +/* Title for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.title" = "Solicitud de función"; + +/* Title for the category selection section in the Privacy Pro feedback form */ +"ppro.feedback-form.select-category.title" = "SELECCIONA UNA CATEGORÍA"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-1" = "¿Has encontrado una incidencia que no aparece en nuestro [centro de ayuda] (duck://)? Nos interesa mucho saberlo.\n\nIndica una dirección de correo electrónico si quieres que nos pongamos en contacto contigo en relación con esta incidencia (es posible que no podamos responder a todas las incidencias):"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-2" = "Además de los detalles introducidos anteriormente, enviamos información anónima con tus comentarios:"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-3" = "• Si algunas funciones del navegador están activas"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-4" = "Agregar diagnósticos de aplicaciones (por ejemplo, códigos de error)"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-5" = "Al tocar en \"Enviar\", aceptas que DuckDuckGo utilice la información enviada para mejorar la aplicación."; + +/* Title for the text box in the Privacy Pro feedback form */ +"ppro.feedback-form.text-box.title" = "Opinión"; + +/* Title for the Privacy Pro feedback form */ +"ppro.feedback-form.title" = "Enviar comentarios"; + /* Button title for sync bookmarks limits exceeded warning to go to manage bookmarks */ "prefrences.sync.bookmarks-limit-exceeded-action" = "Gestionar marcadores"; @@ -2029,6 +2182,18 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Favoritos"; +/* Name of the option the user can chose to give general browser feedback */ +"send.browser.feedback.general-feedback" = "Comentarios generales"; + +/* Name of the option the user can chose to give browser feedback about a problem they enountered */ +"send.browser.feedback.report-problem" = "Informar de un problema"; + +/* Name of the option the user can chose to give browser feedback about a feature they would like */ +"send.browser.feedback.request-feature" = "Solicitar una función"; + +/* Title of the picker where the user can chose the category of the feedback they want ot send. */ +"send.browser.feedback.select-category" = "SELECCIONA UNA CATEGORÍA"; + /* Settings cell for About DDG */ "settings.about.ddg" = "Acerca de DuckDuckGo"; @@ -2084,6 +2249,9 @@ /* Section footer Autolock description */ "settings.autolock.description" = "Si se habilita Touch ID, Face ID o un código de acceso del sistema, se te pedirá que desbloquees la aplicación al abrirla."; +/* Settings cell for Browser Feedback */ +"settings.browser.feedback" = "Comentarios sobre el navegador"; + /* Settings screen cell text for Automatically Clearing Data */ "settings.clear.data" = "Borrar datos automáticamente"; @@ -2182,7 +2350,7 @@ "settings.ppro" = "Privacy Pro"; /* Title for Link in the Footer of Privacy Pro section */ -"settings.ppro.footer" = "Privacy Policy and Terms of Service"; +"settings.ppro.footer" = "Política de privacidad y condiciones del servicio"; /* Settings screen cell for long press previews */ "settings.previews" = "Vistas previas con pulsación larga"; @@ -2208,23 +2376,20 @@ /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Establece la posición de la barra de direcciones"; -/* Subscription activation pending description */ -"settings.subscription.activation.pending.description" = "This is taking longer than usual, please check back later."; +/* Privacy pro description subtitle in settings when the is activating */ +"settings.subscription.activating" = "Activando"; -/* Subscription activation pending title */ -"settings.subscription.activation.pending.title" = "Se está activando tu suscripción"; +/* Subscription activation pending description */ +"settings.subscription.activation.pending.description" = "Esta acción está tardando más de lo habitual, vuelve a comprobarlo más tarde."; /* Data Broker protection cell subtitle for privacy pro */ -"settings.subscription.DBP.subtitle" = "Remove your info from sites that sell it"; +"settings.subscription.DBP.subtitle" = "Elimina tu información de los sitios que la venden"; /* Data Broker protection cell title for privacy pro */ "settings.subscription.DBP.title" = "Personal Information Removal"; -/* Privacy pro description subtext */ -"settings.subscription.description" = "Más privacidad sin fisuras con tres nuevas protecciones:"; - /* I have a Subscription button text for privacy pro */ -"settings.subscription.existing.subscription" = "I Have a Subscription"; +"settings.subscription.existing.subscription" = "Tengo una suscripción"; /* Subscription expired description */ "settings.subscription.expired.comment" = "Suscríbete de nuevo para seguir usando Privacy Pro"; @@ -2232,23 +2397,26 @@ /* Subscription expired tittle message */ "settings.subscription.expired.title" = "Tu suscripción a Privacy Pro ha caducado"; -/* Privacy pro features list */ -"settings.subscription.features" = " • VPN\n • Personal Information Removal\n • Identity Theft Restoration"; - /* Identity theft restoration cell subtitle for privacy pro */ -"settings.subscription.ITR.subtitle" = "If your identity is stolen, we'll help restore it"; +"settings.subscription.ITR.subtitle" = "Si te roban la identidad, te ayudaremos a restaurarla"; /* Identity theft restoration cell title for privacy pro */ "settings.subscription.ITR.title" = "Identity Theft Restoration"; /* Get Privacy Pro button text for privacy pro */ -"settings.subscription.learn.more" = "Get Privacy Pro"; +"settings.subscription.learn.more" = "Conseguir Privacy Pro"; /* Subscription Settings button text for privacy pro */ -"settings.subscription.manage" = "Subscription Settings"; +"settings.subscription.manage" = "Ajustes de suscripción"; + +/* Privacy Pro description subtitle in settings */ +"settings.subscription.row.description" = "Incluye nuestra VPN y Identity Theft Restoration."; -/* Call to action title for Privacy Pro */ -"settings.subscription.subscribe" = "Subscribe to Privacy Pro"; +/* Call to action title for Privacy Pro settings */ +"settings.subscription.subscribe" = "Protege tu conexión e identidad con Privacy Pro"; + +/* Privacy pro description subtitle in settings */ +"settings.subscription.us.description" = "Incluye nuestras VPN, Personal Information Removal, e Identity Theft Restoration."; /* VPN cell title for privacy pro */ "settings.subscription.VPN.title" = "VPN"; @@ -2317,67 +2485,64 @@ "siteFeedback.urlPlaceholder" = "¿Qué sitio web no funciona?"; /* Subscription Activation Info */ -"subscription.activate..header.description" = "Access your Privacy Pro subscription on this device via Apple ID or an email address."; +"subscription.activate..header.description" = "Accede a tu suscripción a Privacy Pro en este dispositivo mediante la ID de Apple o una dirección de correo electrónico."; /* Button for adding email address to subscription */ -"subscription.activate.add.email.button" = "Add Email"; +"subscription.activate.add.email.button" = "Añadir correo electrónico"; /* Apple ID option for activation */ -"subscription.activate.appleid" = "Apple ID"; +"subscription.activate.appleid" = "ID de Apple"; /* Button text for restoring purchase via Apple ID */ -"subscription.activate.appleid.button" = "Restore Purchase"; +"subscription.activate.appleid.button" = "Restaurar compra"; /* Description for Apple ID activation */ -"subscription.activate.appleid.description" = "Restore your purchase to activate your subscription on this device."; +"subscription.activate.appleid.description" = "Restaura tu compra para activar tu suscripción en este dispositivo."; /* Subscription Activation Info */ -"subscription.activate.description" = "Your subscription is automatically available in DuckDuckGo on any device signed in to your Apple ID."; +"subscription.activate.description" = "Tu suscripción está disponible automáticamente en DuckDuckGo en cualquier dispositivo que haya iniciado sesión en tu ID de Apple."; /* Button for editing email address added to subscription */ -"subscription.activate.edit.email.button" = "Edit Email"; +"subscription.activate.edit.email.button" = "Editar correo electrónico"; /* View Title for editing your email account */ -"subscription.activate.edit.email.title" = "Edit Email"; +"subscription.activate.edit.email.title" = "Editar correo electrónico"; /* Email option for activation */ -"subscription.activate.email" = "Email"; +"subscription.activate.email" = "Correo electrónico"; /* Restore button title for Email */ -"subscription.activate.email.button" = "Enter Email"; +"subscription.activate.email.button" = "Introducir correo electrónico"; /* Description for Email activation */ -"subscription.activate.email.description" = "Use your email to activate your subscription on this device."; +"subscription.activate.email.description" = "Utiliza tu correo electrónico para activar tu suscripción en este dispositivo."; /* Activate subscription title */ "subscription.activate.email.title" = "Activar suscripción"; /* Button title for cancelling email deletion */ -"subscription.activate.manage.email.cancel" = "Cancel"; +"subscription.activate.manage.email.cancel" = "Cancelar"; /* Button title for confirming email deletion */ -"subscription.activate.manage.email.OK" = "OK"; +"subscription.activate.manage.email.OK" = "De acuerdo"; /* Restore button title for AppleID */ -"subscription.activate.restore.apple" = "Restore Purchase"; +"subscription.activate.restore.apple" = "Restaurar compra"; /* Subscription Activation Title */ -"subscription.activate.title" = "Activate your subscription on this device"; +"subscription.activate.title" = "Activar tu suscripción en este dispositivo"; /* Resend activation instructions button */ -"subscription.add.device.resend.instructions" = "Resend Instructions"; +"subscription.add.device.resend.instructions" = "Reenviar instrucciones"; /* Add email to an existing subscription */ -"subscription.add.email" = "Add an email address to activate your subscription on your other devices. We’ll only use this address to verify your subscription."; +"subscription.add.email" = "Añade una dirección de correo electrónico para activar tu suscripción en tus otros dispositivos. Solo utilizaremos esta dirección para verificar tu suscripción."; /* View title for adding email to subscription */ -"subscription.add.email.title" = "Add Email"; - -/* Title for Alert messages */ -"subscription.alert.title" = "subscription.alert.title"; +"subscription.add.email.title" = "Añadir correo electrónico"; /* Subscription availability message on Apple devices */ -"subscription.available.apple" = "Privacy Pro is available on any device signed in to the same Apple ID."; +"subscription.available.apple" = "Privacy Pro está disponible en cualquier dispositivo en el que hayas iniciado sesión con la misma ID de Apple."; /* Text for the manage billing page */ "subscription.billing.google.text" = "Has adquirido tu suscripción a través de Google Play Store. Para renovar tu suscripción, abre la configuración de suscripción de Google Play Store en un dispositivo que haya iniciado sesión en la misma cuenta de Google que usaste para comprar originalmente tu suscripción."; @@ -2385,89 +2550,86 @@ /* Title for the manage billing page */ "subscription.billing.google.title" = "Planes de suscripción"; -/* Subscription annual billing period type */ -"subscription.billing.period.annual" = "annual"; - -/* Subscription monthly billing period type */ -"subscription.billing.period.monthly" = "monthly"; - /* Subscription Removal confirmation message */ -"subscription.cancel.message" = "Your subscription has been removed from this device."; +"subscription.cancel.message" = "Tu suscripción se ha eliminado de este dispositivo."; /* Change plan or cancel title */ -"subscription.change.plan" = "Update Plan or Cancel"; +"subscription.change.plan" = "Actualizar el plan o cancelar"; /* Navigation Button for closing subscription view */ -"subscription.close" = "Close"; +"subscription.close" = "Cerrar"; /* Title for Confirm messages */ -"subscription.confirm.title" = "Are you sure?"; +"subscription.confirm.title" = "¿Seguro?"; /* Header for section for activating subscription on other devices */ -"subscription.devices.header" = "Activate on Other Devices"; +"subscription.devices.header" = "Activar en otros dispositivos"; /* Footer for section for activating subscription on other devices when email was not yet added */ -"subscription.devices.no.email.footer" = "Add an optional email to your subscription or use your Apple ID to access Privacy Pro on other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.no.email.footer" = "Añade un correo electrónico opcional a tu suscripción para acceder a Privacy Pro en otros dispositivos. **[Más información](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Footer for section for activating subscription on other devices when email is added */ -"subscription.devices.with.email.footer" = "Use this email to activate your subscription in Settings > Privacy Pro in the DuckDuckGo app on your other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.with.email.footer" = "Utiliza este correo electrónico para activar tu suscripción en Configuración > Privacy Pro en la aplicación DuckDuckGo en tus otros dispositivos. **[Más información](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Alert content for not found subscription */ -"subscription.email.inactive.alert.message" = "The subscription associated with this email is no longer active."; +"subscription.email.inactive.alert.message" = "La suscripción asociada a este correo electrónico ya no está activa."; /* Alert title for not found subscription */ -"subscription.email.inactive.alert.title" = "Subscription Not Found"; +"subscription.email.inactive.alert.title" = "Suscripción no encontrada"; /* Alert content for not found subscription */ -"subscription.expired.alert.message" = "The subscription associated with this Apple ID is no longer active."; +"subscription.expired.alert.message" = "La suscripción asociada a esta ID de Apple ya no está activa."; /* FAQ Button */ -"subscription.faq" = "FAQs and Support"; +"subscription.faq" = "Preguntas frecuentes y asistencia"; /* FAQ Description */ -"subscription.faq.description" = "Get answers to frequently asked questions or contact Privacy Pro support from our help pages."; +"subscription.faq.description" = "Obtén respuestas a las preguntas más frecuentes o ponte en contacto con el servicio de asistencia de Privacy Pro en nuestras páginas de ayuda."; + +/* Send Feedback Button */ +"subscription.feedback" = "Enviar comentarios"; /* Cancel action for the existing subscription dialog */ -"subscription.found.cancel" = "Cancel"; +"subscription.found.cancel" = "Cancelar"; /* Restore action for the existing subscription dialog */ -"subscription.found.restore" = "Restore"; +"subscription.found.restore" = "Restaurar"; /* Message for the existing subscription dialog */ -"subscription.found.text" = "We found a subscription associated with this Apple ID."; +"subscription.found.text" = "Hemos encontrado una suscripción asociada a esta ID de Apple."; /* Title for the existing subscription dialog */ -"subscription.found.title" = "Subscription Found"; +"subscription.found.title" = "Suscripción encontrada"; /* Help and support Section header */ -"subscription.help" = "Help and support"; +"subscription.help" = "Ayuda y asistencia"; /* Manage Plan header */ -"subscription.manage.plan" = "Manage Plan"; +"subscription.manage.plan" = "Gestionar plan"; /* Header for the subscription section */ -"subscription.manage.title" = "Subscription"; +"subscription.manage.title" = "Suscripción"; /* Alert content for not found subscription */ -"subscription.notFound.alert.message" = "There is no subscription associated with this Apple ID."; +"subscription.notFound.alert.message" = "No hay ninguna suscripción asociada a esta ID de Apple."; /* Alert title for not found subscription */ -"subscription.notFound.alert.title" = "Subscription Not Found"; +"subscription.notFound.alert.title" = "Suscripción no encontrada"; /* View plans button text */ -"subscription.notFound.view.plans" = "View Plans"; +"subscription.notFound.view.plans" = "Ver planes"; /* Hero Text for Personal information removal */ -"subscription.pir.hero" = "Activate Privacy Pro on desktop to set up Personal Information Removal"; +"subscription.pir.hero" = "Activa Privacy Pro en el escritorio para configurar Personal Information Removal"; /* Description on how to use Personal information removal in desktop. The first placeholder references a location in the Desktop application. Privacy Pro>, and the second, the menu entry. i.e. */ -"subscription.pir.heroText" = "In the DuckDuckGo browser for desktop, go to %1$@ and click %2$@ to get started."; +"subscription.pir.heroText" = "En el navegador DuckDuckGo para escritorio, ve a %1$@ y haz clic en %2$@ para empezar."; /* Settings references a menu in the Desktop app, Privacy Pro, references our product name */ -"subscription.pir.heroTextLocation" = "Settings > Privacy Pro"; +"subscription.pir.heroTextLocation" = "Ajustes > Privacy Pro"; /* Menu item for enabling Personal Information Removal on Desktop */ -"subscription.pir.heroTextMenyEntry" = "I have a subscription"; +"subscription.pir.heroTextMenyEntry" = "Tengo una suscripción"; /* Text for the 'macOS' button */ "subscription.pir.macos" = "Mac"; @@ -2476,28 +2638,28 @@ "subscription.pir.windows" = "Windows"; /* Progress view title when completing the purchase */ -"subscription.progress.view.completing.purchase" = "Completing purchase..."; +"subscription.progress.view.completing.purchase" = "Completando compra..."; /* Progress view title when starting the purchase */ -"subscription.progress.view.purchasing.subscription" = "Purchase in progress..."; +"subscription.progress.view.purchasing.subscription" = "Compra en curso..."; /* Progress view title when restoring past subscription purchase */ -"subscription.progress.view.restoring.subscription" = "Restoring subscription..."; +"subscription.progress.view.restoring.subscription" = "Restaurando suscripción..."; /* Remove from this device button */ -"subscription.remove.from.device.button" = "Remove From This Device"; +"subscription.remove.from.device.button" = "Eliminar de este dispositivo"; /* Remove from device confirmation dialog text */ -"subscription.remove.from.device.text" = "You will no longer be able to access your Privacy Pro subscription on this device. This will not cancel your subscription, and it will remain active on your other devices."; +"subscription.remove.from.device.text" = "Ya no podrás acceder a tu suscripción a Privacy Pro en este dispositivo. Esto no cancelará tu suscripción, que seguirá activa en tus otros dispositivos."; /* Remove from device confirmation dialog title */ -"subscription.remove.from.device.title" = "Remove from this device?"; +"subscription.remove.from.device.title" = "¿Eliminar de este dispositivo?"; /* Remove subscription button text */ -"subscription.remove.subscription" = "Remove Subscription"; +"subscription.remove.subscription" = "Eliminar suscripción"; /* Remove subscription cancel button text */ -"subscription.remove.subscription.cancel" = "Cancel"; +"subscription.remove.subscription.cancel" = "Cancelar"; /* Button text for general error message */ "subscription.restore.backend.error.button" = "Volver a Ajustes"; @@ -2509,28 +2671,43 @@ "subscription.restore.backend.error.title" = "Se ha producido un error"; /* Alert for general error message */ -"subscription.restore.general.error.message" = "The App Store was unable to process your purchase. Please try again later."; +"subscription.restore.general.error.message" = "La App Store no ha podido procesar tu compra. Inténtalo de nuevo más tarde."; /* Alert for general error title */ -"subscription.restore.general.error.title" = "Something Went Wrong"; +"subscription.restore.general.error.title" = "Se ha producido un error"; /* Alert button text for restored purchase alert */ -"subscription.restore.success.alert.button" = "OK"; +"subscription.restore.success.alert.button" = "De acuerdo"; /* Alert message for restored purchase */ -"subscription.restore.success.alert.message" = "Your purchases have been restored."; +"subscription.restore.success.alert.message" = "Se han restablecido tus compras."; /* Alert title for restored purchase */ -"subscription.restore.success.alert.title" = "You’re all set."; +"subscription.restore.success.alert.title" = "Ya está todo listo."; + +/* Subtitle in header when subscribed */ +"subscription.subscribed" = "Suscrito"; /* Subscription Expired Data. This reads as 'Your subscription expired on (date)' */ "subscription.subscription.expired.caption" = "Tu suscripción caducó el % @"; -/* Subscription expiration info. This reads as 'Your (monthly or annual) subscription expires on (date)' */ -"subscription.subscription.expiring.caption" = "Your %1$@ subscription expires on %2$@."; +/* Monthly subscription expiration info where parameter is expiration date. This reads as 'Your monthly subscription expires on (date)' */ +"subscription.subscription.expiring.monthly.caption" = "Tu suscripción mensual caduca el %@."; + +/* Unknown period subscription expiration info where parameter is expiration date. This reads as 'Your subscription expires on (date)' */ +"subscription.subscription.expiring.unknown.caption" = "Tu suscripción caduca el %@."; + +/* Annual subscription expiration info where parameter is expiration date. This reads as 'Your annual subscription expires on (date)' */ +"subscription.subscription.expiring.yearly.caption" = "Tu suscripción anual caduca el %@."; + +/* Monthly subscription renewal info where parameter is renewal date. This reads as 'Your monthly subscription renews on (date)' */ +"subscription.subscription.renewing.monthly.caption" = "Tu suscripción mensual se renueva el %@."; + +/* Unknown period subscription renewal info where parameter is renewal date. This reads as 'Your subscription renews on (date)' */ +"subscription.subscription.renewing.unknown.caption" = "Tu suscripción se renueva el %@."; -/* Subscription renewal info. This reads as 'Your (monthly or annual) subscription renews on (date)' */ -"subscription.subscription.renewing.caption" = "Your %1$@ subscription renews on %2$@."; +/* Annual subscription renewal info where parameter is renewal date. This reads as 'Your annual subscription renews on (date)' */ +"subscription.subscription.renewing.yearly.caption" = "Tu suscripción anual se renueva el %@."; /* Navigation bar Title for subscriptions */ "subscription.title" = "Privacy Pro"; @@ -2677,118 +2854,118 @@ "voiceSearch.footer.note.old" = "El audio se procesa en el dispositivo. No se almacena ni comparte con nadie, incluyendo DuckDuckGo."; /* Cancel action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.cancel" = "Dismiss"; +"vpn.access-revoked.alert.action.cancel" = "Descartar"; /* Primary action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.subscribe" = "Subscribe"; +"vpn.access-revoked.alert.action.subscribe" = "Subscríbete"; /* Alert message for the alert when the Privacy Pro subscription expiress */ -"vpn.access-revoked.alert.message" = "Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"vpn.access-revoked.alert.message" = "Suscríbete a Privacy Pro para volver a conectar la VPN de DuckDuckGo."; /* Alert title for the alert when the Privacy Pro subscription expires */ -"vpn.access-revoked.alert.title" = "VPN disconnected due to expired subscription"; +"vpn.access-revoked.alert.title" = "VPN desconectada por suscripción caducada"; /* Title for the VPN widget onboarding screen */ "vpn.addWidget.settings.title" = "Busca y selecciona DuckDuckGo. A continuación, desliza el dedo hasta VPN y selecciona Añadir widget."; /* Title for the Cancel button of the VPN feedback form */ -"vpn.feedback-form.button.cancel" = "Cancel"; +"vpn.feedback-form.button.cancel" = "Cancelar"; /* Title for the Done button of the VPN feedback form */ -"vpn.feedback-form.button.done" = "Done"; +"vpn.feedback-form.button.done" = "Hecho"; /* Title for the Submit button of the VPN feedback form */ -"vpn.feedback-form.button.submit" = "Submit"; +"vpn.feedback-form.button.submit" = "Enviar"; /* Title for the Submitting state of the VPN feedback form */ -"vpn.feedback-form.button.submitting" = "Submitting…"; +"vpn.feedback-form.button.submitting" = "Enviando..."; /* Title for the browser crash/freeze category of the VPN feedback form */ -"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN causes browser to crash or freeze"; +"vpn.feedback-form.category.browser-crash-or-freeze" = "La VPN hace que el navegador falle o se congele"; /* Title for the 'VPN fails to connect' category of the VPN feedback form */ -"vpn.feedback-form.category.fails-to-connect" = "VPN fails to connect"; +"vpn.feedback-form.category.fails-to-connect" = "La VPN no se conecta"; /* Title for the 'VPN feature request' category of the VPN feedback form */ -"vpn.feedback-form.category.feature-request" = "VPN feature request"; +"vpn.feedback-form.category.feature-request" = "Solicitud de función de VPN"; /* Title for the category 'VPN causes issues with other apps or websites' category of the VPN feedback form */ -"vpn.feedback-form.category.issues-with-apps" = "VPN causes issues with other apps or websites"; +"vpn.feedback-form.category.issues-with-apps" = "La VPN interfiere con otras aplicaciones o sitios web"; /* Title for the local device connectivity category of the VPN feedback form */ -"vpn.feedback-form.category.local-device-connectivity" = "VPN won't let me connect to local device"; +"vpn.feedback-form.category.local-device-connectivity" = "La VPN no me deja conectarme a un dispositivo local"; /* Title for the 'other VPN feedback' category of the VPN feedback form */ -"vpn.feedback-form.category.other" = "Other VPN feedback"; +"vpn.feedback-form.category.other" = "Otros comentarios de VPN"; /* Title for the category selection state of the VPN feedback form */ -"vpn.feedback-form.category.select-category" = "Select a category"; +"vpn.feedback-form.category.select-category" = "SELECCIONA UNA CATEGORÍA"; /* Title for the 'VPN is too slow' category of the VPN feedback form */ -"vpn.feedback-form.category.too-slow" = "VPN connection is too slow"; +"vpn.feedback-form.category.too-slow" = "La conexión de la VPN es demasiado lenta"; /* Title for the 'unable to install' category of the VPN feedback form */ -"vpn.feedback-form.category.unable-to-install" = "Unable to install VPN"; +"vpn.feedback-form.category.unable-to-install" = "No se puede instalar la VPN"; /* Title for the feedback sent view description of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.description" = "Your feedback will help us improve the\nDuckDuckGo VPN."; +"vpn.feedback-form.sending-confirmation.description" = "Tus comentarios nos ayudarán a mejorar la VPN de DuckDuckGo."; /* Title for the feedback sending error text of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.error" = "We couldn't send your feedback right now, please try again."; +"vpn.feedback-form.sending-confirmation.error" = "No hemos podido enviar tus comentarios ahora mismo, inténtalo de nuevo."; /* Title for the feedback sent view title of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.title" = "Thank you!"; +"vpn.feedback-form.sending-confirmation.title" = "¡Gracias!"; /* Toast message when the VPN feedback form is submitted successfully */ -"vpn.feedback-form.submitted.message" = "Thank You! Feedback submitted."; +"vpn.feedback-form.submitted.message" = "Gracias, se han enviado tus opiniones."; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-1" = "Please describe what's happening, what you expected to happen, and the steps that led to the issue:"; +"vpn.feedback-form.text-1" = "Describe lo que está pasando, lo que esperabas que ocurriera y los pasos que han llevado al problema:"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-2" = "In addition to the details entered into this form, your app issue report will contain:"; +"vpn.feedback-form.text-2" = "Además de los detalles introducidos en este formulario, tu informe de problemas de la aplicación contendrá:"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-3" = "• Whether specific DuckDuckGo features are enabled"; +"vpn.feedback-form.text-3" = "• Si están habilitadas funciones específicas de DuckDuckGo"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-4" = "• Aggregate DuckDuckGo app diagnostics"; +"vpn.feedback-form.text-4" = "• Diagnósticos agregados de la aplicación DuckDuckGo"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-5" = "By tapping \"Submit\" I agree that DuckDuckGo may use the information in this report for purposes of improving the app's features."; +"vpn.feedback-form.text-5" = "Al tocar en \"Enviar\", acepto que DuckDuckGo pueda utilizar la información de este informe para mejorar las funciones de la aplicación."; /* Title for each screen of the VPN feedback form */ -"vpn.feedback-form.title" = "Help Improve the DuckDuckGo VPN"; +"vpn.feedback-form.title" = "Ayuda a mejorar la VPN de DuckDuckGo"; /* VPN settings screen cell text for adding the VPN widget to the home screen */ "vpn.settings.add.widget" = "Añadir widget de VPN a la pantalla de inicio"; /* Disclaimer for the DNS Server section on the DNS Server screen */ -"vpn.settings.dns.section-disclaimer" = "Using a custom DNS server can impact browsing speeds and expose your activity to third parties if the server isn't secure or reliable."; +"vpn.settings.dns.section-disclaimer" = "El uso de un servidor DNS personalizado puede afectar a la velocidad de navegación y exponer tu actividad a terceros si el servidor no es seguro o fiable."; /* Header text for the DNS section on the VPN Settings screen */ "vpn.settings.dns.section-header" = "DNS"; /* Title for the Apply button on the DNS Server setting screen */ -"vpn.settings.dns.server.apply.button.title" = "Apply"; +"vpn.settings.dns.server.apply.button.title" = "Usar"; /* Default value for the DNS Server row on the VPN Settings screen */ "vpn.settings.dns.server.default.value" = "DuckDuckGo"; /* Title for the IPv4 Address setting */ -"vpn.settings.dns.server.ipv4.title" = "IPv4 Address"; +"vpn.settings.dns.server.ipv4.title" = "Dirección IPv4"; /* Custom option for the DNS Server setting */ -"vpn.settings.dns.server.option.custom" = "Custom"; +"vpn.settings.dns.server.option.custom" = "Personalizar"; /* Recommended option for the DNS Server setting */ -"vpn.settings.dns.server.option.default" = "DuckDuckGo (Recommended)"; +"vpn.settings.dns.server.option.default" = "DuckDuckGo (recomendado)"; /* Title for the DNS Server setting screen */ -"vpn.settings.dns.server.screen.title" = "DNS Server"; +"vpn.settings.dns.server.screen.title" = "Servidor DNS"; /* Title for the DNS Server row on the VPN Settings screen */ -"vpn.settings.dns.server.title" = "DNS Server"; +"vpn.settings.dns.server.title" = "Servidor DNS"; /* Title for the button to enable push notifications in system settings */ "waitlist.allow-notifications" = "Permitir notificaciones"; diff --git a/DuckDuckGo/es.lproj/OmniBar.strings b/DuckDuckGo/es.lproj/OmniBar.strings index ed1640fef4..66f635c8b6 100644 --- a/DuckDuckGo/es.lproj/OmniBar.strings +++ b/DuckDuckGo/es.lproj/OmniBar.strings @@ -7,6 +7,9 @@ /* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "Dt8-We-5tV"; */ "Dt8-We-5tV.accessibilityLabel" = "Borrar texto"; +/* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "gb5-wL-4OW"; */ +"gb5-wL-4OW.accessibilityLabel" = "Borrar texto"; + /* Class = "UIButton"; accessibilityLabel = "Share"; ObjectID = "hor-Jd-3kw"; */ "hor-Jd-3kw.accessibilityLabel" = "Compartir"; diff --git a/DuckDuckGo/es.lproj/Settings.strings b/DuckDuckGo/es.lproj/Settings.strings index 062b36d7bb..f7faff040c 100644 --- a/DuckDuckGo/es.lproj/Settings.strings +++ b/DuckDuckGo/es.lproj/Settings.strings @@ -10,9 +10,6 @@ /* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "5YD-Od-uDU"; */ "5YD-Od-uDU.text" = "Borrar datos automáticamente"; -/* Class = "UIButton"; normalTitle = "Settings"; ObjectID = "8YG-7Q-Czd"; */ -"8YG-7Q-Czd.normalTitle" = "Ajustes"; - /* Class = "UILabel"; text = "App Exit, Inactive for 1 Hour"; ObjectID = "9EC-lx-77p"; */ "9EC-lx-77p.text" = "Salir de la aplicación (1 hora de inactividad)"; @@ -76,9 +73,6 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Sitios no protegidos"; -/* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ -"ssa-zd-L3T.title" = "Tamaño del texto"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Mostrar teclado en"; diff --git a/DuckDuckGo/et.lproj/DaxOnboarding.strings b/DuckDuckGo/et.lproj/DaxOnboarding.strings index 42abf6aeaa..2c47c1a75a 100644 --- a/DuckDuckGo/et.lproj/DaxOnboarding.strings +++ b/DuckDuckGo/et.lproj/DaxOnboarding.strings @@ -1,12 +1,6 @@ /* Class = "UIButton"; normalTitle = "Hide"; ObjectID = "hgy-75-OvJ"; */ "hgy-75-OvJ.normalTitle" = "Peida"; -/* Class = "UILabel"; text = "Welcome to\nDuckDuckGo!"; ObjectID = "Lvy-9G-3gr"; */ -"Lvy-9G-3gr.text" = "Tere tulemast\nDuckDuckGo kasutajaks!"; - -/* Class = "UIButton"; normalTitle = "Let’s Do It!"; ObjectID = "oAv-Zt-uIt"; */ -"oAv-Zt-uIt.normalTitle" = "Teeme ära!"; - /* Class = "UIButton"; normalTitle = "Button"; ObjectID = "zJt-F9-L48"; */ "zJt-F9-L48.normalTitle" = "Nupp"; diff --git a/DuckDuckGo/et.lproj/Localizable.strings b/DuckDuckGo/et.lproj/Localizable.strings index 66bfb39f19..debc2d270a 100644 --- a/DuckDuckGo/et.lproj/Localizable.strings +++ b/DuckDuckGo/et.lproj/Localizable.strings @@ -341,7 +341,7 @@ "autofill.keep-enabled.alert.disable" = "Keela"; /* A link that takes the user to the DuckDuckGo help pages explaining password managers */ -"autofill.learn.more.link.title" = "Learn More"; +"autofill.learn.more.link.title" = "Loe edasi"; /* Button displayed after saving/updating an autofill login that takes the user to the saved login */ "autofill.login-save-action-button.toast" = "Kuva"; @@ -977,9 +977,6 @@ /* ad = advertisment */ "dax.onboarding.home.subsequent" = "Said selle!\n\nPea meeles: iga kord, kui minuga sirvid, kaotab jube reklaam tiivad. 👍"; -/* No comment provided by engineer. */ -"dax.onboarding.message" = "Internet võib olla üsna jube.\n\nÄra muretse! Privaatselt otsimine ja sirvimine on lihtsam kui arvad."; - /* No comment provided by engineer. */ "Debug" = "Silumine"; @@ -1080,7 +1077,7 @@ "duckPlayer.never.label" = "Mitte kunagi"; /* Body text for the modal feature explanation */ -"duckplayer.presentation.modal.body" = "Duck Player võimaldab sul DuckDuckGo-s teaterliku kogumusega YouTube'i vaadata ilma sihitud reklaamideta ja see, mida vaatad, ei mõjuta sinu soovitusi."; +"duckplayer.presentation.modal.body" = "Duck Player võimaldab sul DuckDuckGo-s YouTube'i vaadata ilma sihitud reklaamideta ja see, mida vaatad, ei mõjuta sinu soovitusi."; /* Button that will dismiss the modal */ "duckplayer.presentation.modal.dismiss-button" = "Sain aru!"; @@ -1088,17 +1085,23 @@ /* Two line title (separated by \n) for the feature explanation */ "duckplayer.presentation.modal.title" = "Kas näed Youtube'i kasutades massiliselt reklaame? Proovi Duck Playerit!"; +/* Two line title (separated by \n) for the feature explanation */ +"duckplayer.presentation.modal.title.youtube" = "Kas näed Youtube'i kasutades massiliselt reklaame? Mitte Duck Playeriga!"; + /* Footer label in the settings screen for Duck Player */ "duckplayer.settings.footer" = "DuckDuckGo pakub kõik privaatsuseks vajaliku, mida vajad enda kaitsmiseks veebi sirvimisel."; /* Text explaining what Duck Player is in the settings screen. */ -"duckplayer.settings.info-text" = "Duck Player pakub isikupärastatud reklaamidest vaba vaatamiskogemust ja takistab, et vaatamisaktiivsus mõjutaks sinu YouTube'i soovitusi."; +"duckplayer.settings.info-text" = "Duck Player võimaldab sul DuckDuckGo-s YouTube'i vaadata ilma sihitud reklaamideta ja see, mida vaatad, ei mõjuta sinu soovitusi."; /* Button that takes the user to learn more about Duck Player. */ "duckplayer.settings.learn-more" = "Loe edasi"; +/* Settings screen cell text for DuckPlayer settings to open in new tab */ +"duckplayer.settings.open-new-tab-label" = "Ava Duck Player uues vahekaardis"; + /* Settings screen cell text for DuckPlayer settings */ -"duckplayer.settings.open-videos-in" = "Ava videod Duck Playeris"; +"duckplayer.settings.open-videos-in" = "Kas avada YouTube'i videod Duck Playeris?"; /* Settings screen cell text for DuckPlayer settings */ "duckplayer.settings.title" = "Duck Player"; @@ -1169,9 +1172,6 @@ /* Title for prompt to sign up for email protection */ "email.signup-prompt.title" = "Peida oma e-post ja\nblokeeri jälgijad"; -/* No comment provided by engineer. */ -"empty!" = "empty!"; - /* Empty list state placholder */ "empty.bookmarks" = "Järjehoidjaid pole veel lisatud"; @@ -1322,6 +1322,39 @@ /* Confirmation button */ "feedback.form.submit" = "Esita"; +/* Title of the feedback form */ +"feedback.form.title" = "Aidake täiustada Privacy Pro'd"; + +/* Category for Identity Theft Restoration feedback */ +"feedback.general.category.itr" = "Identity Theft Restoration"; + +/* Category for Personal Info Removal feedback */ +"feedback.general.category.pir" = "Isikliku teabe eemaldamine"; + +/* Category for subscription and payments feedback */ +"feedback.general.category.ppro" = "Tellimused ja maksed"; + +/* Prompt to select a category for general feedback */ +"feedback.general.category.select" = "VALI KATEGOORIA"; + +/* Category for VPN feedback */ +"feedback.general.category.vpn" = "VPN"; + +/* Category for access code issues */ +"feedback.itr.category.access-code" = "Probleem pääsukoodiga"; + +/* Category for issues contacting an advisor */ +"feedback.itr.category.cant-contact-advisor" = "Ei saa nõustajaga ühendust võtta"; + +/* Prompt to select a category for Identity Theft Restoration feedback */ +"feedback.itr.category.select" = "VALI KATEGOORIA"; + +/* Category for other Identity Theft Restoration issues */ +"feedback.itr.category.something-else" = "Midagi muud"; + +/* Category for unhelpful advisor calls */ +"feedback.itr.category.unhelpful" = "Nõustajale helistamine ei aidanud"; + /* No comment provided by engineer. */ "feedback.negative.form.genericPlaceholder" = "Palun ole võimalikult konkreetne"; @@ -1361,6 +1394,24 @@ /* No comment provided by engineer. */ "feedback.performance.slowLoading" = "Veebilehed või otsingutulemid laaditakse aeglaselt"; +/* Category for when scan finds incorrect records */ +"feedback.pir.category.not-me" = "Skaneerimisel leiti kirjeid, mis ei ole minuga seotud"; + +/* Category for when scan doesn't find info on a specific site */ +"feedback.pir.category.nothing-on-site" = "Skaneerimisel ei leitud minu infot konkreetselt saidilt"; + +/* Category for other Personal Info Removal issues */ +"feedback.pir.category.other" = "Midagi muud"; + +/* Category for when the removal process is stuck */ +"feedback.pir.category.removal-stuck" = "Eemaldamisprotsess on takerdunud"; + +/* Category for when the scan is stuck */ +"feedback.pir.category.scan-stuck" = "Kirjete skannimine on takerdunud"; + +/* Prompt to select a category for Personal Info Removal feedback */ +"feedback.pir.category.select" = "VALI KATEGOORIA"; + /* Header above input field */ "feedback.positive.form.header" = "Jagage üksikasju"; @@ -1379,6 +1430,15 @@ /* Button encouraging uses to share details aboout their feedback */ "feedback.positive.submit" = "Jagage üksikasju"; +/* Category for other Privacy Pro issues */ +"feedback.ppro.category.other" = "Midagi muud"; + +/* Category for one-time password issues */ +"feedback.ppro.category.otp" = "Probleem ühekordse parooliga"; + +/* Prompt to select a category for Privacy Pro feedback */ +"feedback.ppro.category.select" = "VALI KATEGOORIA"; + /* No comment provided by engineer. */ "feedback.start.footer" = "Teie anonüümne tagasiside on meile oluline."; @@ -1529,12 +1589,6 @@ /* No comment provided by engineer. */ "keyCommandShowAllTabs" = "Kuva kõik vahekaardid"; -/* Please preserve newline character */ -"launchscreenWelcomeMessage" = "Tere tulemast\nDuckDuckGo kasutajaks!"; - -/* No comment provided by engineer. */ -"LOREM IPSUM" = "LOREM IPSUM"; - /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "DuckDuckGo for Mac on just nii kiire, kui vajad, oodatud sirvimisfunktsioonidega ja tulvil meie oma klassi parimaid privaatsuselemente."; @@ -1586,62 +1640,83 @@ /* Edit button */ "navigation.title.edit" = "Redigeeri"; -/* String indicating NetP is connected when viewed from the settings screen */ -"netP.cell.connected" = "Connected"; - -/* String indicating NetP is disconnected when viewed from the settings screen */ -"netP.cell.disconnected" = "Not connected"; - -/* Title for the DuckDuckGo VPN feature in settings */ -"netP.settings.title" = "VPN"; +/* String indicating NetP is snoozing when viewed from the settings screen */ +"netP.cell.snoozing" = "Peatamine"; /* Title for the DuckDuckGo VPN feature */ "netP.title" = "DuckDuckGo VPN"; +/* Button title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.action" = "Lisage vidin"; + +/* Message for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.message" = "Lülitage VPN sisse ja välja otse avakuvalt."; + +/* Title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.title" = "VPN vidina lisamine"; + /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN-ühendus katkestati aegunud tellimuse tõttu. DuckDuckGo VPN-i taasühendamiseks telli Privacy Pro."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Network Protectioni ühenduse loomine nurjus. Proovi hiljem uuesti."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "DuckDuckGo VPN-i ühendamine ebaõnnestus. Proovige hiljem uuesti."; /* Title for the VPN FAQ screen. */ "network.protection.faq.title" = "DuckDuckGo VPN-i KKK"; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Network Protection katkestati. Proovin uuesti ühendust luua ..."; +/* Message for tooltip about geoswitching */ +"network.protection.geoswitching.tip.message" = "Saate oma VPN-i asukohta kohandada, ühendades mõne meie serveriga kogu maailmas."; + +/* Title for tooltip about geoswitching */ +"network.protection.geoswitching.tip.title" = "Muutke oma asukohta"; + +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "DuckDuckGo VPN katkestati. Proovin uuesti ühendust luua..."; /* Message for the network protection invite dialog */ -"network.protection.invite.dialog.message" = "Enter your invite code to get started."; +"network.protection.invite.dialog.message" = "Alustamiseks sisesta oma kutsekood."; /* Title for the network protection invite screen */ "network.protection.invite.dialog.title" = "Oled kutsutud proovima DuckDuckGo VPN-i"; /* Prompt for the network protection invite code text field */ -"network.protection.invite.field.prompt" = "Invite Code"; +"network.protection.invite.field.prompt" = "Kutsekood"; /* Message for the network protection invite success view */ -"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; +"network.protection.invite.success.message" = "Peida oma asukoht veebisaitide eest ja varja oma veebitegevust Interneti-teenusepakkujate ja teiste võrgus olevate inimeste eest."; /* Title for the network protection invite success view */ -"network.protection.invite.success.title" = "Success! You’re in."; +"network.protection.invite.success.title" = "Tehtud! Sa oled sees."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; /* Title text for an iOS quick action that opens VPN settings */ "network.protection.quick-action.open-vpn" = "Ava VPN"; +/* Button title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.action" = "Loe edasi"; + +/* Message for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.message" = "VPN-i liiklust blokeerivaid saite või rakendusi saate kasutada VPN-ühendust uinutades."; + +/* Title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.title" = "Vältige VPN-konflikte"; + +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN on peatatud %@ jooksul"; + /* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ "network.protection.status.connected.format" = "Ühendatud · %@"; /* The label for the NetP VPN when connecting */ -"network.protection.status.connecting" = "Connecting..."; +"network.protection.status.connecting" = "Ühendamine..."; /* The label for the NetP VPN when disconnected */ -"network.protection.status.disconnected" = "Not connected"; +"network.protection.status.disconnected" = "Ei ole ühendatud"; /* The label for the NetP VPN when disconnecting */ -"network.protection.status.disconnecting" = "Disconnecting..."; +"network.protection.status.disconnecting" = "Katkestamine..."; /* Message label text for the status view when VPN is disconnected */ "network.protection.status.header.message.off" = "Ühendage kõigi oma seadme kaitsmiseks\n Internetiliiklus."; @@ -1655,26 +1730,41 @@ /* Header title label text for the status view when VPN is connected */ "network.protection.status.header.title.on" = "DuckDuckGo VPN on sisse lülitatud"; +/* Header title label text for the status view when VPN is snoozing */ +"network.protection.status.header.title.snoozed" = "DuckDuckGo VPN on peatatud"; + /* The status view 'Share Feedback' button which is shown inline on the status view after the temporary free use footer text */ -"network.protection.status.menu.share.feedback" = "Share Feedback"; +"network.protection.status.menu.share.feedback" = "Jaga tagasisidet"; + +/* The label for the NetP VPN when paused */ +"network.protection.status.paused" = "Peatatud"; + +/* The label for when NetP VPN is snoozing plus the length of time remaining formatted as '0:00' */ +"network.protection.status.snoozing.format" = "Peatatud, veel %@ jooksul"; + +/* Snooze button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.snooze" = "Peata 20 minutiks"; + +/* Wake Up button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.wake-up" = "Ärata"; /* Connection details label shown in NetworkProtection's status view. */ -"network.protection.status.view.connection.details" = "Connection Details"; +"network.protection.status.view.connection.details" = "Ühenduse üksikasjad"; /* Custom DNS label shown in NetworkProtection's status view. */ -"network.protection.status.view.custom.dns" = "DNS Server"; +"network.protection.status.view.custom.dns" = "DNS server"; /* Generic connection failed error message shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.message" = "Please try again later."; +"network.protection.status.view.error.connection.failed.message" = "Proovi hiljem uuesti."; /* Generic connection failed error title shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; +"network.protection.status.view.error.connection.failed.title" = "Ühendus ebaõnnestus."; /* IP Address label shown in NetworkProtection's status view. */ -"network.protection.status.view.ip.address" = "IP Address"; +"network.protection.status.view.ip.address" = "IP-aadress"; /* Location label shown in NetworkProtection's status view. */ -"network.protection.status.view.location" = "Location"; +"network.protection.status.view.location" = "Asukoht"; /* Label shown on the title of the settings section in NetworkProtection's status view. */ "network.protection.status.view.settings.section.title" = "Halda"; @@ -1682,12 +1772,15 @@ /* Title label text for the status view when netP is disconnected */ "network.protection.status.view.title" = "VPN"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Network Protection on sisse lülitatud. Sinu asukoht ja võrgutegevus on kaitstud."; +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "DuckDuckGo VPN on sisse lülitatud. Teie asukoht ja võrgutegevus on kaitstud."; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Seadme liiklus suunatakse läbi %@."; +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "VPN-i peatamine on lõppenud. Seadme liiklus suunatakse läbi %@."; + /* Title for the button to link to the iOS app settings and enable notifications app-wide. */ "network.protection.turn.on.notifications.button.title" = "Lülita teavitused sisse"; @@ -1698,13 +1791,13 @@ "network.protection.vpn.about" = "Teave"; /* Section header for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.section.header" = "Notifications"; +"network.protection.vpn.alerts.section.header" = "Teavitused"; /* List section footer for the toggle for VPN alerts. */ "network.protection.vpn.alerts.toggle.section.footer" = "Saad teate, kui sinu ühendus katkeb või VPN-i olek muutub."; /* Title for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.toggle.title" = "VPN Notifications"; +"network.protection.vpn.alerts.toggle.title" = "VPN-i teavitused"; /* Title for the data volume section in the VPN status screen */ "network.protection.vpn.data-volume" = "Andmemaht"; @@ -1713,13 +1806,13 @@ "network.protection.vpn.exclude.local.networks.setting.footer" = "Suuna kohalik võrguliiklus VPN-ist mööda ja loo ühendus kohtvõrgu seadmete, nt printeriga."; /* Header text for the Exclude Local Networks setting item. */ -"network.protection.vpn.exclude.local.networks.setting.header" = "General"; +"network.protection.vpn.exclude.local.networks.setting.header" = "Üldine"; /* Title for the Exclude Local Networks setting item. */ "network.protection.vpn.exclude.local.networks.setting.title" = "Kohtvõrkude välistamine"; /* Title for the VPN Location screen's All Countries section. */ -"network.protection.vpn.location.all.countries.section.title" = "All Countries"; +"network.protection.vpn.location.all.countries.section.title" = "Kõik riigid"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.connected" = "Ühendatud asukoht"; @@ -1734,7 +1827,7 @@ "network.protection.vpn.location.recommended.section.footer" = "Loob automaatselt ühenduse lähima leitud serveriga."; /* Title for the VPN Location screen's Recommended section. */ -"network.protection.vpn.location.recommended.section.title" = "Recommended"; +"network.protection.vpn.location.recommended.section.title" = "Soovitatav"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.selected" = "Valitud asukoht"; @@ -1752,13 +1845,13 @@ "network.protection.vpn.preferred.location.nearest" = "Lähim asukoht"; /* Title for the Preferred Location VPN Settings item. */ -"network.protection.vpn.preferred.location.title" = "Preferred Location"; +"network.protection.vpn.preferred.location.title" = "Eelistatud asukoht"; /* Footer text for the DNS server setting item. */ -"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo routes DNS queries through our DNS servers so your internet provider can't see what websites you visit."; +"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo suunab DNS-päringud meie DNS-serverite kaudu, nii et sinu internetiteenuse pakkuja ei näe, milliseid veebisaite sa külastad."; /* Title for the FAQ row in the VPN status screen. */ -"network.protection.vpn.settings.faq" = "FAQs and Support"; +"network.protection.vpn.settings.faq" = "KKK ja tugi"; /* Title for the feedback row in the VPN status screen. */ "network.protection.vpn.settings.share-feedback" = "Jaga VPN-i tagasisidet"; @@ -1940,6 +2033,66 @@ /* Deactivate button */ "pm.deactivate" = "Inaktiveeri"; +/* Label for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.label" = "E-post (valikuline)"; + +/* Placeholder for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.placeholder" = "name@email.com"; + +/* Placeholder for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.placeholder" = "Palun anna meile tagasisidet..."; + +/* Title for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.title" = "Üldine tagasiside"; + +/* Title for the ITR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-itr-problem.title" = "Identity Theft Restoration"; + +/* Title for the PIR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-pir-problem.title" = "Personal Information Removal"; + +/* Title for the Subscriptions and Payments category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-ppro-problem.title" = "Tellimused ja maksed"; + +/* Placeholder for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.placeholder" = "Rääkige meile, mis toimub..."; + +/* Title for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.title" = "Teata probleemist"; + +/* Title for the VPN category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-vpn-problem.title" = "VPN"; + +/* Placeholder for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.placeholder" = "Millist funktsiooni soovid näha?"; + +/* Title for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.title" = "Funktsiooni taotlus"; + +/* Title for the category selection section in the Privacy Pro feedback form */ +"ppro.feedback-form.select-category.title" = "VALI KATEGOORIA"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-1" = "Kas leidsid probleemi, mida meie [abikeskuses](duck://) ei käsitleta? Me tahame sellest kindlasti teada saada.\n\nAnna oma e-posti aadress, kui soovid, et võtaksime sinuga selle probleemi osas ühendust (me ei pruugi kõigile probleemidele vastata):"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-2" = "Lisaks eespool sisestatud andmetele saadame koos teie tagasisidega mõned anonüümsed andmed:"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-3" = "• Kas mõned brauseri funktsioonid on aktiivsed"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-4" = "• Rakenduse koonddiagnostika (nt veakoodid)"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-5" = "Puudutades nuppu „Saada“, nõustute, et DuckDuckGo võib kasutada esitatud teavet rakenduse täiustamiseks."; + +/* Title for the text box in the Privacy Pro feedback form */ +"ppro.feedback-form.text-box.title" = "Tagasiside"; + +/* Title for the Privacy Pro feedback form */ +"ppro.feedback-form.title" = "Saada tagasisidet"; + /* Button title for sync bookmarks limits exceeded warning to go to manage bookmarks */ "prefrences.sync.bookmarks-limit-exceeded-action" = "Halda järjehoidjaid"; @@ -2029,6 +2182,18 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Lemmikud"; +/* Name of the option the user can chose to give general browser feedback */ +"send.browser.feedback.general-feedback" = "Üldine tagasiside"; + +/* Name of the option the user can chose to give browser feedback about a problem they enountered */ +"send.browser.feedback.report-problem" = "Teata probleemist"; + +/* Name of the option the user can chose to give browser feedback about a feature they would like */ +"send.browser.feedback.request-feature" = "Funktsiooni taotlemine"; + +/* Title of the picker where the user can chose the category of the feedback they want ot send. */ +"send.browser.feedback.select-category" = "VALI KATEGOORIA"; + /* Settings cell for About DDG */ "settings.about.ddg" = "DuckDuckGo'st"; @@ -2084,6 +2249,9 @@ /* Section footer Autolock description */ "settings.autolock.description" = "Kui on lubatud Touch ID, Face ID või süsteemi pääsukood, palutakse selle avamisel rakendus avada."; +/* Settings cell for Browser Feedback */ +"settings.browser.feedback" = "Brauseri tagasiside"; + /* Settings screen cell text for Automatically Clearing Data */ "settings.clear.data" = "Kustuta andmed automaatselt"; @@ -2182,7 +2350,7 @@ "settings.ppro" = "Privacy Pro"; /* Title for Link in the Footer of Privacy Pro section */ -"settings.ppro.footer" = "Privacy Policy and Terms of Service"; +"settings.ppro.footer" = "Privaatsuspoliitika ja teenuse tingimused"; /* Settings screen cell for long press previews */ "settings.previews" = "Pika vajutusega eelvaated"; @@ -2208,23 +2376,20 @@ /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Määra aadressiriba asukoht"; -/* Subscription activation pending description */ -"settings.subscription.activation.pending.description" = "This is taking longer than usual, please check back later."; +/* Privacy pro description subtitle in settings when the is activating */ +"settings.subscription.activating" = "Aktiveerimine"; -/* Subscription activation pending title */ -"settings.subscription.activation.pending.title" = "Sinu tellimus on aktiveeritud"; +/* Subscription activation pending description */ +"settings.subscription.activation.pending.description" = "See võtab tavalisest kauem aega, palun tule hiljem tagasi."; /* Data Broker protection cell subtitle for privacy pro */ -"settings.subscription.DBP.subtitle" = "Remove your info from sites that sell it"; +"settings.subscription.DBP.subtitle" = "Eemalda oma andmed saitidelt, mis neid müüvad"; /* Data Broker protection cell title for privacy pro */ "settings.subscription.DBP.title" = "Personal Information Removal"; -/* Privacy pro description subtext */ -"settings.subscription.description" = "Veel ladusam privaatsus kolme uue kaitsega:"; - /* I have a Subscription button text for privacy pro */ -"settings.subscription.existing.subscription" = "I Have a Subscription"; +"settings.subscription.existing.subscription" = "Mul on tellimus"; /* Subscription expired description */ "settings.subscription.expired.comment" = "Privacy Pro kasutamise jätkamiseks telli uuesti"; @@ -2232,23 +2397,26 @@ /* Subscription expired tittle message */ "settings.subscription.expired.title" = "Sinu Privacy Pro tellimus on aegunud"; -/* Privacy pro features list */ -"settings.subscription.features" = " • VPN\n • Personal Information Removal\n • Identity Theft Restoration"; - /* Identity theft restoration cell subtitle for privacy pro */ -"settings.subscription.ITR.subtitle" = "If your identity is stolen, we'll help restore it"; +"settings.subscription.ITR.subtitle" = "Kui sinu identiteet on varastatud, aitame selle taastada"; /* Identity theft restoration cell title for privacy pro */ "settings.subscription.ITR.title" = "Identity Theft Restoration"; /* Get Privacy Pro button text for privacy pro */ -"settings.subscription.learn.more" = "Get Privacy Pro"; +"settings.subscription.learn.more" = "Hangi Privacy Pro"; /* Subscription Settings button text for privacy pro */ -"settings.subscription.manage" = "Subscription Settings"; +"settings.subscription.manage" = "Tellimuse seaded"; + +/* Privacy Pro description subtitle in settings */ +"settings.subscription.row.description" = "Sisaldab meie VPN-i ja teenust Identity Theft Restoration."; -/* Call to action title for Privacy Pro */ -"settings.subscription.subscribe" = "Subscribe to Privacy Pro"; +/* Call to action title for Privacy Pro settings */ +"settings.subscription.subscribe" = "Kaitse oma ühendust ja identiteeti rakendusega Privacy Pro"; + +/* Privacy pro description subtitle in settings */ +"settings.subscription.us.description" = "Sisaldab meie VPN-i, rakendusi Personal Information Removal ja Identity Theft Restoration."; /* VPN cell title for privacy pro */ "settings.subscription.VPN.title" = "VPN"; @@ -2317,67 +2485,64 @@ "siteFeedback.urlPlaceholder" = "Milline veebisait on katki?"; /* Subscription Activation Info */ -"subscription.activate..header.description" = "Access your Privacy Pro subscription on this device via Apple ID or an email address."; +"subscription.activate..header.description" = "Pääse oma Privacy Pro tellimusele selles seadmes ligi Apple ID või e-posti aadressi kaudu."; /* Button for adding email address to subscription */ -"subscription.activate.add.email.button" = "Add Email"; +"subscription.activate.add.email.button" = "Lisa e-post"; /* Apple ID option for activation */ "subscription.activate.appleid" = "Apple ID"; /* Button text for restoring purchase via Apple ID */ -"subscription.activate.appleid.button" = "Restore Purchase"; +"subscription.activate.appleid.button" = "Taasta ost"; /* Description for Apple ID activation */ -"subscription.activate.appleid.description" = "Restore your purchase to activate your subscription on this device."; +"subscription.activate.appleid.description" = "Taasta oma ost, et aktiveerida oma tellimus sellel seadmel."; /* Subscription Activation Info */ -"subscription.activate.description" = "Your subscription is automatically available in DuckDuckGo on any device signed in to your Apple ID."; +"subscription.activate.description" = "Sinu tellimus on automaatselt saadaval rakenduses DuckDuckGo igas seadmes, mis on sinu Apple ID-ga sisse logitud."; /* Button for editing email address added to subscription */ -"subscription.activate.edit.email.button" = "Edit Email"; +"subscription.activate.edit.email.button" = "Muuda e-posti"; /* View Title for editing your email account */ -"subscription.activate.edit.email.title" = "Edit Email"; +"subscription.activate.edit.email.title" = "Muuda e-posti"; /* Email option for activation */ -"subscription.activate.email" = "Email"; +"subscription.activate.email" = "E-post"; /* Restore button title for Email */ -"subscription.activate.email.button" = "Enter Email"; +"subscription.activate.email.button" = "Sisesta e-posti aadress"; /* Description for Email activation */ -"subscription.activate.email.description" = "Use your email to activate your subscription on this device."; +"subscription.activate.email.description" = "Kasuta oma e-posti, et aktiveerida oma tellimus selles seadmes."; /* Activate subscription title */ "subscription.activate.email.title" = "Aktiveeri püsitellimus"; /* Button title for cancelling email deletion */ -"subscription.activate.manage.email.cancel" = "Cancel"; +"subscription.activate.manage.email.cancel" = "Tühista"; /* Button title for confirming email deletion */ "subscription.activate.manage.email.OK" = "OK"; /* Restore button title for AppleID */ -"subscription.activate.restore.apple" = "Restore Purchase"; +"subscription.activate.restore.apple" = "Taasta ost"; /* Subscription Activation Title */ -"subscription.activate.title" = "Activate your subscription on this device"; +"subscription.activate.title" = "Aktiveeri oma tellimus selles seadmes"; /* Resend activation instructions button */ -"subscription.add.device.resend.instructions" = "Resend Instructions"; +"subscription.add.device.resend.instructions" = "Saada juhised uuesti"; /* Add email to an existing subscription */ -"subscription.add.email" = "Add an email address to activate your subscription on your other devices. We’ll only use this address to verify your subscription."; +"subscription.add.email" = "Lisa e-posti aadress, et aktiveerida oma tellimus teistes seadmetes. Kasutame seda aadressi ainult sinu tellimuse kinnitamiseks."; /* View title for adding email to subscription */ -"subscription.add.email.title" = "Add Email"; - -/* Title for Alert messages */ -"subscription.alert.title" = "subscription.alert.title"; +"subscription.add.email.title" = "Lisa e-post"; /* Subscription availability message on Apple devices */ -"subscription.available.apple" = "Privacy Pro is available on any device signed in to the same Apple ID."; +"subscription.available.apple" = "Privacy Pro on saadaval igas seadmes, mis on sisse logitud sama Apple ID-ga."; /* Text for the manage billing page */ "subscription.billing.google.text" = "Sinu püsitellimus osteti Google Play poe kaudu. Püsitellimuse pikendamiseks ava Google Play poe tellimuse seaded seadmes, mis on sisse logitud samasse Google'i kontosse, mida kasutati algselt püsitellimuse ostmiseks."; @@ -2385,89 +2550,86 @@ /* Title for the manage billing page */ "subscription.billing.google.title" = "Püsitellimuse plaanid"; -/* Subscription annual billing period type */ -"subscription.billing.period.annual" = "annual"; - -/* Subscription monthly billing period type */ -"subscription.billing.period.monthly" = "monthly"; - /* Subscription Removal confirmation message */ -"subscription.cancel.message" = "Your subscription has been removed from this device."; +"subscription.cancel.message" = "Sinu tellimus on sellest seadmest eemaldatud."; /* Change plan or cancel title */ -"subscription.change.plan" = "Update Plan or Cancel"; +"subscription.change.plan" = "Uuenda plaani või loobu"; /* Navigation Button for closing subscription view */ -"subscription.close" = "Close"; +"subscription.close" = "Sulge"; /* Title for Confirm messages */ -"subscription.confirm.title" = "Are you sure?"; +"subscription.confirm.title" = "Oled sa kindel?"; /* Header for section for activating subscription on other devices */ -"subscription.devices.header" = "Activate on Other Devices"; +"subscription.devices.header" = "Aktiveeri teistes seadmetes"; /* Footer for section for activating subscription on other devices when email was not yet added */ -"subscription.devices.no.email.footer" = "Add an optional email to your subscription or use your Apple ID to access Privacy Pro on other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.no.email.footer" = "Lisa oma tellimusele valikuline e-posti aadress, et pääseda Privacy Pro'le ligi teistes seadmetes. **[Lisateave](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Footer for section for activating subscription on other devices when email is added */ -"subscription.devices.with.email.footer" = "Use this email to activate your subscription in Settings > Privacy Pro in the DuckDuckGo app on your other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.with.email.footer" = "Kasuta seda e-kirja, et aktiveerida oma tellimus jaotises Seaded > Privacy Pro rakenduses DuckDuckGo oma teistes seadmetes. **[Lisateave](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Alert content for not found subscription */ -"subscription.email.inactive.alert.message" = "The subscription associated with this email is no longer active."; +"subscription.email.inactive.alert.message" = "Selle e-posti aadressiga seotud tellimus ei ole enam aktiivne."; /* Alert title for not found subscription */ -"subscription.email.inactive.alert.title" = "Subscription Not Found"; +"subscription.email.inactive.alert.title" = "Tellimust ei leitud"; /* Alert content for not found subscription */ -"subscription.expired.alert.message" = "The subscription associated with this Apple ID is no longer active."; +"subscription.expired.alert.message" = "Selle Apple ID-ga seotud tellimus ei ole enam aktiivne."; /* FAQ Button */ -"subscription.faq" = "FAQs and Support"; +"subscription.faq" = "KKK ja tugi"; /* FAQ Description */ -"subscription.faq.description" = "Get answers to frequently asked questions or contact Privacy Pro support from our help pages."; +"subscription.faq.description" = "Saa vastused korduma kippuvatele küsimustele või võta ühendust Privacy Pro klienditoega meie abilehtedelt."; + +/* Send Feedback Button */ +"subscription.feedback" = "Saada tagasisidet"; /* Cancel action for the existing subscription dialog */ -"subscription.found.cancel" = "Cancel"; +"subscription.found.cancel" = "Tühista"; /* Restore action for the existing subscription dialog */ -"subscription.found.restore" = "Restore"; +"subscription.found.restore" = "Taasta"; /* Message for the existing subscription dialog */ -"subscription.found.text" = "We found a subscription associated with this Apple ID."; +"subscription.found.text" = "Leidsime selle Apple ID-ga seotud tellimuse."; /* Title for the existing subscription dialog */ -"subscription.found.title" = "Subscription Found"; +"subscription.found.title" = "Tellimus leitud"; /* Help and support Section header */ -"subscription.help" = "Help and support"; +"subscription.help" = "Abi ja tugi"; /* Manage Plan header */ -"subscription.manage.plan" = "Manage Plan"; +"subscription.manage.plan" = "Halda plaani"; /* Header for the subscription section */ -"subscription.manage.title" = "Subscription"; +"subscription.manage.title" = "Püsitellimus"; /* Alert content for not found subscription */ -"subscription.notFound.alert.message" = "There is no subscription associated with this Apple ID."; +"subscription.notFound.alert.message" = "Selle Apple ID-ga ei ole seotud ühtegi tellimust."; /* Alert title for not found subscription */ -"subscription.notFound.alert.title" = "Subscription Not Found"; +"subscription.notFound.alert.title" = "Tellimust ei leitud"; /* View plans button text */ -"subscription.notFound.view.plans" = "View Plans"; +"subscription.notFound.view.plans" = "Kuva plaane"; /* Hero Text for Personal information removal */ -"subscription.pir.hero" = "Activate Privacy Pro on desktop to set up Personal Information Removal"; +"subscription.pir.hero" = "Personal Information Removal'i seadistamiseks aktiveerige töölaual Privacy Pro"; /* Description on how to use Personal information removal in desktop. The first placeholder references a location in the Desktop application. Privacy Pro>, and the second, the menu entry. i.e. */ -"subscription.pir.heroText" = "In the DuckDuckGo browser for desktop, go to %1$@ and click %2$@ to get started."; +"subscription.pir.heroText" = "Mine töölaua DuckDuckGo brauseris jaotisse %1$@ ja klõpsa %2$@, et alustada."; /* Settings references a menu in the Desktop app, Privacy Pro, references our product name */ -"subscription.pir.heroTextLocation" = "Settings > Privacy Pro"; +"subscription.pir.heroTextLocation" = "Seaded > Privacy Pro"; /* Menu item for enabling Personal Information Removal on Desktop */ -"subscription.pir.heroTextMenyEntry" = "I have a subscription"; +"subscription.pir.heroTextMenyEntry" = "Mul on tellimus"; /* Text for the 'macOS' button */ "subscription.pir.macos" = "Mac"; @@ -2476,28 +2638,28 @@ "subscription.pir.windows" = "Windows"; /* Progress view title when completing the purchase */ -"subscription.progress.view.completing.purchase" = "Completing purchase..."; +"subscription.progress.view.completing.purchase" = "Ostu sooritamine..."; /* Progress view title when starting the purchase */ -"subscription.progress.view.purchasing.subscription" = "Purchase in progress..."; +"subscription.progress.view.purchasing.subscription" = "Ostmine on käimas..."; /* Progress view title when restoring past subscription purchase */ -"subscription.progress.view.restoring.subscription" = "Restoring subscription..."; +"subscription.progress.view.restoring.subscription" = "Tellimuse taastamine..."; /* Remove from this device button */ -"subscription.remove.from.device.button" = "Remove From This Device"; +"subscription.remove.from.device.button" = "Eemalda sellest seadmest"; /* Remove from device confirmation dialog text */ -"subscription.remove.from.device.text" = "You will no longer be able to access your Privacy Pro subscription on this device. This will not cancel your subscription, and it will remain active on your other devices."; +"subscription.remove.from.device.text" = "Sa ei pääse enam oma Privacy Pro tellimusele selles seadmes ligi. See ei tühista su tellimust ja see jääb teistel seadmetel aktiivseks."; /* Remove from device confirmation dialog title */ -"subscription.remove.from.device.title" = "Remove from this device?"; +"subscription.remove.from.device.title" = "Kas soovid sellest seadmest eemaldada?"; /* Remove subscription button text */ -"subscription.remove.subscription" = "Remove Subscription"; +"subscription.remove.subscription" = "Eemalda tellimus"; /* Remove subscription cancel button text */ -"subscription.remove.subscription.cancel" = "Cancel"; +"subscription.remove.subscription.cancel" = "Tühista"; /* Button text for general error message */ "subscription.restore.backend.error.button" = "Tagasi seadete juurde"; @@ -2509,28 +2671,43 @@ "subscription.restore.backend.error.title" = "Midagi läks valesti"; /* Alert for general error message */ -"subscription.restore.general.error.message" = "The App Store was unable to process your purchase. Please try again later."; +"subscription.restore.general.error.message" = "App Store ei suutnud su ostu töödelda. Proovi hiljem uuesti."; /* Alert for general error title */ -"subscription.restore.general.error.title" = "Something Went Wrong"; +"subscription.restore.general.error.title" = "Midagi läks valesti"; /* Alert button text for restored purchase alert */ "subscription.restore.success.alert.button" = "OK"; /* Alert message for restored purchase */ -"subscription.restore.success.alert.message" = "Your purchases have been restored."; +"subscription.restore.success.alert.message" = "Sinu ostud on taastatud."; /* Alert title for restored purchase */ -"subscription.restore.success.alert.title" = "You’re all set."; +"subscription.restore.success.alert.title" = "Sa oled valmis."; + +/* Subtitle in header when subscribed */ +"subscription.subscribed" = "Tellitud"; /* Subscription Expired Data. This reads as 'Your subscription expired on (date)' */ "subscription.subscription.expired.caption" = "Sinu tellimus lõppes %@"; -/* Subscription expiration info. This reads as 'Your (monthly or annual) subscription expires on (date)' */ -"subscription.subscription.expiring.caption" = "Your %1$@ subscription expires on %2$@."; +/* Monthly subscription expiration info where parameter is expiration date. This reads as 'Your monthly subscription expires on (date)' */ +"subscription.subscription.expiring.monthly.caption" = "Teie igakuine tellimus aegub %@."; + +/* Unknown period subscription expiration info where parameter is expiration date. This reads as 'Your subscription expires on (date)' */ +"subscription.subscription.expiring.unknown.caption" = "Teie tellimus aegub %@."; + +/* Annual subscription expiration info where parameter is expiration date. This reads as 'Your annual subscription expires on (date)' */ +"subscription.subscription.expiring.yearly.caption" = "Teie aastane tellimus aegub %@."; + +/* Monthly subscription renewal info where parameter is renewal date. This reads as 'Your monthly subscription renews on (date)' */ +"subscription.subscription.renewing.monthly.caption" = "Teie kuutellimus uueneb %@."; + +/* Unknown period subscription renewal info where parameter is renewal date. This reads as 'Your subscription renews on (date)' */ +"subscription.subscription.renewing.unknown.caption" = "Teie tellimus uueneb %@."; -/* Subscription renewal info. This reads as 'Your (monthly or annual) subscription renews on (date)' */ -"subscription.subscription.renewing.caption" = "Your %1$@ subscription renews on %2$@."; +/* Annual subscription renewal info where parameter is renewal date. This reads as 'Your annual subscription renews on (date)' */ +"subscription.subscription.renewing.yearly.caption" = "Teie aastane tellimus uueneb %@."; /* Navigation bar Title for subscriptions */ "subscription.title" = "Privacy Pro"; @@ -2677,118 +2854,118 @@ "voiceSearch.footer.note.old" = "Heli töödeldakse seadmes. Seda ei salvestata ega jagata kellegagi, sealhulgas DuckDuckGoga."; /* Cancel action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.cancel" = "Dismiss"; +"vpn.access-revoked.alert.action.cancel" = "Loobu"; /* Primary action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.subscribe" = "Subscribe"; +"vpn.access-revoked.alert.action.subscribe" = "Telli"; /* Alert message for the alert when the Privacy Pro subscription expiress */ -"vpn.access-revoked.alert.message" = "Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"vpn.access-revoked.alert.message" = "DuckDuckGo VPN-i taasühendamiseks telli Privacy Pro."; /* Alert title for the alert when the Privacy Pro subscription expires */ -"vpn.access-revoked.alert.title" = "VPN disconnected due to expired subscription"; +"vpn.access-revoked.alert.title" = "VPN-ühendus katkestati aegunud tellimuse tõttu"; /* Title for the VPN widget onboarding screen */ "vpn.addWidget.settings.title" = "Leia ja vali DuckDuckGo. Seejärel pühi VPN-ile ja vali Lisa vidin."; /* Title for the Cancel button of the VPN feedback form */ -"vpn.feedback-form.button.cancel" = "Cancel"; +"vpn.feedback-form.button.cancel" = "Tühista"; /* Title for the Done button of the VPN feedback form */ -"vpn.feedback-form.button.done" = "Done"; +"vpn.feedback-form.button.done" = "Valmis"; /* Title for the Submit button of the VPN feedback form */ -"vpn.feedback-form.button.submit" = "Submit"; +"vpn.feedback-form.button.submit" = "Esita"; /* Title for the Submitting state of the VPN feedback form */ -"vpn.feedback-form.button.submitting" = "Submitting…"; +"vpn.feedback-form.button.submitting" = "Esitamine..."; /* Title for the browser crash/freeze category of the VPN feedback form */ -"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN causes browser to crash or freeze"; +"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN põhjustab veebilehitseja kokkujooksmise või hangumise"; /* Title for the 'VPN fails to connect' category of the VPN feedback form */ -"vpn.feedback-form.category.fails-to-connect" = "VPN fails to connect"; +"vpn.feedback-form.category.fails-to-connect" = "VPN ei suuda ühendust luua"; /* Title for the 'VPN feature request' category of the VPN feedback form */ -"vpn.feedback-form.category.feature-request" = "VPN feature request"; +"vpn.feedback-form.category.feature-request" = "VPN funktsiooni soov"; /* Title for the category 'VPN causes issues with other apps or websites' category of the VPN feedback form */ -"vpn.feedback-form.category.issues-with-apps" = "VPN causes issues with other apps or websites"; +"vpn.feedback-form.category.issues-with-apps" = "VPN tekitab probleeme teiste rakenduste või veebisaitidega"; /* Title for the local device connectivity category of the VPN feedback form */ -"vpn.feedback-form.category.local-device-connectivity" = "VPN won't let me connect to local device"; +"vpn.feedback-form.category.local-device-connectivity" = "VPN ei lase mul kohaliku seadmega ühenduda"; /* Title for the 'other VPN feedback' category of the VPN feedback form */ -"vpn.feedback-form.category.other" = "Other VPN feedback"; +"vpn.feedback-form.category.other" = "Muu VPN-i tagasiside"; /* Title for the category selection state of the VPN feedback form */ -"vpn.feedback-form.category.select-category" = "Select a category"; +"vpn.feedback-form.category.select-category" = "VALI KATEGOORIA"; /* Title for the 'VPN is too slow' category of the VPN feedback form */ -"vpn.feedback-form.category.too-slow" = "VPN connection is too slow"; +"vpn.feedback-form.category.too-slow" = "VPN-ühendus on liiga aeglane"; /* Title for the 'unable to install' category of the VPN feedback form */ -"vpn.feedback-form.category.unable-to-install" = "Unable to install VPN"; +"vpn.feedback-form.category.unable-to-install" = "VPN-i ei saa paigaldada"; /* Title for the feedback sent view description of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.description" = "Your feedback will help us improve the\nDuckDuckGo VPN."; +"vpn.feedback-form.sending-confirmation.description" = "Sinu tagasiside aitab meil \nDuckDuckGo VPN-i parandada."; /* Title for the feedback sending error text of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.error" = "We couldn't send your feedback right now, please try again."; +"vpn.feedback-form.sending-confirmation.error" = "Me ei saanud su tagasisidet praegu saata, palun proovi uuesti."; /* Title for the feedback sent view title of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.title" = "Thank you!"; +"vpn.feedback-form.sending-confirmation.title" = "Aitäh!"; /* Toast message when the VPN feedback form is submitted successfully */ -"vpn.feedback-form.submitted.message" = "Thank You! Feedback submitted."; +"vpn.feedback-form.submitted.message" = "Aitäh! Tagasiside on esitatud."; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-1" = "Please describe what's happening, what you expected to happen, and the steps that led to the issue:"; +"vpn.feedback-form.text-1" = "Palun kirjelda, mis toimub, mida sa ootasid ja millised sammud viisid probleemi tekkimiseni."; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-2" = "In addition to the details entered into this form, your app issue report will contain:"; +"vpn.feedback-form.text-2" = "Lisaks sellele vormile sisestatud andmetele sisaldab sinu rakenduse probleemi aruanne järgmist:"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-3" = "• Whether specific DuckDuckGo features are enabled"; +"vpn.feedback-form.text-3" = "• Kas teatud DuckDuckGo funktsioonid on lubatud"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-4" = "• Aggregate DuckDuckGo app diagnostics"; +"vpn.feedback-form.text-4" = "• Koguge DuckDuckGo rakenduse diagnostika"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-5" = "By tapping \"Submit\" I agree that DuckDuckGo may use the information in this report for purposes of improving the app's features."; +"vpn.feedback-form.text-5" = "Puudutades nuppu „Saada“, nõustun, et DuckDuckGo võib kasutada selles aruandes sisalduvat teavet rakenduse funktsioonide täiustamiseks."; /* Title for each screen of the VPN feedback form */ -"vpn.feedback-form.title" = "Help Improve the DuckDuckGo VPN"; +"vpn.feedback-form.title" = "Aita parandada DuckDuckGo VPN-i"; /* VPN settings screen cell text for adding the VPN widget to the home screen */ "vpn.settings.add.widget" = "Lisa VPN-i vidin avakuvale"; /* Disclaimer for the DNS Server section on the DNS Server screen */ -"vpn.settings.dns.section-disclaimer" = "Using a custom DNS server can impact browsing speeds and expose your activity to third parties if the server isn't secure or reliable."; +"vpn.settings.dns.section-disclaimer" = "Kohandatud DNS-serveri kasutamine võib mõjutada sirvimise kiirust ja paljastada su tegevuse kolmandatele osapooltele, kui server ei ole turvaline või usaldusväärne."; /* Header text for the DNS section on the VPN Settings screen */ "vpn.settings.dns.section-header" = "DNS"; /* Title for the Apply button on the DNS Server setting screen */ -"vpn.settings.dns.server.apply.button.title" = "Apply"; +"vpn.settings.dns.server.apply.button.title" = "Rakenda"; /* Default value for the DNS Server row on the VPN Settings screen */ "vpn.settings.dns.server.default.value" = "DuckDuckGo"; /* Title for the IPv4 Address setting */ -"vpn.settings.dns.server.ipv4.title" = "IPv4 Address"; +"vpn.settings.dns.server.ipv4.title" = "IPv4 aadress"; /* Custom option for the DNS Server setting */ -"vpn.settings.dns.server.option.custom" = "Custom"; +"vpn.settings.dns.server.option.custom" = "Kohandatud"; /* Recommended option for the DNS Server setting */ -"vpn.settings.dns.server.option.default" = "DuckDuckGo (Recommended)"; +"vpn.settings.dns.server.option.default" = "DuckDuckGo (soovitatav)"; /* Title for the DNS Server setting screen */ -"vpn.settings.dns.server.screen.title" = "DNS Server"; +"vpn.settings.dns.server.screen.title" = "DNS server"; /* Title for the DNS Server row on the VPN Settings screen */ -"vpn.settings.dns.server.title" = "DNS Server"; +"vpn.settings.dns.server.title" = "DNS server"; /* Title for the button to enable push notifications in system settings */ "waitlist.allow-notifications" = "Luba teavitused"; diff --git a/DuckDuckGo/et.lproj/OmniBar.strings b/DuckDuckGo/et.lproj/OmniBar.strings index d8e0b24e17..d8872df518 100644 --- a/DuckDuckGo/et.lproj/OmniBar.strings +++ b/DuckDuckGo/et.lproj/OmniBar.strings @@ -7,6 +7,9 @@ /* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "Dt8-We-5tV"; */ "Dt8-We-5tV.accessibilityLabel" = "Kustuta tekst"; +/* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "gb5-wL-4OW"; */ +"gb5-wL-4OW.accessibilityLabel" = "Kustuta tekst"; + /* Class = "UIButton"; accessibilityLabel = "Share"; ObjectID = "hor-Jd-3kw"; */ "hor-Jd-3kw.accessibilityLabel" = "Jaga"; diff --git a/DuckDuckGo/et.lproj/Settings.strings b/DuckDuckGo/et.lproj/Settings.strings index 1aaa03a0a4..64175d113a 100644 --- a/DuckDuckGo/et.lproj/Settings.strings +++ b/DuckDuckGo/et.lproj/Settings.strings @@ -10,9 +10,6 @@ /* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "5YD-Od-uDU"; */ "5YD-Od-uDU.text" = "Kustuta andmed automaatselt"; -/* Class = "UIButton"; normalTitle = "Settings"; ObjectID = "8YG-7Q-Czd"; */ -"8YG-7Q-Czd.normalTitle" = "Seaded"; - /* Class = "UILabel"; text = "App Exit, Inactive for 1 Hour"; ObjectID = "9EC-lx-77p"; */ "9EC-lx-77p.text" = "Rakendusest väljumisel, 1-tunnise passiivsuse järel"; @@ -76,9 +73,6 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Kaitseta saidid"; -/* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ -"ssa-zd-L3T.title" = "Teksti suurus"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Näita klaviatuuri"; diff --git a/DuckDuckGo/fi.lproj/DaxOnboarding.strings b/DuckDuckGo/fi.lproj/DaxOnboarding.strings index a92183ed63..304148923f 100644 --- a/DuckDuckGo/fi.lproj/DaxOnboarding.strings +++ b/DuckDuckGo/fi.lproj/DaxOnboarding.strings @@ -1,12 +1,6 @@ /* Class = "UIButton"; normalTitle = "Hide"; ObjectID = "hgy-75-OvJ"; */ "hgy-75-OvJ.normalTitle" = "Piilota"; -/* Class = "UILabel"; text = "Welcome to\nDuckDuckGo!"; ObjectID = "Lvy-9G-3gr"; */ -"Lvy-9G-3gr.text" = "Tervetuloa\nDuckDuckGo-sovellukseen!"; - -/* Class = "UIButton"; normalTitle = "Let’s Do It!"; ObjectID = "oAv-Zt-uIt"; */ -"oAv-Zt-uIt.normalTitle" = "Aloitetaan!"; - /* Class = "UIButton"; normalTitle = "Button"; ObjectID = "zJt-F9-L48"; */ "zJt-F9-L48.normalTitle" = "Painike"; diff --git a/DuckDuckGo/fi.lproj/Localizable.strings b/DuckDuckGo/fi.lproj/Localizable.strings index f4793f560b..8b910b5bff 100644 --- a/DuckDuckGo/fi.lproj/Localizable.strings +++ b/DuckDuckGo/fi.lproj/Localizable.strings @@ -341,7 +341,7 @@ "autofill.keep-enabled.alert.disable" = "Poista Käytöstä"; /* A link that takes the user to the DuckDuckGo help pages explaining password managers */ -"autofill.learn.more.link.title" = "Learn More"; +"autofill.learn.more.link.title" = "Lue lisää"; /* Button displayed after saving/updating an autofill login that takes the user to the saved login */ "autofill.login-save-action-button.toast" = "Näytä"; @@ -977,9 +977,6 @@ /* ad = advertisment */ "dax.onboarding.home.subsequent" = "Hyvin menee!\n\nMuista, että joka kerta kun käytät minua selaamiseen, rasittavat mainokset katoavat. 👍"; -/* No comment provided by engineer. */ -"dax.onboarding.message" = "Internet voi olla pelottava paikka.\n\nMutta ei hätää! Yksityinen haku ja selaaminen on helpompaa kuin luulet."; - /* No comment provided by engineer. */ "Debug" = "Virheenkorjaus"; @@ -1080,7 +1077,7 @@ "duckPlayer.never.label" = "Ei koskaan"; /* Body text for the modal feature explanation */ -"duckplayer.presentation.modal.body" = "Duck Playerin avulla voit katsella YouTubea ilman kohdennettuja mainoksia teatterimaisessa kokemuksessa DuckDuckGossa, eivätkä katsomasi videot vaikuta suosituksiisi."; +"duckplayer.presentation.modal.body" = "Duck Playerin avulla voit katsella YouTubea ilman kohdennettuja mainoksia DuckDuckGossa, eivätkä katsomasi videot vaikuta suosituksiisi."; /* Button that will dismiss the modal */ "duckplayer.presentation.modal.dismiss-button" = "Selvä!"; @@ -1088,17 +1085,23 @@ /* Two line title (separated by \n) for the feature explanation */ "duckplayer.presentation.modal.title" = "Hukutko mainoksiin YouTubessa? Kokeile Duck Playeria!"; +/* Two line title (separated by \n) for the feature explanation */ +"duckplayer.presentation.modal.title.youtube" = "Hukutko mainoksiin YouTubessa? Duck Playerin avulla et huku!"; + /* Footer label in the settings screen for Duck Player */ "duckplayer.settings.footer" = "DuckDuckGo tarjoaa kaikki Privacy Essentials ominaisuudet, joita tarvitset suojautuaksesi selatessasi verkkoa."; /* Text explaining what Duck Player is in the settings screen. */ -"duckplayer.settings.info-text" = "Duck Player tarjoaa puhtaan katselukokemuksen ilman kohdennettuja mainoksia ja estää katseluhistoriaa vaikuttamasta YouTube-suosituksiisi."; +"duckplayer.settings.info-text" = "Duck Playerin avulla voit katsella YouTubea ilman kohdennettuja mainoksia DuckDuckGossa, eivätkä katsomasi videot vaikuta suosituksiisi."; /* Button that takes the user to learn more about Duck Player. */ "duckplayer.settings.learn-more" = "Lue lisää"; +/* Settings screen cell text for DuckPlayer settings to open in new tab */ +"duckplayer.settings.open-new-tab-label" = "Avaa Duck Player uudessa välilehdessä"; + /* Settings screen cell text for DuckPlayer settings */ -"duckplayer.settings.open-videos-in" = "Avaa videot Duck Playerissa"; +"duckplayer.settings.open-videos-in" = "Avaa YouTube-videot Duck Playerissa"; /* Settings screen cell text for DuckPlayer settings */ "duckplayer.settings.title" = "Duck Player"; @@ -1169,9 +1172,6 @@ /* Title for prompt to sign up for email protection */ "email.signup-prompt.title" = "Piilota sähköpostisi ja\nEstä seurantaohjelmat"; -/* No comment provided by engineer. */ -"empty!" = "tyhjä!"; - /* Empty list state placholder */ "empty.bookmarks" = "Kirjanmerkkejä ei ole vielä lisätty"; @@ -1322,6 +1322,39 @@ /* Confirmation button */ "feedback.form.submit" = "Lähetä"; +/* Title of the feedback form */ +"feedback.form.title" = "Auta parantamaan Privacy Prota"; + +/* Category for Identity Theft Restoration feedback */ +"feedback.general.category.itr" = "Identity Theft Restoration"; + +/* Category for Personal Info Removal feedback */ +"feedback.general.category.pir" = "Henkilötietojen poistaminen"; + +/* Category for subscription and payments feedback */ +"feedback.general.category.ppro" = "Tilaus ja maksut"; + +/* Prompt to select a category for general feedback */ +"feedback.general.category.select" = "VALITSE KATEGORIA"; + +/* Category for VPN feedback */ +"feedback.general.category.vpn" = "VPN"; + +/* Category for access code issues */ +"feedback.itr.category.access-code" = "Ongelma pääsykoodin kanssa"; + +/* Category for issues contacting an advisor */ +"feedback.itr.category.cant-contact-advisor" = "Yhteydenotto neuvonantajaan ei onnistu"; + +/* Prompt to select a category for Identity Theft Restoration feedback */ +"feedback.itr.category.select" = "VALITSE KATEGORIA"; + +/* Category for other Identity Theft Restoration issues */ +"feedback.itr.category.something-else" = "Jotain muuta"; + +/* Category for unhelpful advisor calls */ +"feedback.itr.category.unhelpful" = "Soitto neuvonantajalle ei ollut hyödyllinen"; + /* No comment provided by engineer. */ "feedback.negative.form.genericPlaceholder" = "Kerro mahdollisimman tarkasti"; @@ -1361,6 +1394,24 @@ /* No comment provided by engineer. */ "feedback.performance.slowLoading" = "Verkkosivut tai hakutulokset latautuvat hitaasti"; +/* Category for when scan finds incorrect records */ +"feedback.pir.category.not-me" = "Skannaus löysi tietueita, jotka eivät liity minuun"; + +/* Category for when scan doesn't find info on a specific site */ +"feedback.pir.category.nothing-on-site" = "Skannaus ei löytänyt tietojani tietyltä sivustolta"; + +/* Category for other Personal Info Removal issues */ +"feedback.pir.category.other" = "Jotain muuta"; + +/* Category for when the removal process is stuck */ +"feedback.pir.category.removal-stuck" = "Poistoprosessi on jumissa"; + +/* Category for when the scan is stuck */ +"feedback.pir.category.scan-stuck" = "Tietueiden skannaus on jumissa"; + +/* Prompt to select a category for Personal Info Removal feedback */ +"feedback.pir.category.select" = "VALITSE KATEGORIA"; + /* Header above input field */ "feedback.positive.form.header" = "Kerro tarkempia tietoja"; @@ -1379,6 +1430,15 @@ /* Button encouraging uses to share details aboout their feedback */ "feedback.positive.submit" = "Kerro tarkempia tietoja"; +/* Category for other Privacy Pro issues */ +"feedback.ppro.category.other" = "Jotain muuta"; + +/* Category for one-time password issues */ +"feedback.ppro.category.otp" = "Ongelma kertakäyttöisen salasanan kanssa"; + +/* Prompt to select a category for Privacy Pro feedback */ +"feedback.ppro.category.select" = "VALITSE KATEGORIA"; + /* No comment provided by engineer. */ "feedback.start.footer" = "Anonyymi palautteesi on meille tärkeää."; @@ -1529,12 +1589,6 @@ /* No comment provided by engineer. */ "keyCommandShowAllTabs" = "Näytä kaikki välilehdet"; -/* Please preserve newline character */ -"launchscreenWelcomeMessage" = "Tervetuloa\nDuckDuckGo-sovellukseen!"; - -/* No comment provided by engineer. */ -"LOREM IPSUM" = "LOREM IPSUM"; - /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "Macin DuckDuckGo tarjoaa kaipaamasi nopeuden, odottamasi selaintoiminnot ja luokkansa parhaat tietosuojaominaisuudet."; @@ -1586,62 +1640,83 @@ /* Edit button */ "navigation.title.edit" = "Muokkaa"; -/* String indicating NetP is connected when viewed from the settings screen */ -"netP.cell.connected" = "Connected"; - -/* String indicating NetP is disconnected when viewed from the settings screen */ -"netP.cell.disconnected" = "Not connected"; - -/* Title for the DuckDuckGo VPN feature in settings */ -"netP.settings.title" = "VPN"; +/* String indicating NetP is snoozing when viewed from the settings screen */ +"netP.cell.snoozing" = "Lepotila"; /* Title for the DuckDuckGo VPN feature */ "netP.title" = "DuckDuckGo VPN"; +/* Button title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.action" = "Lisää widget"; + +/* Message for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.message" = "Kytke VPN päälle ja pois päältä suoraan aloitusnäytöstä."; + +/* Title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.title" = "Lisää VPN-widget"; + /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN katkaistu vanhentuneen tilauksen takia. Tilaa Privacy Pro, jotta voit yhdistää DuckDuckGo VPN:n uudelleen."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Network Protection ei onnistunut muodostamaan yhteyttä. Yritä myöhemmin uudelleen."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "DuckDuckGo VPN ei onnistunut muodostamaan yhteyttä. Yritä myöhemmin uudelleen."; /* Title for the VPN FAQ screen. */ "network.protection.faq.title" = "DuckDuckGo VPN – usein kysytyt kysymykset"; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Network Protection keskeytettiin. Yritetään muodostaa yhteys uudelleen..."; +/* Message for tooltip about geoswitching */ +"network.protection.geoswitching.tip.message" = "Voit mukauttaa VPN-sijaintisi muodostamalla yhteyden mihin tahansa palvelimistamme maailmanlaajuisesti."; + +/* Title for tooltip about geoswitching */ +"network.protection.geoswitching.tip.title" = "Vaihda sijaintisi"; + +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "DuckDuckGo VPN keskeytyi. Yritetään muodostaa yhteys uudelleen..."; /* Message for the network protection invite dialog */ -"network.protection.invite.dialog.message" = "Enter your invite code to get started."; +"network.protection.invite.dialog.message" = "Aloita antamalla kutsukoodisi."; /* Title for the network protection invite screen */ "network.protection.invite.dialog.title" = "Sinut on kutsuttu kokeilemaan DuckDuckGo VPN:ää"; /* Prompt for the network protection invite code text field */ -"network.protection.invite.field.prompt" = "Invite Code"; +"network.protection.invite.field.prompt" = "Kutsukoodi"; /* Message for the network protection invite success view */ -"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; +"network.protection.invite.success.message" = "Piilota sijaintisi verkkosivustoilta ja verkkotoimintasi internetpalveluntarjoajilta ja muilta verkossasi olevilta."; /* Title for the network protection invite success view */ -"network.protection.invite.success.title" = "Success! You’re in."; +"network.protection.invite.success.title" = "Onnistui! Olet mukana."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; /* Title text for an iOS quick action that opens VPN settings */ "network.protection.quick-action.open-vpn" = "Avaa VPN"; +/* Button title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.action" = "Lue lisää"; + +/* Message for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.message" = "Voit käyttää sivustoja tai sovelluksia, jotka estävät VPN-liikenteen torkuttamalla VPN-yhteyttä."; + +/* Title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.title" = "Vältä VPN-konfliktit"; + +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN on lepotilassa %@"; + /* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ "network.protection.status.connected.format" = "Yhdistetty · %@"; /* The label for the NetP VPN when connecting */ -"network.protection.status.connecting" = "Connecting..."; +"network.protection.status.connecting" = "Yhdistetään..."; /* The label for the NetP VPN when disconnected */ -"network.protection.status.disconnected" = "Not connected"; +"network.protection.status.disconnected" = "Ei yhteyttä"; /* The label for the NetP VPN when disconnecting */ -"network.protection.status.disconnecting" = "Disconnecting..."; +"network.protection.status.disconnecting" = "Katkaistaan yhteys..."; /* Message label text for the status view when VPN is disconnected */ "network.protection.status.header.message.off" = "Yhdistä suojataksesi laitteesi\nkoko verkkoliikenne."; @@ -1655,26 +1730,41 @@ /* Header title label text for the status view when VPN is connected */ "network.protection.status.header.title.on" = "DuckDuckGo VPN on päällä"; +/* Header title label text for the status view when VPN is snoozing */ +"network.protection.status.header.title.snoozed" = "DuckDuckGo VPN on lepotilassa"; + /* The status view 'Share Feedback' button which is shown inline on the status view after the temporary free use footer text */ -"network.protection.status.menu.share.feedback" = "Share Feedback"; +"network.protection.status.menu.share.feedback" = "Jaa palaute"; + +/* The label for the NetP VPN when paused */ +"network.protection.status.paused" = "Keskeytetty"; + +/* The label for when NetP VPN is snoozing plus the length of time remaining formatted as '0:00' */ +"network.protection.status.snoozing.format" = "Lepotila, %@ jäljellä"; + +/* Snooze button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.snooze" = "Aseta lepotilaan 20 minuutiksi"; + +/* Wake Up button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.wake-up" = "Herätä"; /* Connection details label shown in NetworkProtection's status view. */ -"network.protection.status.view.connection.details" = "Connection Details"; +"network.protection.status.view.connection.details" = "Yhteyden tiedot"; /* Custom DNS label shown in NetworkProtection's status view. */ -"network.protection.status.view.custom.dns" = "DNS Server"; +"network.protection.status.view.custom.dns" = "DNS-palvelin"; /* Generic connection failed error message shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.message" = "Please try again later."; +"network.protection.status.view.error.connection.failed.message" = "Yritä myöhemmin uudelleen."; /* Generic connection failed error title shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; +"network.protection.status.view.error.connection.failed.title" = "Yhteyden muodostaminen epäonnistui."; /* IP Address label shown in NetworkProtection's status view. */ -"network.protection.status.view.ip.address" = "IP Address"; +"network.protection.status.view.ip.address" = "IP-osoite"; /* Location label shown in NetworkProtection's status view. */ -"network.protection.status.view.location" = "Location"; +"network.protection.status.view.location" = "Sijainti"; /* Label shown on the title of the settings section in NetworkProtection's status view. */ "network.protection.status.view.settings.section.title" = "Hallitse"; @@ -1682,12 +1772,15 @@ /* Title label text for the status view when netP is disconnected */ "network.protection.status.view.title" = "VPN"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Network Protection on käytössä. Sijaintisi ja verkkotoimintasi on suojattu."; +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "DuckDuckGo VPN on päällä. Sijaintisi ja verkkotoimintasi on suojattu."; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Laitteen liikenne reititetään %@ kautta."; +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "VPN-lepotila on päättynyt. Laitteen liikenne reititetään %@ kautta."; + /* Title for the button to link to the iOS app settings and enable notifications app-wide. */ "network.protection.turn.on.notifications.button.title" = "Ota ilmoitukset käyttöön"; @@ -1698,13 +1791,13 @@ "network.protection.vpn.about" = "Tietoja"; /* Section header for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.section.header" = "Notifications"; +"network.protection.vpn.alerts.section.header" = "Ilmoitukset"; /* List section footer for the toggle for VPN alerts. */ "network.protection.vpn.alerts.toggle.section.footer" = "Saat ilmoituksen, jos yhteytesi katkeaa tai VPN:n tila muuttuu."; /* Title for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.toggle.title" = "VPN Notifications"; +"network.protection.vpn.alerts.toggle.title" = "VPN-ilmoitukset"; /* Title for the data volume section in the VPN status screen */ "network.protection.vpn.data-volume" = "Datamäärä"; @@ -1713,13 +1806,13 @@ "network.protection.vpn.exclude.local.networks.setting.footer" = "Anna paikallisen liikenteen ohittaa VPN ja muodosta yhteys lähiverkkosi laitteisiin, kuten tulostimeen."; /* Header text for the Exclude Local Networks setting item. */ -"network.protection.vpn.exclude.local.networks.setting.header" = "General"; +"network.protection.vpn.exclude.local.networks.setting.header" = "Yleiset"; /* Title for the Exclude Local Networks setting item. */ "network.protection.vpn.exclude.local.networks.setting.title" = "Sulje lähiverkot pois"; /* Title for the VPN Location screen's All Countries section. */ -"network.protection.vpn.location.all.countries.section.title" = "All Countries"; +"network.protection.vpn.location.all.countries.section.title" = "Kaikki maat"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.connected" = "Yhteyteen käytetty sijainti"; @@ -1734,7 +1827,7 @@ "network.protection.vpn.location.recommended.section.footer" = "Muodostaa yhteyden automaattisesti lähimpään löytämäämme palvelimeen."; /* Title for the VPN Location screen's Recommended section. */ -"network.protection.vpn.location.recommended.section.title" = "Recommended"; +"network.protection.vpn.location.recommended.section.title" = "Suositeltu"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.selected" = "Valittu sijainti"; @@ -1752,13 +1845,13 @@ "network.protection.vpn.preferred.location.nearest" = "Lähin sijainti"; /* Title for the Preferred Location VPN Settings item. */ -"network.protection.vpn.preferred.location.title" = "Preferred Location"; +"network.protection.vpn.preferred.location.title" = "Ensisijainen sijainti"; /* Footer text for the DNS server setting item. */ -"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo routes DNS queries through our DNS servers so your internet provider can't see what websites you visit."; +"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo reitittää DNS-kyselyt DNS-palvelimiemme kautta, jotta internetpalveluntarjoajasi ei näe, millä verkkosivustoilla vierailet."; /* Title for the FAQ row in the VPN status screen. */ -"network.protection.vpn.settings.faq" = "FAQs and Support"; +"network.protection.vpn.settings.faq" = "Usein kysytyt kysymykset ja tuki"; /* Title for the feedback row in the VPN status screen. */ "network.protection.vpn.settings.share-feedback" = "Anna palautett VPN:stä"; @@ -1940,6 +2033,66 @@ /* Deactivate button */ "pm.deactivate" = "Deaktivoi"; +/* Label for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.label" = "Sähköpostiosoite (valinnainen)"; + +/* Placeholder for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.placeholder" = "nimi@sähköposti.com"; + +/* Placeholder for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.placeholder" = "Anna meille palautetta..."; + +/* Title for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.title" = "Yleistä palautetta"; + +/* Title for the ITR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-itr-problem.title" = "Identity Theft Restoration"; + +/* Title for the PIR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-pir-problem.title" = "Personal Information Removal"; + +/* Title for the Subscriptions and Payments category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-ppro-problem.title" = "Tilaukset ja maksut"; + +/* Placeholder for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.placeholder" = "Kerro meille, mitä tapahtuu..."; + +/* Title for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.title" = "Ilmoita ongelmasta"; + +/* Title for the VPN category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-vpn-problem.title" = "VPN"; + +/* Placeholder for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.placeholder" = "Minkä ominaisuuden haluaisit nähdä?"; + +/* Title for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.title" = "Ominaisuuspyyntö"; + +/* Title for the category selection section in the Privacy Pro feedback form */ +"ppro.feedback-form.select-category.title" = "VALITSE KATEGORIA"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-1" = "Kohtasitko ongelman, jota ei löydy [ohjekeskuksestamme](duck://)? Haluamme ehdottomasti tietää siitä.\n\nAnna sähköpostiosoite, jos haluat meidän ottavan sinuun yhteyttä tästä ongelmasta (emme ehkä pysty vastaamaan kaikkiin ongelmiin):"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-2" = "Edellä annettujen tietojen lisäksi lähetämme palautteesi mukana joitakin anonymisoituja tietoja:"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-3" = "• Ovatko jotkin selaimen ominaisuudet aktiivisia"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-4" = "• Yhdistetty sovellusdiagnostiikka (esim. virhekoodit)"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-5" = "Napauttamalla \"Lähetä\" hyväksyt, että DuckDuckGo voi käyttää annettuja tietoja sovelluksen parantamiseen."; + +/* Title for the text box in the Privacy Pro feedback form */ +"ppro.feedback-form.text-box.title" = "Palaute"; + +/* Title for the Privacy Pro feedback form */ +"ppro.feedback-form.title" = "Lähetä palautetta"; + /* Button title for sync bookmarks limits exceeded warning to go to manage bookmarks */ "prefrences.sync.bookmarks-limit-exceeded-action" = "Hallitse kirjanmerkkejä"; @@ -2029,6 +2182,18 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Suosikit"; +/* Name of the option the user can chose to give general browser feedback */ +"send.browser.feedback.general-feedback" = "Yleistä palautetta"; + +/* Name of the option the user can chose to give browser feedback about a problem they enountered */ +"send.browser.feedback.report-problem" = "Ilmoita ongelmasta"; + +/* Name of the option the user can chose to give browser feedback about a feature they would like */ +"send.browser.feedback.request-feature" = "Pyydä ominaisuutta"; + +/* Title of the picker where the user can chose the category of the feedback they want ot send. */ +"send.browser.feedback.select-category" = "VALITSE KATEGORIA"; + /* Settings cell for About DDG */ "settings.about.ddg" = "Tietoa DuckDuckGo:sta"; @@ -2084,6 +2249,9 @@ /* Section footer Autolock description */ "settings.autolock.description" = "Jos käytössä on Touch ID, Face ID tai järjestelmän salasana, sinua pyydetään poistamaan lukitus, kun avaat sovelluksen."; +/* Settings cell for Browser Feedback */ +"settings.browser.feedback" = "Selainpalaute"; + /* Settings screen cell text for Automatically Clearing Data */ "settings.clear.data" = "Tyhjennä tiedot automaattisesti"; @@ -2182,7 +2350,7 @@ "settings.ppro" = "Privacy Pro"; /* Title for Link in the Footer of Privacy Pro section */ -"settings.ppro.footer" = "Privacy Policy and Terms of Service"; +"settings.ppro.footer" = "Tietosuojakäytäntö ja palveluehdot"; /* Settings screen cell for long press previews */ "settings.previews" = "Esikatselu pitkällä painalluksella"; @@ -2208,23 +2376,20 @@ /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Aseta osoitekentän sijainti"; -/* Subscription activation pending description */ -"settings.subscription.activation.pending.description" = "This is taking longer than usual, please check back later."; +/* Privacy pro description subtitle in settings when the is activating */ +"settings.subscription.activating" = "Aktivoidaan"; -/* Subscription activation pending title */ -"settings.subscription.activation.pending.title" = "Tilauksesi aktivoidaan"; +/* Subscription activation pending description */ +"settings.subscription.activation.pending.description" = "Tämä kestää tavallista pidempään, palaa myöhemmin."; /* Data Broker protection cell subtitle for privacy pro */ -"settings.subscription.DBP.subtitle" = "Remove your info from sites that sell it"; +"settings.subscription.DBP.subtitle" = "Poista tietosi sivustoilta, jotka myyvät niitä"; /* Data Broker protection cell title for privacy pro */ "settings.subscription.DBP.title" = "Personal Information Removal"; -/* Privacy pro description subtext */ -"settings.subscription.description" = "Saumattomampi yksityisyys kolmen uuden suojauksen avulla:"; - /* I have a Subscription button text for privacy pro */ -"settings.subscription.existing.subscription" = "I Have a Subscription"; +"settings.subscription.existing.subscription" = "Minulla on tilaus"; /* Subscription expired description */ "settings.subscription.expired.comment" = "Tilaa uudelleen jatkaaksesi Privacy Pron käyttöä"; @@ -2232,23 +2397,26 @@ /* Subscription expired tittle message */ "settings.subscription.expired.title" = "Privacy Pro -tilauksesi on päättynyt"; -/* Privacy pro features list */ -"settings.subscription.features" = " • VPN\n • Personal Information Removal\n • Identity Theft Restoration"; - /* Identity theft restoration cell subtitle for privacy pro */ -"settings.subscription.ITR.subtitle" = "If your identity is stolen, we'll help restore it"; +"settings.subscription.ITR.subtitle" = "Jos henkilöllisyytesi varastetaan, me autamme palauttamaan sen."; /* Identity theft restoration cell title for privacy pro */ "settings.subscription.ITR.title" = "Identity Theft Restoration"; /* Get Privacy Pro button text for privacy pro */ -"settings.subscription.learn.more" = "Get Privacy Pro"; +"settings.subscription.learn.more" = "Hanki Privacy Pro"; /* Subscription Settings button text for privacy pro */ -"settings.subscription.manage" = "Subscription Settings"; +"settings.subscription.manage" = "Tilauksen asetukset"; + +/* Privacy Pro description subtitle in settings */ +"settings.subscription.row.description" = "Sisältää: VPN ja Identity Theft Restoration."; -/* Call to action title for Privacy Pro */ -"settings.subscription.subscribe" = "Subscribe to Privacy Pro"; +/* Call to action title for Privacy Pro settings */ +"settings.subscription.subscribe" = "Suojaa yhteytesi ja identiteettisi Privacy Prolla"; + +/* Privacy pro description subtitle in settings */ +"settings.subscription.us.description" = "Seuraavat sisältyvät: VPN, Personal Information Removal ja Identity Theft Restoration."; /* VPN cell title for privacy pro */ "settings.subscription.VPN.title" = "VPN"; @@ -2317,67 +2485,64 @@ "siteFeedback.urlPlaceholder" = "Mikä verkkosivusto on viallinen?"; /* Subscription Activation Info */ -"subscription.activate..header.description" = "Access your Privacy Pro subscription on this device via Apple ID or an email address."; +"subscription.activate..header.description" = "Pääset Privacy Pro -tilaukseesi tällä laitteella Apple-tilin tai sähköpostiosoitteen kautta."; /* Button for adding email address to subscription */ -"subscription.activate.add.email.button" = "Add Email"; +"subscription.activate.add.email.button" = "Lisää sähköpostiosoite"; /* Apple ID option for activation */ -"subscription.activate.appleid" = "Apple ID"; +"subscription.activate.appleid" = "Apple-tili"; /* Button text for restoring purchase via Apple ID */ -"subscription.activate.appleid.button" = "Restore Purchase"; +"subscription.activate.appleid.button" = "Palauta osto"; /* Description for Apple ID activation */ -"subscription.activate.appleid.description" = "Restore your purchase to activate your subscription on this device."; +"subscription.activate.appleid.description" = "Aktivoi tilauksesi tällä laitteella palauttamalla ostosi."; /* Subscription Activation Info */ -"subscription.activate.description" = "Your subscription is automatically available in DuckDuckGo on any device signed in to your Apple ID."; +"subscription.activate.description" = "Tilauksesi on automaattisesti saatavilla DuckDuckGossa kaikilla laitteilla, joilla on kirjauduttu Apple-tilillesi."; /* Button for editing email address added to subscription */ -"subscription.activate.edit.email.button" = "Edit Email"; +"subscription.activate.edit.email.button" = "Muokkaa sähköpostiosoitetta"; /* View Title for editing your email account */ -"subscription.activate.edit.email.title" = "Edit Email"; +"subscription.activate.edit.email.title" = "Muokkaa sähköpostiosoitetta"; /* Email option for activation */ -"subscription.activate.email" = "Email"; +"subscription.activate.email" = "Sähköposti"; /* Restore button title for Email */ -"subscription.activate.email.button" = "Enter Email"; +"subscription.activate.email.button" = "Anna sähköpostiosoite"; /* Description for Email activation */ -"subscription.activate.email.description" = "Use your email to activate your subscription on this device."; +"subscription.activate.email.description" = "Aktivoi tilauksesi tällä laitteella sähköpostiosoitteellasi."; /* Activate subscription title */ "subscription.activate.email.title" = "Aktivoi tilaus"; /* Button title for cancelling email deletion */ -"subscription.activate.manage.email.cancel" = "Cancel"; +"subscription.activate.manage.email.cancel" = "Peruuta"; /* Button title for confirming email deletion */ "subscription.activate.manage.email.OK" = "OK"; /* Restore button title for AppleID */ -"subscription.activate.restore.apple" = "Restore Purchase"; +"subscription.activate.restore.apple" = "Palauta osto"; /* Subscription Activation Title */ -"subscription.activate.title" = "Activate your subscription on this device"; +"subscription.activate.title" = "Aktivoi tilauksesi tällä laitteella"; /* Resend activation instructions button */ -"subscription.add.device.resend.instructions" = "Resend Instructions"; +"subscription.add.device.resend.instructions" = "Lähetä ohjeet uudelleen"; /* Add email to an existing subscription */ -"subscription.add.email" = "Add an email address to activate your subscription on your other devices. We’ll only use this address to verify your subscription."; +"subscription.add.email" = "Aktivoi tilauksesi muilla laitteillasi lisäämällä sähköpostiosoite. Käytämme tätä osoitetta vain tilauksesi vahvistamiseen."; /* View title for adding email to subscription */ -"subscription.add.email.title" = "Add Email"; - -/* Title for Alert messages */ -"subscription.alert.title" = "subscription.alert.title"; +"subscription.add.email.title" = "Lisää sähköpostiosoite"; /* Subscription availability message on Apple devices */ -"subscription.available.apple" = "Privacy Pro is available on any device signed in to the same Apple ID."; +"subscription.available.apple" = "Privacy Pro on saatavilla kaikilla laitteilla, joilla on kirjauduttu samalle Apple-tilille."; /* Text for the manage billing page */ "subscription.billing.google.text" = "Tilauksesi on ostettu Google Play Kaupasta. Jos haluat uusia tilauksesi, avaa Google Play Kaupan tilausasetukset laitteella, jolla olet kirjautunut samalle Google-tilille, jolla alun perin ostit tilauksesi."; @@ -2385,89 +2550,86 @@ /* Title for the manage billing page */ "subscription.billing.google.title" = "Tilaussopimukset"; -/* Subscription annual billing period type */ -"subscription.billing.period.annual" = "annual"; - -/* Subscription monthly billing period type */ -"subscription.billing.period.monthly" = "monthly"; - /* Subscription Removal confirmation message */ -"subscription.cancel.message" = "Your subscription has been removed from this device."; +"subscription.cancel.message" = "Tilauksesi on poistettu tältä laitteelta."; /* Change plan or cancel title */ -"subscription.change.plan" = "Update Plan or Cancel"; +"subscription.change.plan" = "Päivitä tilaus tai peruuta"; /* Navigation Button for closing subscription view */ -"subscription.close" = "Close"; +"subscription.close" = "Sulje"; /* Title for Confirm messages */ -"subscription.confirm.title" = "Are you sure?"; +"subscription.confirm.title" = "Oletko varma?"; /* Header for section for activating subscription on other devices */ -"subscription.devices.header" = "Activate on Other Devices"; +"subscription.devices.header" = "Aktivoi muilla laitteilla"; /* Footer for section for activating subscription on other devices when email was not yet added */ -"subscription.devices.no.email.footer" = "Add an optional email to your subscription or use your Apple ID to access Privacy Pro on other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.no.email.footer" = "Lisää tilaukseesi valinnainen sähköpostiosoite, jotta voit käyttää Privacy Prota muilla laitteilla. **[Lue lisää](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Footer for section for activating subscription on other devices when email is added */ -"subscription.devices.with.email.footer" = "Use this email to activate your subscription in Settings > Privacy Pro in the DuckDuckGo app on your other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.with.email.footer" = "Aktivoi tilauksesi tällä sähköpostiosoitteella muiden laitteiden DuckDuckGo-sovelluksessa kohdassa Asetukset > Privacy Pro. **[Lue lisää](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Alert content for not found subscription */ -"subscription.email.inactive.alert.message" = "The subscription associated with this email is no longer active."; +"subscription.email.inactive.alert.message" = "Tähän sähköpostiin liitetty tilaus ei ole enää aktiivinen."; /* Alert title for not found subscription */ -"subscription.email.inactive.alert.title" = "Subscription Not Found"; +"subscription.email.inactive.alert.title" = "Tilausta ei löytynyt"; /* Alert content for not found subscription */ -"subscription.expired.alert.message" = "The subscription associated with this Apple ID is no longer active."; +"subscription.expired.alert.message" = "Tähän Apple-tiliin liitetty tilaus ei ole enää aktiivinen."; /* FAQ Button */ -"subscription.faq" = "FAQs and Support"; +"subscription.faq" = "Usein kysytyt kysymykset ja tuki"; /* FAQ Description */ -"subscription.faq.description" = "Get answers to frequently asked questions or contact Privacy Pro support from our help pages."; +"subscription.faq.description" = "Katso vastaukset usein kysyttyihin kysymyksiin tai ota yhteyttä Privacy Pron tukeen ohjesivuiltamme."; + +/* Send Feedback Button */ +"subscription.feedback" = "Lähetä palautetta"; /* Cancel action for the existing subscription dialog */ -"subscription.found.cancel" = "Cancel"; +"subscription.found.cancel" = "Peruuta"; /* Restore action for the existing subscription dialog */ -"subscription.found.restore" = "Restore"; +"subscription.found.restore" = "Palauta"; /* Message for the existing subscription dialog */ -"subscription.found.text" = "We found a subscription associated with this Apple ID."; +"subscription.found.text" = "Löysimme tähän Apple ID-tiliin liitetyn tilauksen."; /* Title for the existing subscription dialog */ -"subscription.found.title" = "Subscription Found"; +"subscription.found.title" = "Tilaus löytyi"; /* Help and support Section header */ -"subscription.help" = "Help and support"; +"subscription.help" = "Apu ja tuki"; /* Manage Plan header */ -"subscription.manage.plan" = "Manage Plan"; +"subscription.manage.plan" = "Hallinnoi suunnitelmaa"; /* Header for the subscription section */ -"subscription.manage.title" = "Subscription"; +"subscription.manage.title" = "Tilaus"; /* Alert content for not found subscription */ -"subscription.notFound.alert.message" = "There is no subscription associated with this Apple ID."; +"subscription.notFound.alert.message" = "Tähän Apple-tiliin ei ole liitetty tilausta."; /* Alert title for not found subscription */ -"subscription.notFound.alert.title" = "Subscription Not Found"; +"subscription.notFound.alert.title" = "Tilausta ei löytynyt"; /* View plans button text */ -"subscription.notFound.view.plans" = "View Plans"; +"subscription.notFound.view.plans" = "Näytä tilaukset"; /* Hero Text for Personal information removal */ -"subscription.pir.hero" = "Activate Privacy Pro on desktop to set up Personal Information Removal"; +"subscription.pir.hero" = "Aktivoi Privacy Pro tietokoneella Personal Information Removalin asentamiseksi"; /* Description on how to use Personal information removal in desktop. The first placeholder references a location in the Desktop application. Privacy Pro>, and the second, the menu entry. i.e. */ -"subscription.pir.heroText" = "In the DuckDuckGo browser for desktop, go to %1$@ and click %2$@ to get started."; +"subscription.pir.heroText" = "Avaa aluksi tietokoneessa DuckDuckGo-selain, siirry osoitteeseen %1$@ ja klikkaa %2$@."; /* Settings references a menu in the Desktop app, Privacy Pro, references our product name */ -"subscription.pir.heroTextLocation" = "Settings > Privacy Pro"; +"subscription.pir.heroTextLocation" = "Asetukset > Privacy Pro"; /* Menu item for enabling Personal Information Removal on Desktop */ -"subscription.pir.heroTextMenyEntry" = "I have a subscription"; +"subscription.pir.heroTextMenyEntry" = "Minulla on tilaus"; /* Text for the 'macOS' button */ "subscription.pir.macos" = "Mac"; @@ -2476,28 +2638,28 @@ "subscription.pir.windows" = "Windows"; /* Progress view title when completing the purchase */ -"subscription.progress.view.completing.purchase" = "Completing purchase..."; +"subscription.progress.view.completing.purchase" = "Ostoksen suorittaminen..."; /* Progress view title when starting the purchase */ -"subscription.progress.view.purchasing.subscription" = "Purchase in progress..."; +"subscription.progress.view.purchasing.subscription" = "Osto käynnissä..."; /* Progress view title when restoring past subscription purchase */ -"subscription.progress.view.restoring.subscription" = "Restoring subscription..."; +"subscription.progress.view.restoring.subscription" = "Palautetaan tilausta..."; /* Remove from this device button */ -"subscription.remove.from.device.button" = "Remove From This Device"; +"subscription.remove.from.device.button" = "Poista tältä laitteelta"; /* Remove from device confirmation dialog text */ -"subscription.remove.from.device.text" = "You will no longer be able to access your Privacy Pro subscription on this device. This will not cancel your subscription, and it will remain active on your other devices."; +"subscription.remove.from.device.text" = "Et voi enää käyttää Privacy Pro -tilaustasi tällä laitteella. Tämä ei peruuta tilaustasi, vaan se pysyy aktiivisena muilla laitteillasi."; /* Remove from device confirmation dialog title */ -"subscription.remove.from.device.title" = "Remove from this device?"; +"subscription.remove.from.device.title" = "Poistetaanko tältä laitteelta?"; /* Remove subscription button text */ -"subscription.remove.subscription" = "Remove Subscription"; +"subscription.remove.subscription" = "Poista tilaus"; /* Remove subscription cancel button text */ -"subscription.remove.subscription.cancel" = "Cancel"; +"subscription.remove.subscription.cancel" = "Peruuta"; /* Button text for general error message */ "subscription.restore.backend.error.button" = "Takaisin asetuksiin"; @@ -2509,28 +2671,43 @@ "subscription.restore.backend.error.title" = "Jokin meni pieleen"; /* Alert for general error message */ -"subscription.restore.general.error.message" = "The App Store was unable to process your purchase. Please try again later."; +"subscription.restore.general.error.message" = "App Store ei voinut käsitellä ostoasi. Yritä myöhemmin uudelleen."; /* Alert for general error title */ -"subscription.restore.general.error.title" = "Something Went Wrong"; +"subscription.restore.general.error.title" = "Jokin meni pieleen"; /* Alert button text for restored purchase alert */ "subscription.restore.success.alert.button" = "OK"; /* Alert message for restored purchase */ -"subscription.restore.success.alert.message" = "Your purchases have been restored."; +"subscription.restore.success.alert.message" = "Ostosi on palautettu."; /* Alert title for restored purchase */ -"subscription.restore.success.alert.title" = "You’re all set."; +"subscription.restore.success.alert.title" = "Kaikki on valmista."; + +/* Subtitle in header when subscribed */ +"subscription.subscribed" = "Tilattu"; /* Subscription Expired Data. This reads as 'Your subscription expired on (date)' */ "subscription.subscription.expired.caption" = "Tilauksesi päättyi %@"; -/* Subscription expiration info. This reads as 'Your (monthly or annual) subscription expires on (date)' */ -"subscription.subscription.expiring.caption" = "Your %1$@ subscription expires on %2$@."; +/* Monthly subscription expiration info where parameter is expiration date. This reads as 'Your monthly subscription expires on (date)' */ +"subscription.subscription.expiring.monthly.caption" = "Kuukausitilauksesi vanhenee %@."; + +/* Unknown period subscription expiration info where parameter is expiration date. This reads as 'Your subscription expires on (date)' */ +"subscription.subscription.expiring.unknown.caption" = "Tilauksesi vanhenee %@."; + +/* Annual subscription expiration info where parameter is expiration date. This reads as 'Your annual subscription expires on (date)' */ +"subscription.subscription.expiring.yearly.caption" = "Vuositilauksesi päättyy %@."; + +/* Monthly subscription renewal info where parameter is renewal date. This reads as 'Your monthly subscription renews on (date)' */ +"subscription.subscription.renewing.monthly.caption" = "Kuukausitilauksesi uusiutuu %@."; + +/* Unknown period subscription renewal info where parameter is renewal date. This reads as 'Your subscription renews on (date)' */ +"subscription.subscription.renewing.unknown.caption" = "Tilauksesi uusiutuu %@."; -/* Subscription renewal info. This reads as 'Your (monthly or annual) subscription renews on (date)' */ -"subscription.subscription.renewing.caption" = "Your %1$@ subscription renews on %2$@."; +/* Annual subscription renewal info where parameter is renewal date. This reads as 'Your annual subscription renews on (date)' */ +"subscription.subscription.renewing.yearly.caption" = "Vuositilauksesi uusiutuu %@."; /* Navigation bar Title for subscriptions */ "subscription.title" = "Privacy Pro"; @@ -2677,118 +2854,118 @@ "voiceSearch.footer.note.old" = "Ääni käsitellään laitteella. Sitä ei tallenneta eikä jaeta kenellekään, edes DuckDuckGolle."; /* Cancel action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.cancel" = "Dismiss"; +"vpn.access-revoked.alert.action.cancel" = "Hylkää"; /* Primary action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.subscribe" = "Subscribe"; +"vpn.access-revoked.alert.action.subscribe" = "Tilaa"; /* Alert message for the alert when the Privacy Pro subscription expiress */ -"vpn.access-revoked.alert.message" = "Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"vpn.access-revoked.alert.message" = "Tilaa Privacy Pro, jotta voit yhdistää DuckDuckGo VPN:n uudelleen."; /* Alert title for the alert when the Privacy Pro subscription expires */ -"vpn.access-revoked.alert.title" = "VPN disconnected due to expired subscription"; +"vpn.access-revoked.alert.title" = "VPN-yhteys katkaistu vanhentuneen tilauksen takia."; /* Title for the VPN widget onboarding screen */ "vpn.addWidget.settings.title" = "Etsi ja valitse DuckDuckGo. Pyyhkäise sitten kohtaan VPN ja valitse Lisää widget."; /* Title for the Cancel button of the VPN feedback form */ -"vpn.feedback-form.button.cancel" = "Cancel"; +"vpn.feedback-form.button.cancel" = "Peruuta"; /* Title for the Done button of the VPN feedback form */ -"vpn.feedback-form.button.done" = "Done"; +"vpn.feedback-form.button.done" = "Valmis"; /* Title for the Submit button of the VPN feedback form */ -"vpn.feedback-form.button.submit" = "Submit"; +"vpn.feedback-form.button.submit" = "Lähetä"; /* Title for the Submitting state of the VPN feedback form */ -"vpn.feedback-form.button.submitting" = "Submitting…"; +"vpn.feedback-form.button.submitting" = "Lähetetään…"; /* Title for the browser crash/freeze category of the VPN feedback form */ -"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN causes browser to crash or freeze"; +"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN aiheuttaa selaimen kaatumisen tai jähmettymisen"; /* Title for the 'VPN fails to connect' category of the VPN feedback form */ -"vpn.feedback-form.category.fails-to-connect" = "VPN fails to connect"; +"vpn.feedback-form.category.fails-to-connect" = "VPN ei saa yhteyttä"; /* Title for the 'VPN feature request' category of the VPN feedback form */ -"vpn.feedback-form.category.feature-request" = "VPN feature request"; +"vpn.feedback-form.category.feature-request" = "VPN-ominaisuuspyyntö"; /* Title for the category 'VPN causes issues with other apps or websites' category of the VPN feedback form */ -"vpn.feedback-form.category.issues-with-apps" = "VPN causes issues with other apps or websites"; +"vpn.feedback-form.category.issues-with-apps" = "VPN aiheuttaa ongelmia muiden sovellusten tai verkkosivustojen kanssa"; /* Title for the local device connectivity category of the VPN feedback form */ -"vpn.feedback-form.category.local-device-connectivity" = "VPN won't let me connect to local device"; +"vpn.feedback-form.category.local-device-connectivity" = "VPN ei anna minun muodostaa yhteyttä paikalliseen laitteeseen"; /* Title for the 'other VPN feedback' category of the VPN feedback form */ -"vpn.feedback-form.category.other" = "Other VPN feedback"; +"vpn.feedback-form.category.other" = "Muu VPN-palaute"; /* Title for the category selection state of the VPN feedback form */ -"vpn.feedback-form.category.select-category" = "Select a category"; +"vpn.feedback-form.category.select-category" = "VALITSE KATEGORIA"; /* Title for the 'VPN is too slow' category of the VPN feedback form */ -"vpn.feedback-form.category.too-slow" = "VPN connection is too slow"; +"vpn.feedback-form.category.too-slow" = "VPN-yhteys on liian hidas"; /* Title for the 'unable to install' category of the VPN feedback form */ -"vpn.feedback-form.category.unable-to-install" = "Unable to install VPN"; +"vpn.feedback-form.category.unable-to-install" = "VPN:n asentaminen ei onnistu"; /* Title for the feedback sent view description of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.description" = "Your feedback will help us improve the\nDuckDuckGo VPN."; +"vpn.feedback-form.sending-confirmation.description" = "Palautteesi auttaa meitä parantamaan\nDuckDuckGo VPN:ää."; /* Title for the feedback sending error text of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.error" = "We couldn't send your feedback right now, please try again."; +"vpn.feedback-form.sending-confirmation.error" = "Emme voineet lähettää palautettasi juuri nyt. Yritä uudelleen."; /* Title for the feedback sent view title of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.title" = "Thank you!"; +"vpn.feedback-form.sending-confirmation.title" = "Kiitos!"; /* Toast message when the VPN feedback form is submitted successfully */ -"vpn.feedback-form.submitted.message" = "Thank You! Feedback submitted."; +"vpn.feedback-form.submitted.message" = "Kiitos! Palaute lähetetty."; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-1" = "Please describe what's happening, what you expected to happen, and the steps that led to the issue:"; +"vpn.feedback-form.text-1" = "Kuvaile, mitä tapahtuu, mitä odotit tapahtuvan ja vaiheet, jotka johtivat ongelmaan:"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-2" = "In addition to the details entered into this form, your app issue report will contain:"; +"vpn.feedback-form.text-2" = "Tässä lomakkeessa annettujen tietojen lisäksi sovellusongelmaraporttisi sisältää:"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-3" = "• Whether specific DuckDuckGo features are enabled"; +"vpn.feedback-form.text-3" = "• Ovatko tietyt DuckDuckGo-ominaisuudet käytössä"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-4" = "• Aggregate DuckDuckGo app diagnostics"; +"vpn.feedback-form.text-4" = "• DuckDuckGo-sovelluksen yhdistetty diagnostiikka"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-5" = "By tapping \"Submit\" I agree that DuckDuckGo may use the information in this report for purposes of improving the app's features."; +"vpn.feedback-form.text-5" = "Napauttamalla \"Lähetä\" hyväksyn, että DuckDuckGo voi käyttää tämän raportin tietoja sovelluksen ominaisuuksien parantamiseen."; /* Title for each screen of the VPN feedback form */ -"vpn.feedback-form.title" = "Help Improve the DuckDuckGo VPN"; +"vpn.feedback-form.title" = "Auta parantamaan DuckDuckGo VPN:ää"; /* VPN settings screen cell text for adding the VPN widget to the home screen */ "vpn.settings.add.widget" = "Lisää VPN-widget aloitusnäyttöön"; /* Disclaimer for the DNS Server section on the DNS Server screen */ -"vpn.settings.dns.section-disclaimer" = "Using a custom DNS server can impact browsing speeds and expose your activity to third parties if the server isn't secure or reliable."; +"vpn.settings.dns.section-disclaimer" = "Mukautetun DNS-palvelimen käyttö voi vaikuttaa selausnopeuteen ja altistaa toimintasi kolmansille osapuolille, jos palvelin ei ole turvallinen tai luotettava."; /* Header text for the DNS section on the VPN Settings screen */ "vpn.settings.dns.section-header" = "DNS"; /* Title for the Apply button on the DNS Server setting screen */ -"vpn.settings.dns.server.apply.button.title" = "Apply"; +"vpn.settings.dns.server.apply.button.title" = "Käytä"; /* Default value for the DNS Server row on the VPN Settings screen */ "vpn.settings.dns.server.default.value" = "DuckDuckGo"; /* Title for the IPv4 Address setting */ -"vpn.settings.dns.server.ipv4.title" = "IPv4 Address"; +"vpn.settings.dns.server.ipv4.title" = "IPv4-osoite"; /* Custom option for the DNS Server setting */ -"vpn.settings.dns.server.option.custom" = "Custom"; +"vpn.settings.dns.server.option.custom" = "Kustomoitu"; /* Recommended option for the DNS Server setting */ -"vpn.settings.dns.server.option.default" = "DuckDuckGo (Recommended)"; +"vpn.settings.dns.server.option.default" = "DuckDuckGo (Suositeltu)"; /* Title for the DNS Server setting screen */ -"vpn.settings.dns.server.screen.title" = "DNS Server"; +"vpn.settings.dns.server.screen.title" = "DNS-palvelin"; /* Title for the DNS Server row on the VPN Settings screen */ -"vpn.settings.dns.server.title" = "DNS Server"; +"vpn.settings.dns.server.title" = "DNS-palvelin"; /* Title for the button to enable push notifications in system settings */ "waitlist.allow-notifications" = "Salli ilmoitukset"; diff --git a/DuckDuckGo/fi.lproj/OmniBar.strings b/DuckDuckGo/fi.lproj/OmniBar.strings index c512dbd143..3f1375be22 100644 --- a/DuckDuckGo/fi.lproj/OmniBar.strings +++ b/DuckDuckGo/fi.lproj/OmniBar.strings @@ -7,6 +7,9 @@ /* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "Dt8-We-5tV"; */ "Dt8-We-5tV.accessibilityLabel" = "Tyhjennä teksti"; +/* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "gb5-wL-4OW"; */ +"gb5-wL-4OW.accessibilityLabel" = "Tyhjennä teksti"; + /* Class = "UIButton"; accessibilityLabel = "Share"; ObjectID = "hor-Jd-3kw"; */ "hor-Jd-3kw.accessibilityLabel" = "Jaa"; diff --git a/DuckDuckGo/fi.lproj/Settings.strings b/DuckDuckGo/fi.lproj/Settings.strings index 57c52799c3..3e0137aaba 100644 --- a/DuckDuckGo/fi.lproj/Settings.strings +++ b/DuckDuckGo/fi.lproj/Settings.strings @@ -10,9 +10,6 @@ /* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "5YD-Od-uDU"; */ "5YD-Od-uDU.text" = "Tyhjennä tiedot automaattisesti"; -/* Class = "UIButton"; normalTitle = "Settings"; ObjectID = "8YG-7Q-Czd"; */ -"8YG-7Q-Czd.normalTitle" = "Asetukset"; - /* Class = "UILabel"; text = "App Exit, Inactive for 1 Hour"; ObjectID = "9EC-lx-77p"; */ "9EC-lx-77p.text" = "Sovelluksesta poistuttaessa, 1 tunnin toimettomuuden jälkeen"; @@ -76,9 +73,6 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Suojaamattomat sivustot"; -/* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ -"ssa-zd-L3T.title" = "Tekstin koko"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Näytä näppäimistö kun"; diff --git a/DuckDuckGo/fr.lproj/DaxOnboarding.strings b/DuckDuckGo/fr.lproj/DaxOnboarding.strings index 51765a123d..926dc80aa2 100644 --- a/DuckDuckGo/fr.lproj/DaxOnboarding.strings +++ b/DuckDuckGo/fr.lproj/DaxOnboarding.strings @@ -1,12 +1,6 @@ /* Class = "UIButton"; normalTitle = "Hide"; ObjectID = "hgy-75-OvJ"; */ "hgy-75-OvJ.normalTitle" = "Masquer"; -/* Class = "UILabel"; text = "Welcome to\nDuckDuckGo!"; ObjectID = "Lvy-9G-3gr"; */ -"Lvy-9G-3gr.text" = "Bienvenue sur\nDuckDuckGo !"; - -/* Class = "UIButton"; normalTitle = "Let’s Do It!"; ObjectID = "oAv-Zt-uIt"; */ -"oAv-Zt-uIt.normalTitle" = "C'est parti !"; - /* Class = "UIButton"; normalTitle = "Button"; ObjectID = "zJt-F9-L48"; */ "zJt-F9-L48.normalTitle" = "Bouton"; diff --git a/DuckDuckGo/fr.lproj/Localizable.strings b/DuckDuckGo/fr.lproj/Localizable.strings index 623a9d5afc..37dc6fbcdf 100644 --- a/DuckDuckGo/fr.lproj/Localizable.strings +++ b/DuckDuckGo/fr.lproj/Localizable.strings @@ -341,7 +341,7 @@ "autofill.keep-enabled.alert.disable" = "Désactiver"; /* A link that takes the user to the DuckDuckGo help pages explaining password managers */ -"autofill.learn.more.link.title" = "Learn More"; +"autofill.learn.more.link.title" = "En savoir plus"; /* Button displayed after saving/updating an autofill login that takes the user to the saved login */ "autofill.login-save-action-button.toast" = "Voir"; @@ -977,9 +977,6 @@ /* ad = advertisment */ "dax.onboarding.home.subsequent" = "Bien joué !\n\nSouvenez-vous : chaque fois que vous naviguez avec moi, des publicités intrusives disparaissent. 👍"; -/* No comment provided by engineer. */ -"dax.onboarding.message" = "Internet peut être intrusif.\n\nMais ne vous inquiétez pas ! Rechercher et naviguer de manière confidentielle est plus facile que vous ne le pensez."; - /* No comment provided by engineer. */ "Debug" = "Déboguer"; @@ -1080,7 +1077,7 @@ "duckPlayer.never.label" = "Jamais"; /* Body text for the modal feature explanation */ -"duckplayer.presentation.modal.body" = "Duck Player vous permet de regarder YouTube sans publicités ciblées, en vivant une expérience de type cinéma dans DuckDuckGo. En outre, ce que vous regardez n'influence pas vos recommandations."; +"duckplayer.presentation.modal.body" = "Duck Player vous permet de regarder YouTube sans publicités ciblées dans DuckDuckGo. De plus, ce que vous regardez n'influence pas vos recommandations."; /* Button that will dismiss the modal */ "duckplayer.presentation.modal.dismiss-button" = "J'ai compris !"; @@ -1088,17 +1085,23 @@ /* Two line title (separated by \n) for the feature explanation */ "duckplayer.presentation.modal.title" = "YouTube vous inonde de publicités ? Essayez Duck Player !"; +/* Two line title (separated by \n) for the feature explanation */ +"duckplayer.presentation.modal.title.youtube" = "YouTube vous inonde de publicités ? Pas avec Duck Player !"; + /* Footer label in the settings screen for Duck Player */ "duckplayer.settings.footer" = "DuckDuckGo fournit tous les incontournables de la confidentialité dont vous avez besoin pour vous protéger lorsque vous naviguez sur le Web."; /* Text explaining what Duck Player is in the settings screen. */ -"duckplayer.settings.info-text" = "Duck Player offre une expérience de visionnage épurée, sans publicités personnalisées, et empêche l'activité de visionnage d'influencer vos recommandations YouTube."; +"duckplayer.settings.info-text" = "Duck Player vous permet de regarder YouTube sans publicités ciblées dans DuckDuckGo. De plus, ce que vous regardez n'influence pas vos recommandations."; /* Button that takes the user to learn more about Duck Player. */ "duckplayer.settings.learn-more" = "En savoir plus"; +/* Settings screen cell text for DuckPlayer settings to open in new tab */ +"duckplayer.settings.open-new-tab-label" = "Ouvrir Duck Player dans un nouvel onglet"; + /* Settings screen cell text for DuckPlayer settings */ -"duckplayer.settings.open-videos-in" = "Ouvrir des vidéos dans Duck Player"; +"duckplayer.settings.open-videos-in" = "Ouvrir les vidéos YouTube dans Duck Player"; /* Settings screen cell text for DuckPlayer settings */ "duckplayer.settings.title" = "Duck Player"; @@ -1169,9 +1172,6 @@ /* Title for prompt to sign up for email protection */ "email.signup-prompt.title" = "Masquez votre adresse e-mail et bloquez les traqueurs"; -/* No comment provided by engineer. */ -"empty!" = "vide !"; - /* Empty list state placholder */ "empty.bookmarks" = "Aucun signet ajouté pour le moment"; @@ -1322,6 +1322,39 @@ /* Confirmation button */ "feedback.form.submit" = "Envoyer"; +/* Title of the feedback form */ +"feedback.form.title" = "Aidez-nous à améliorer Privacy Pro"; + +/* Category for Identity Theft Restoration feedback */ +"feedback.general.category.itr" = "Identity Theft Restoration"; + +/* Category for Personal Info Removal feedback */ +"feedback.general.category.pir" = "Suppression des informations personnelles"; + +/* Category for subscription and payments feedback */ +"feedback.general.category.ppro" = "Abonnement et paiements"; + +/* Prompt to select a category for general feedback */ +"feedback.general.category.select" = "SÉLECTIONNER UNE CATÉGORIE"; + +/* Category for VPN feedback */ +"feedback.general.category.vpn" = "VPN"; + +/* Category for access code issues */ +"feedback.itr.category.access-code" = "Problème avec le code d'accès"; + +/* Category for issues contacting an advisor */ +"feedback.itr.category.cant-contact-advisor" = "Impossible de contacter le conseiller"; + +/* Prompt to select a category for Identity Theft Restoration feedback */ +"feedback.itr.category.select" = "SÉLECTIONNER UNE CATÉGORIE"; + +/* Category for other Identity Theft Restoration issues */ +"feedback.itr.category.something-else" = "Autre chose"; + +/* Category for unhelpful advisor calls */ +"feedback.itr.category.unhelpful" = "L'échange avec le conseiller n'a pas été utile"; + /* No comment provided by engineer. */ "feedback.negative.form.genericPlaceholder" = "Veuillez être aussi précis que possible"; @@ -1361,6 +1394,24 @@ /* No comment provided by engineer. */ "feedback.performance.slowLoading" = "Les pages Web ou les résultats de recherche se chargent lentement"; +/* Category for when scan finds incorrect records */ +"feedback.pir.category.not-me" = "L'analyse a trouvé des données qui ne me concernent pas"; + +/* Category for when scan doesn't find info on a specific site */ +"feedback.pir.category.nothing-on-site" = "L'analyse n'a pas trouvé mes informations sur un certain site"; + +/* Category for other Personal Info Removal issues */ +"feedback.pir.category.other" = "Autre chose"; + +/* Category for when the removal process is stuck */ +"feedback.pir.category.removal-stuck" = "Le processus de suppression est bloqué"; + +/* Category for when the scan is stuck */ +"feedback.pir.category.scan-stuck" = "L'analyse des données est bloquée"; + +/* Prompt to select a category for Personal Info Removal feedback */ +"feedback.pir.category.select" = "SÉLECTIONNER UNE CATÉGORIE"; + /* Header above input field */ "feedback.positive.form.header" = "Partager des informations"; @@ -1379,6 +1430,15 @@ /* Button encouraging uses to share details aboout their feedback */ "feedback.positive.submit" = "Partager des informations"; +/* Category for other Privacy Pro issues */ +"feedback.ppro.category.other" = "Autre chose"; + +/* Category for one-time password issues */ +"feedback.ppro.category.otp" = "Problème lié au mot de passe à usage unique"; + +/* Prompt to select a category for Privacy Pro feedback */ +"feedback.ppro.category.select" = "SÉLECTIONNER UNE CATÉGORIE"; + /* No comment provided by engineer. */ "feedback.start.footer" = "Vos commentaires anonymes sont importants pour nous."; @@ -1529,12 +1589,6 @@ /* No comment provided by engineer. */ "keyCommandShowAllTabs" = "Afficher tous les onglets"; -/* Please preserve newline character */ -"launchscreenWelcomeMessage" = "Bienvenue sur\nDuckDuckGo !"; - -/* No comment provided by engineer. */ -"LOREM IPSUM" = "LOREM IPSUM"; - /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "DuckDuckGo pour Mac répond à vos besoins de rapidité et de fonctionnalités de navigation, et dispose des meilleurs outils de leur catégorie pour protéger votre vie privée."; @@ -1586,62 +1640,83 @@ /* Edit button */ "navigation.title.edit" = "Modifier"; -/* String indicating NetP is connected when viewed from the settings screen */ -"netP.cell.connected" = "Connected"; - -/* String indicating NetP is disconnected when viewed from the settings screen */ -"netP.cell.disconnected" = "Not connected"; - -/* Title for the DuckDuckGo VPN feature in settings */ -"netP.settings.title" = "VPN"; +/* String indicating NetP is snoozing when viewed from the settings screen */ +"netP.cell.snoozing" = "En veille"; /* Title for the DuckDuckGo VPN feature */ "netP.title" = "VPN DuckDuckGo"; +/* Button title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.action" = "Ajouter un widget"; + +/* Message for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.message" = "Activez et désactivez le VPN directement depuis l’écran d’accueil."; + +/* Title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.title" = "Ajoutez un widget VPN"; + /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN déconnecté en raison de l'expiration de l'abonnement. Abonnez-vous à Privacy Pro pour reconnecter le VPN DuckDuckGo."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Échec de la connexion à Network Protection. Veuillez réessayer plus tard."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "Le VPN DuckDuckGo n'a pas réussi à se connecter. Veuillez réessayer plus tard."; /* Title for the VPN FAQ screen. */ "network.protection.faq.title" = "FAQ VPN DuckDuckGo"; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Interruption de Network Protection. Tentative de reconnexion en cours…"; +/* Message for tooltip about geoswitching */ +"network.protection.geoswitching.tip.message" = "Vous pouvez personnaliser votre emplacement VPN en vous connectant à l’un de nos serveurs dans le monde entier."; + +/* Title for tooltip about geoswitching */ +"network.protection.geoswitching.tip.title" = "Modifiez votre emplacement"; + +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "Le VPN DuckDuckGo a été interrompu. Tentative de reconnexion en cours…"; /* Message for the network protection invite dialog */ -"network.protection.invite.dialog.message" = "Enter your invite code to get started."; +"network.protection.invite.dialog.message" = "Saisissez votre code d'invitation pour commencer."; /* Title for the network protection invite screen */ "network.protection.invite.dialog.title" = "Vous êtes invité à essayer DuckDuckGo VPN"; /* Prompt for the network protection invite code text field */ -"network.protection.invite.field.prompt" = "Invite Code"; +"network.protection.invite.field.prompt" = "Code d'invitation"; /* Message for the network protection invite success view */ -"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; +"network.protection.invite.success.message" = "Empêchez les sites Web de voir votre position et dissimulez votre activité en ligne aux fournisseurs d'accès Internet et aux autres personnes de votre réseau."; /* Title for the network protection invite success view */ -"network.protection.invite.success.title" = "Success! You’re in."; +"network.protection.invite.success.title" = "Bravo ! Vous y êtes."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; /* Title text for an iOS quick action that opens VPN settings */ "network.protection.quick-action.open-vpn" = "Ouvrir le VPN"; +/* Button title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.action" = "En savoir plus"; + +/* Message for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.message" = "Vous pouvez utiliser des sites ou des applications qui bloquent le trafic VPN en mettant en pause la connexion VPN."; + +/* Title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.title" = "Évitez les conflits VPN"; + +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "Le VPN est en veille pendant %@"; + /* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ "network.protection.status.connected.format" = "Connecté · %@"; /* The label for the NetP VPN when connecting */ -"network.protection.status.connecting" = "Connecting..."; +"network.protection.status.connecting" = "Connexion en cours…"; /* The label for the NetP VPN when disconnected */ -"network.protection.status.disconnected" = "Not connected"; +"network.protection.status.disconnected" = "Non connecté"; /* The label for the NetP VPN when disconnecting */ -"network.protection.status.disconnecting" = "Disconnecting..."; +"network.protection.status.disconnecting" = "Déconnexion en cours…"; /* Message label text for the status view when VPN is disconnected */ "network.protection.status.header.message.off" = "Connectez-vous pour sécuriser tout le trafic\ntrafic Internet de votre appareil."; @@ -1655,26 +1730,41 @@ /* Header title label text for the status view when VPN is connected */ "network.protection.status.header.title.on" = "Le VPN DuckDuckGo est activé"; +/* Header title label text for the status view when VPN is snoozing */ +"network.protection.status.header.title.snoozed" = "Le VPN DuckDuckGo est en veille"; + /* The status view 'Share Feedback' button which is shown inline on the status view after the temporary free use footer text */ -"network.protection.status.menu.share.feedback" = "Share Feedback"; +"network.protection.status.menu.share.feedback" = "Partagez vos commentaires"; + +/* The label for the NetP VPN when paused */ +"network.protection.status.paused" = "En pause"; + +/* The label for when NetP VPN is snoozing plus the length of time remaining formatted as '0:00' */ +"network.protection.status.snoozing.format" = "En veille, %@ restante(s)"; + +/* Snooze button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.snooze" = "Mettre en veille pendant 20 minutes"; + +/* Wake Up button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.wake-up" = "Réveiller"; /* Connection details label shown in NetworkProtection's status view. */ -"network.protection.status.view.connection.details" = "Connection Details"; +"network.protection.status.view.connection.details" = "Détails de la connexion"; /* Custom DNS label shown in NetworkProtection's status view. */ -"network.protection.status.view.custom.dns" = "DNS Server"; +"network.protection.status.view.custom.dns" = "Serveur DNS"; /* Generic connection failed error message shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.message" = "Please try again later."; +"network.protection.status.view.error.connection.failed.message" = "Veuillez réessayer plus tard."; /* Generic connection failed error title shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; +"network.protection.status.view.error.connection.failed.title" = "Connexion impossible."; /* IP Address label shown in NetworkProtection's status view. */ -"network.protection.status.view.ip.address" = "IP Address"; +"network.protection.status.view.ip.address" = "Adresse IP"; /* Location label shown in NetworkProtection's status view. */ -"network.protection.status.view.location" = "Location"; +"network.protection.status.view.location" = "Localisation"; /* Label shown on the title of the settings section in NetworkProtection's status view. */ "network.protection.status.view.settings.section.title" = "Gérer"; @@ -1682,12 +1772,15 @@ /* Title label text for the status view when netP is disconnected */ "network.protection.status.view.title" = "VPN"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Network Protection est activé. Votre emplacement et votre activité en ligne sont protégés."; +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "Le VPN DuckDuckGo est activé. Votre emplacement et votre activité en ligne sont protégés."; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Routage du trafic de l'appareil via %@."; +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "La veille du VPN a pris fin. Routage du trafic de l'appareil via %@."; + /* Title for the button to link to the iOS app settings and enable notifications app-wide. */ "network.protection.turn.on.notifications.button.title" = "Activer les notifications"; @@ -1704,7 +1797,7 @@ "network.protection.vpn.alerts.toggle.section.footer" = "Recevez une notification si votre connexion échoue ou si l'état de votre VPN change."; /* Title for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.toggle.title" = "VPN Notifications"; +"network.protection.vpn.alerts.toggle.title" = "Notifications VPN"; /* Title for the data volume section in the VPN status screen */ "network.protection.vpn.data-volume" = "Volume de données"; @@ -1713,13 +1806,13 @@ "network.protection.vpn.exclude.local.networks.setting.footer" = "Permettez au trafic local de contourner le VPN et connectez-vous aux appareils de votre réseau local, tels qu'une imprimante."; /* Header text for the Exclude Local Networks setting item. */ -"network.protection.vpn.exclude.local.networks.setting.header" = "General"; +"network.protection.vpn.exclude.local.networks.setting.header" = "Général"; /* Title for the Exclude Local Networks setting item. */ "network.protection.vpn.exclude.local.networks.setting.title" = "Exclure les réseaux locaux"; /* Title for the VPN Location screen's All Countries section. */ -"network.protection.vpn.location.all.countries.section.title" = "All Countries"; +"network.protection.vpn.location.all.countries.section.title" = "Tous les pays"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.connected" = "Localisation connectée"; @@ -1734,7 +1827,7 @@ "network.protection.vpn.location.recommended.section.footer" = "Connectez-vous automatiquement au serveur le plus proche que nous trouvons."; /* Title for the VPN Location screen's Recommended section. */ -"network.protection.vpn.location.recommended.section.title" = "Recommended"; +"network.protection.vpn.location.recommended.section.title" = "Recommandée"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.selected" = "Emplacement sélectionné"; @@ -1752,13 +1845,13 @@ "network.protection.vpn.preferred.location.nearest" = "Emplacement le plus proche"; /* Title for the Preferred Location VPN Settings item. */ -"network.protection.vpn.preferred.location.title" = "Preferred Location"; +"network.protection.vpn.preferred.location.title" = "Emplacement préféré"; /* Footer text for the DNS server setting item. */ -"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo routes DNS queries through our DNS servers so your internet provider can't see what websites you visit."; +"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo achemine les requêtes DNS via nos serveurs DNS afin que votre fournisseur d'accès à Internet ne puisse pas voir les sites Web que vous visitez."; /* Title for the FAQ row in the VPN status screen. */ -"network.protection.vpn.settings.faq" = "FAQs and Support"; +"network.protection.vpn.settings.faq" = "FAQ et assistance"; /* Title for the feedback row in the VPN status screen. */ "network.protection.vpn.settings.share-feedback" = "Partagez vos commentaires sur le VPN"; @@ -1940,6 +2033,66 @@ /* Deactivate button */ "pm.deactivate" = "Désactiver"; +/* Label for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.label" = "E-mail (facultatif)"; + +/* Placeholder for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.placeholder" = "nom@email.com"; + +/* Placeholder for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.placeholder" = "Veuillez nous faire part de vos commentaires…"; + +/* Title for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.title" = "Commentaires d'ordre général"; + +/* Title for the ITR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-itr-problem.title" = "Identity Theft Restoration"; + +/* Title for the PIR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-pir-problem.title" = "Personal Information Removal"; + +/* Title for the Subscriptions and Payments category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-ppro-problem.title" = "Abonnements et paiements"; + +/* Placeholder for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.placeholder" = "Dis-nous ce qui se passe…"; + +/* Title for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.title" = "Signaler un problème"; + +/* Title for the VPN category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-vpn-problem.title" = "VPN"; + +/* Placeholder for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.placeholder" = "Quelle fonctionnalité aimeriez-vous voir ?"; + +/* Title for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.title" = "Demande de fonctionnalité"; + +/* Title for the category selection section in the Privacy Pro feedback form */ +"ppro.feedback-form.select-category.title" = "SÉLECTIONNER UNE CATÉGORIE"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-1" = "Vous avez rencontré un problème qui n'est pas traité dans notre [centre d'aide](duck://) ? Nous tenons vraiment à savoir de quoi il s'agit.\n\nIndiquez une adresse e-mail si vous souhaitez que nous vous contactions à propos de ce problème (il se peut que nous ne soyons pas en mesure de répondre à tous les problèmes) :"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-2" = "Outre les informations saisies ci-dessus, nous envoyons des informations anonymisées avec vos commentaires :"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-3" = "• Si certaines fonctionnalités du navigateur sont actives"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-4" = "• Diagnostic agrégé des applications (par exemple, codes d'erreur)"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-5" = "En appuyant sur « Envoyer », vous autorisez DuckDuckGo à utiliser les informations fournies pour améliorer l'application."; + +/* Title for the text box in the Privacy Pro feedback form */ +"ppro.feedback-form.text-box.title" = "Avis"; + +/* Title for the Privacy Pro feedback form */ +"ppro.feedback-form.title" = "Envoyer vos remarques"; + /* Button title for sync bookmarks limits exceeded warning to go to manage bookmarks */ "prefrences.sync.bookmarks-limit-exceeded-action" = "Gérer les signets"; @@ -2029,6 +2182,18 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Favoris"; +/* Name of the option the user can chose to give general browser feedback */ +"send.browser.feedback.general-feedback" = "Commentaires d'ordre général"; + +/* Name of the option the user can chose to give browser feedback about a problem they enountered */ +"send.browser.feedback.report-problem" = "Signaler un problème"; + +/* Name of the option the user can chose to give browser feedback about a feature they would like */ +"send.browser.feedback.request-feature" = "Demander une fonctionnalité"; + +/* Title of the picker where the user can chose the category of the feedback they want ot send. */ +"send.browser.feedback.select-category" = "SÉLECTIONNER UNE CATÉGORIE"; + /* Settings cell for About DDG */ "settings.about.ddg" = "À propos de DuckDuckGo"; @@ -2084,6 +2249,9 @@ /* Section footer Autolock description */ "settings.autolock.description" = "Si Touch ID, Face ID ou un code d'accès au système est activé, il vous sera demandé de déverrouiller l'application lors de son ouverture."; +/* Settings cell for Browser Feedback */ +"settings.browser.feedback" = "Commentaires sur le navigateur"; + /* Settings screen cell text for Automatically Clearing Data */ "settings.clear.data" = "Effacer automatiquement les données"; @@ -2182,7 +2350,7 @@ "settings.ppro" = "Privacy Pro"; /* Title for Link in the Footer of Privacy Pro section */ -"settings.ppro.footer" = "Privacy Policy and Terms of Service"; +"settings.ppro.footer" = "Politique de confidentialité et conditions d'utilisation"; /* Settings screen cell for long press previews */ "settings.previews" = "Aperçus obtenus en appuyant longuement"; @@ -2208,23 +2376,20 @@ /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Définissez la position de votre barre d'adresse"; -/* Subscription activation pending description */ -"settings.subscription.activation.pending.description" = "This is taking longer than usual, please check back later."; +/* Privacy pro description subtitle in settings when the is activating */ +"settings.subscription.activating" = "Activation en cours"; -/* Subscription activation pending title */ -"settings.subscription.activation.pending.title" = "Votre abonnement est en cours d’activation"; +/* Subscription activation pending description */ +"settings.subscription.activation.pending.description" = "Cela prend plus de temps que la normale ; veuillez revenir plus tard."; /* Data Broker protection cell subtitle for privacy pro */ -"settings.subscription.DBP.subtitle" = "Remove your info from sites that sell it"; +"settings.subscription.DBP.subtitle" = "Supprimez vos infos des sites qui les vendent"; /* Data Broker protection cell title for privacy pro */ "settings.subscription.DBP.title" = "Personal Information Removal"; -/* Privacy pro description subtext */ -"settings.subscription.description" = "Plus de confidentialité en continu avec trois nouvelles protections :"; - /* I have a Subscription button text for privacy pro */ -"settings.subscription.existing.subscription" = "I Have a Subscription"; +"settings.subscription.existing.subscription" = "J'ai un abonnement"; /* Subscription expired description */ "settings.subscription.expired.comment" = "Abonnez-vous à nouveau pour continuer à utiliser Privacy Pro"; @@ -2232,23 +2397,26 @@ /* Subscription expired tittle message */ "settings.subscription.expired.title" = "Votre abonnement Privacy Pro a expiré"; -/* Privacy pro features list */ -"settings.subscription.features" = " • VPN\n • Personal Information Removal\n • Identity Theft Restoration"; - /* Identity theft restoration cell subtitle for privacy pro */ -"settings.subscription.ITR.subtitle" = "If your identity is stolen, we'll help restore it"; +"settings.subscription.ITR.subtitle" = "Si votre identité a été usurpée, nous vous aiderons à la rétablir"; /* Identity theft restoration cell title for privacy pro */ "settings.subscription.ITR.title" = "Identity Theft Restoration"; /* Get Privacy Pro button text for privacy pro */ -"settings.subscription.learn.more" = "Get Privacy Pro"; +"settings.subscription.learn.more" = "Obtenir Privacy Pro"; /* Subscription Settings button text for privacy pro */ -"settings.subscription.manage" = "Subscription Settings"; +"settings.subscription.manage" = "Paramètres d'abonnement"; + +/* Privacy Pro description subtitle in settings */ +"settings.subscription.row.description" = "Comprend notre VPN et Identity Theft Restoration."; -/* Call to action title for Privacy Pro */ -"settings.subscription.subscribe" = "Subscribe to Privacy Pro"; +/* Call to action title for Privacy Pro settings */ +"settings.subscription.subscribe" = "Protégez votre connexion et votre identité avec Privacy Pro"; + +/* Privacy pro description subtitle in settings */ +"settings.subscription.us.description" = "Comprend notre VPN, Personal Information Removal et Identity Theft Restoration."; /* VPN cell title for privacy pro */ "settings.subscription.VPN.title" = "VPN"; @@ -2317,67 +2485,64 @@ "siteFeedback.urlPlaceholder" = "Quel site Web pose problème ?"; /* Subscription Activation Info */ -"subscription.activate..header.description" = "Access your Privacy Pro subscription on this device via Apple ID or an email address."; +"subscription.activate..header.description" = "Accédez à votre abonnement Privacy Pro sur cet appareil via un identifiant Apple ou via une adresse e-mail."; /* Button for adding email address to subscription */ -"subscription.activate.add.email.button" = "Add Email"; +"subscription.activate.add.email.button" = "Ajouter un e-mail"; /* Apple ID option for activation */ -"subscription.activate.appleid" = "Apple ID"; +"subscription.activate.appleid" = "Identifiant Apple"; /* Button text for restoring purchase via Apple ID */ -"subscription.activate.appleid.button" = "Restore Purchase"; +"subscription.activate.appleid.button" = "Rétablir l'achat"; /* Description for Apple ID activation */ -"subscription.activate.appleid.description" = "Restore your purchase to activate your subscription on this device."; +"subscription.activate.appleid.description" = "Rétablissez votre achat pour activer votre abonnement sur cet appareil."; /* Subscription Activation Info */ -"subscription.activate.description" = "Your subscription is automatically available in DuckDuckGo on any device signed in to your Apple ID."; +"subscription.activate.description" = "Votre abonnement est automatiquement disponible dans DuckDuckGo sur n'importe quel appareil connecté à votre identifiant Apple."; /* Button for editing email address added to subscription */ -"subscription.activate.edit.email.button" = "Edit Email"; +"subscription.activate.edit.email.button" = "Modifier l'email"; /* View Title for editing your email account */ -"subscription.activate.edit.email.title" = "Edit Email"; +"subscription.activate.edit.email.title" = "Modifier l'email"; /* Email option for activation */ -"subscription.activate.email" = "Email"; +"subscription.activate.email" = "Adresse électronique"; /* Restore button title for Email */ -"subscription.activate.email.button" = "Enter Email"; +"subscription.activate.email.button" = "Saisir votre e-mail"; /* Description for Email activation */ -"subscription.activate.email.description" = "Use your email to activate your subscription on this device."; +"subscription.activate.email.description" = "Utilisez votre adresse e-mail pour activer votre abonnement sur cet appareil."; /* Activate subscription title */ "subscription.activate.email.title" = "Activez l'abonnement"; /* Button title for cancelling email deletion */ -"subscription.activate.manage.email.cancel" = "Cancel"; +"subscription.activate.manage.email.cancel" = "Annuler"; /* Button title for confirming email deletion */ "subscription.activate.manage.email.OK" = "OK"; /* Restore button title for AppleID */ -"subscription.activate.restore.apple" = "Restore Purchase"; +"subscription.activate.restore.apple" = "Rétablir l'achat"; /* Subscription Activation Title */ -"subscription.activate.title" = "Activate your subscription on this device"; +"subscription.activate.title" = "Activez votre abonnement sur cet appareil"; /* Resend activation instructions button */ -"subscription.add.device.resend.instructions" = "Resend Instructions"; +"subscription.add.device.resend.instructions" = "Renvoyer les instructions"; /* Add email to an existing subscription */ -"subscription.add.email" = "Add an email address to activate your subscription on your other devices. We’ll only use this address to verify your subscription."; +"subscription.add.email" = "Ajoutez une adresse e-mail pour activer votre abonnement sur vos autres appareils. Nous n’utiliserons cette adresse que pour confirmer votre abonnement."; /* View title for adding email to subscription */ -"subscription.add.email.title" = "Add Email"; - -/* Title for Alert messages */ -"subscription.alert.title" = "subscription.alert.title"; +"subscription.add.email.title" = "Ajouter un e-mail"; /* Subscription availability message on Apple devices */ -"subscription.available.apple" = "Privacy Pro is available on any device signed in to the same Apple ID."; +"subscription.available.apple" = "Privacy Pro est disponible sur tous les appareils connectés au même identifiant Apple."; /* Text for the manage billing page */ "subscription.billing.google.text" = "Votre abonnement a été acheté via le Google Play Store. Pour renouveler votre abonnement, ouvrez les paramètres d'abonnement du Google Play Store sur un appareil connecté au même compte Google que celui utilisé lors de l'achat initial de votre abonnement."; @@ -2385,89 +2550,86 @@ /* Title for the manage billing page */ "subscription.billing.google.title" = "Forfaits d'abonnement"; -/* Subscription annual billing period type */ -"subscription.billing.period.annual" = "annual"; - -/* Subscription monthly billing period type */ -"subscription.billing.period.monthly" = "monthly"; - /* Subscription Removal confirmation message */ -"subscription.cancel.message" = "Your subscription has been removed from this device."; +"subscription.cancel.message" = "Votre abonnement a été supprimé de cet appareil."; /* Change plan or cancel title */ -"subscription.change.plan" = "Update Plan or Cancel"; +"subscription.change.plan" = "Modifier le forfait ou le résilier"; /* Navigation Button for closing subscription view */ -"subscription.close" = "Close"; +"subscription.close" = "Fermer"; /* Title for Confirm messages */ -"subscription.confirm.title" = "Are you sure?"; +"subscription.confirm.title" = "Confirmez-vous ?"; /* Header for section for activating subscription on other devices */ -"subscription.devices.header" = "Activate on Other Devices"; +"subscription.devices.header" = "Activer sur d'autres appareils"; /* Footer for section for activating subscription on other devices when email was not yet added */ -"subscription.devices.no.email.footer" = "Add an optional email to your subscription or use your Apple ID to access Privacy Pro on other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.no.email.footer" = "Ajoutez un e-mail facultatif à votre abonnement pour accéder à Privacy Pro sur d'autres appareils. **[En savoir plus](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Footer for section for activating subscription on other devices when email is added */ -"subscription.devices.with.email.footer" = "Use this email to activate your subscription in Settings > Privacy Pro in the DuckDuckGo app on your other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.with.email.footer" = "Utilisez cet e-mail pour activer votre abonnement dans Réglages > Privacy Pro dans l'application DuckDuckGo sur vos autres appareils. **[En savoir plus](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Alert content for not found subscription */ -"subscription.email.inactive.alert.message" = "The subscription associated with this email is no longer active."; +"subscription.email.inactive.alert.message" = "L'abonnement associé à cet e-mail n'est plus actif."; /* Alert title for not found subscription */ -"subscription.email.inactive.alert.title" = "Subscription Not Found"; +"subscription.email.inactive.alert.title" = "Abonnement introuvable"; /* Alert content for not found subscription */ -"subscription.expired.alert.message" = "The subscription associated with this Apple ID is no longer active."; +"subscription.expired.alert.message" = "L'abonnement associé à cet identifiant Apple n'est plus actif."; /* FAQ Button */ -"subscription.faq" = "FAQs and Support"; +"subscription.faq" = "FAQ et assistance"; /* FAQ Description */ -"subscription.faq.description" = "Get answers to frequently asked questions or contact Privacy Pro support from our help pages."; +"subscription.faq.description" = "Trouvez les réponses aux questions fréquemment posées ou contactez l'assistance de Privacy Pro sur nos pages d'aide."; + +/* Send Feedback Button */ +"subscription.feedback" = "Envoyer vos remarques"; /* Cancel action for the existing subscription dialog */ -"subscription.found.cancel" = "Cancel"; +"subscription.found.cancel" = "Annuler"; /* Restore action for the existing subscription dialog */ -"subscription.found.restore" = "Restore"; +"subscription.found.restore" = "Rétablir"; /* Message for the existing subscription dialog */ -"subscription.found.text" = "We found a subscription associated with this Apple ID."; +"subscription.found.text" = "Nous avons trouvé un abonnement associé à cet identifiant Apple."; /* Title for the existing subscription dialog */ -"subscription.found.title" = "Subscription Found"; +"subscription.found.title" = "Abonnement trouvé"; /* Help and support Section header */ -"subscription.help" = "Help and support"; +"subscription.help" = "Aide et assistance"; /* Manage Plan header */ -"subscription.manage.plan" = "Manage Plan"; +"subscription.manage.plan" = "Gérer le forfait"; /* Header for the subscription section */ -"subscription.manage.title" = "Subscription"; +"subscription.manage.title" = "Abonnement"; /* Alert content for not found subscription */ -"subscription.notFound.alert.message" = "There is no subscription associated with this Apple ID."; +"subscription.notFound.alert.message" = "Aucun abonnement n'est associé à cet identifiant Apple."; /* Alert title for not found subscription */ -"subscription.notFound.alert.title" = "Subscription Not Found"; +"subscription.notFound.alert.title" = "Abonnement introuvable"; /* View plans button text */ -"subscription.notFound.view.plans" = "View Plans"; +"subscription.notFound.view.plans" = "Voir les forfaits"; /* Hero Text for Personal information removal */ -"subscription.pir.hero" = "Activate Privacy Pro on desktop to set up Personal Information Removal"; +"subscription.pir.hero" = "Activez Privacy Pro sur ordinateur pour configurer Personal Information Removal"; /* Description on how to use Personal information removal in desktop. The first placeholder references a location in the Desktop application. Privacy Pro>, and the second, the menu entry. i.e. */ -"subscription.pir.heroText" = "In the DuckDuckGo browser for desktop, go to %1$@ and click %2$@ to get started."; +"subscription.pir.heroText" = "Dans le navigateur DuckDuckGo pour ordinateur de bureau, accédez à %1$@ et cliquez sur %2$@ pour commencer."; /* Settings references a menu in the Desktop app, Privacy Pro, references our product name */ -"subscription.pir.heroTextLocation" = "Settings > Privacy Pro"; +"subscription.pir.heroTextLocation" = "Réglages > Privacy Pro"; /* Menu item for enabling Personal Information Removal on Desktop */ -"subscription.pir.heroTextMenyEntry" = "I have a subscription"; +"subscription.pir.heroTextMenyEntry" = "J'ai un abonnement"; /* Text for the 'macOS' button */ "subscription.pir.macos" = "Mac"; @@ -2476,28 +2638,28 @@ "subscription.pir.windows" = "Windows"; /* Progress view title when completing the purchase */ -"subscription.progress.view.completing.purchase" = "Completing purchase..."; +"subscription.progress.view.completing.purchase" = "Finalisation de l’achat en cours…"; /* Progress view title when starting the purchase */ -"subscription.progress.view.purchasing.subscription" = "Purchase in progress..."; +"subscription.progress.view.purchasing.subscription" = "Achat en cours…"; /* Progress view title when restoring past subscription purchase */ -"subscription.progress.view.restoring.subscription" = "Restoring subscription..."; +"subscription.progress.view.restoring.subscription" = "Rétablissement de l'abonnement…"; /* Remove from this device button */ -"subscription.remove.from.device.button" = "Remove From This Device"; +"subscription.remove.from.device.button" = "Supprimer de cet appareil"; /* Remove from device confirmation dialog text */ -"subscription.remove.from.device.text" = "You will no longer be able to access your Privacy Pro subscription on this device. This will not cancel your subscription, and it will remain active on your other devices."; +"subscription.remove.from.device.text" = "Vous ne pourrez plus accéder à votre abonnement Privacy Pro sur cet appareil. Cela n'annulera pas votre abonnement et il restera actif sur vos autres appareils."; /* Remove from device confirmation dialog title */ -"subscription.remove.from.device.title" = "Remove from this device?"; +"subscription.remove.from.device.title" = "Supprimer de cet appareil ?"; /* Remove subscription button text */ -"subscription.remove.subscription" = "Remove Subscription"; +"subscription.remove.subscription" = "Supprimer l'abonnement"; /* Remove subscription cancel button text */ -"subscription.remove.subscription.cancel" = "Cancel"; +"subscription.remove.subscription.cancel" = "Annuler"; /* Button text for general error message */ "subscription.restore.backend.error.button" = "Retour aux paramètres"; @@ -2509,28 +2671,43 @@ "subscription.restore.backend.error.title" = "Une erreur s'est produite"; /* Alert for general error message */ -"subscription.restore.general.error.message" = "The App Store was unable to process your purchase. Please try again later."; +"subscription.restore.general.error.message" = "L'App Store n'a pas pu traiter votre achat. Veuillez réessayer plus tard."; /* Alert for general error title */ -"subscription.restore.general.error.title" = "Something Went Wrong"; +"subscription.restore.general.error.title" = "Une erreur s'est produite"; /* Alert button text for restored purchase alert */ "subscription.restore.success.alert.button" = "OK"; /* Alert message for restored purchase */ -"subscription.restore.success.alert.message" = "Your purchases have been restored."; +"subscription.restore.success.alert.message" = "Vos achats ont été rétablis."; /* Alert title for restored purchase */ -"subscription.restore.success.alert.title" = "You’re all set."; +"subscription.restore.success.alert.title" = "Tout est prêt !"; + +/* Subtitle in header when subscribed */ +"subscription.subscribed" = "Abonné(e)"; /* Subscription Expired Data. This reads as 'Your subscription expired on (date)' */ "subscription.subscription.expired.caption" = "Votre abonnement a expiré le % @"; -/* Subscription expiration info. This reads as 'Your (monthly or annual) subscription expires on (date)' */ -"subscription.subscription.expiring.caption" = "Your %1$@ subscription expires on %2$@."; +/* Monthly subscription expiration info where parameter is expiration date. This reads as 'Your monthly subscription expires on (date)' */ +"subscription.subscription.expiring.monthly.caption" = "Votre abonnement mensuel expire le %@."; + +/* Unknown period subscription expiration info where parameter is expiration date. This reads as 'Your subscription expires on (date)' */ +"subscription.subscription.expiring.unknown.caption" = "Votre abonnement expire le %@."; + +/* Annual subscription expiration info where parameter is expiration date. This reads as 'Your annual subscription expires on (date)' */ +"subscription.subscription.expiring.yearly.caption" = "Votre abonnement annuel expire le %@."; + +/* Monthly subscription renewal info where parameter is renewal date. This reads as 'Your monthly subscription renews on (date)' */ +"subscription.subscription.renewing.monthly.caption" = "Votre abonnement mensuel sera renouvelé le %@."; + +/* Unknown period subscription renewal info where parameter is renewal date. This reads as 'Your subscription renews on (date)' */ +"subscription.subscription.renewing.unknown.caption" = "Votre abonnement sera renouvelé le %@."; -/* Subscription renewal info. This reads as 'Your (monthly or annual) subscription renews on (date)' */ -"subscription.subscription.renewing.caption" = "Your %1$@ subscription renews on %2$@."; +/* Annual subscription renewal info where parameter is renewal date. This reads as 'Your annual subscription renews on (date)' */ +"subscription.subscription.renewing.yearly.caption" = "Votre abonnement annuel sera renouvelé le %@."; /* Navigation bar Title for subscriptions */ "subscription.title" = "Privacy Pro"; @@ -2677,118 +2854,118 @@ "voiceSearch.footer.note.old" = "L'audio est traité directement sur l'appareil. Il n'est ni stocké ni partagé avec qui que ce soit, y compris DuckDuckGo."; /* Cancel action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.cancel" = "Dismiss"; +"vpn.access-revoked.alert.action.cancel" = "Ignorer"; /* Primary action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.subscribe" = "Subscribe"; +"vpn.access-revoked.alert.action.subscribe" = "S'abonner"; /* Alert message for the alert when the Privacy Pro subscription expiress */ -"vpn.access-revoked.alert.message" = "Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"vpn.access-revoked.alert.message" = "Abonnez-vous à Privacy Pro pour reconnecter le VPN DuckDuckGo."; /* Alert title for the alert when the Privacy Pro subscription expires */ -"vpn.access-revoked.alert.title" = "VPN disconnected due to expired subscription"; +"vpn.access-revoked.alert.title" = "VPN déconnecté en raison de l'expiration de l'abonnement"; /* Title for the VPN widget onboarding screen */ "vpn.addWidget.settings.title" = "Trouvez et sélectionnez DuckDuckGo. Glissez ensuite vers VPN et sélectionnez Ajouter un widget."; /* Title for the Cancel button of the VPN feedback form */ -"vpn.feedback-form.button.cancel" = "Cancel"; +"vpn.feedback-form.button.cancel" = "Annuler"; /* Title for the Done button of the VPN feedback form */ -"vpn.feedback-form.button.done" = "Done"; +"vpn.feedback-form.button.done" = "Terminé"; /* Title for the Submit button of the VPN feedback form */ -"vpn.feedback-form.button.submit" = "Submit"; +"vpn.feedback-form.button.submit" = "Envoyer"; /* Title for the Submitting state of the VPN feedback form */ -"vpn.feedback-form.button.submitting" = "Submitting…"; +"vpn.feedback-form.button.submitting" = "Envoi en cours…"; /* Title for the browser crash/freeze category of the VPN feedback form */ -"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN causes browser to crash or freeze"; +"vpn.feedback-form.category.browser-crash-or-freeze" = "Le VPN provoque le plantage ou le blocage du navigateur"; /* Title for the 'VPN fails to connect' category of the VPN feedback form */ -"vpn.feedback-form.category.fails-to-connect" = "VPN fails to connect"; +"vpn.feedback-form.category.fails-to-connect" = "Le VPN ne parvient pas à se connecter"; /* Title for the 'VPN feature request' category of the VPN feedback form */ -"vpn.feedback-form.category.feature-request" = "VPN feature request"; +"vpn.feedback-form.category.feature-request" = "Demande de fonctionnalité VPN"; /* Title for the category 'VPN causes issues with other apps or websites' category of the VPN feedback form */ -"vpn.feedback-form.category.issues-with-apps" = "VPN causes issues with other apps or websites"; +"vpn.feedback-form.category.issues-with-apps" = "Le VPN est à l'origine de problèmes avec d'autres applications ou sites Web"; /* Title for the local device connectivity category of the VPN feedback form */ -"vpn.feedback-form.category.local-device-connectivity" = "VPN won't let me connect to local device"; +"vpn.feedback-form.category.local-device-connectivity" = "Le VPN ne me permet pas de me connecter à un appareil local"; /* Title for the 'other VPN feedback' category of the VPN feedback form */ -"vpn.feedback-form.category.other" = "Other VPN feedback"; +"vpn.feedback-form.category.other" = "Autres commentaires sur le VPN"; /* Title for the category selection state of the VPN feedback form */ -"vpn.feedback-form.category.select-category" = "Select a category"; +"vpn.feedback-form.category.select-category" = "SÉLECTIONNER UNE CATÉGORIE"; /* Title for the 'VPN is too slow' category of the VPN feedback form */ -"vpn.feedback-form.category.too-slow" = "VPN connection is too slow"; +"vpn.feedback-form.category.too-slow" = "La connexion VPN est trop lente"; /* Title for the 'unable to install' category of the VPN feedback form */ -"vpn.feedback-form.category.unable-to-install" = "Unable to install VPN"; +"vpn.feedback-form.category.unable-to-install" = "Impossible d'installer le VPN"; /* Title for the feedback sent view description of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.description" = "Your feedback will help us improve the\nDuckDuckGo VPN."; +"vpn.feedback-form.sending-confirmation.description" = "Vos commentaires nous aideront à améliorer le VPN DuckDuckGo."; /* Title for the feedback sending error text of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.error" = "We couldn't send your feedback right now, please try again."; +"vpn.feedback-form.sending-confirmation.error" = "Nous n'avons pas pu envoyer vos commentaires pour le moment ; veuillez réessayer."; /* Title for the feedback sent view title of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.title" = "Thank you!"; +"vpn.feedback-form.sending-confirmation.title" = "Merci !"; /* Toast message when the VPN feedback form is submitted successfully */ -"vpn.feedback-form.submitted.message" = "Thank You! Feedback submitted."; +"vpn.feedback-form.submitted.message" = "Merci ! Commentaire envoyé."; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-1" = "Please describe what's happening, what you expected to happen, and the steps that led to the issue:"; +"vpn.feedback-form.text-1" = "Veuillez décrire ce qui se passe, ce qui aurait dû se passer et les différentes étapes qui ont précédé le problème :"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-2" = "In addition to the details entered into this form, your app issue report will contain:"; +"vpn.feedback-form.text-2" = "Outre les renseignements saisis dans ce formulaire, votre rapport sur les problèmes de l'application contiendra les informations suivantes :"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-3" = "• Whether specific DuckDuckGo features are enabled"; +"vpn.feedback-form.text-3" = "• Si des fonctionnalités spécifiques de DuckDuckGo sont actives"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-4" = "• Aggregate DuckDuckGo app diagnostics"; +"vpn.feedback-form.text-4" = "• Les diagnostics agrégés de l'application DuckDuckGo"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-5" = "By tapping \"Submit\" I agree that DuckDuckGo may use the information in this report for purposes of improving the app's features."; +"vpn.feedback-form.text-5" = "En appuyant sur « Envoyer », j'accepte que DuckDuckGo puisse utiliser les informations contenues dans ce rapport afin d'améliorer les fonctionnalités de l'application."; /* Title for each screen of the VPN feedback form */ -"vpn.feedback-form.title" = "Help Improve the DuckDuckGo VPN"; +"vpn.feedback-form.title" = "Participez à l'amélioration du VPN DuckDuckGo"; /* VPN settings screen cell text for adding the VPN widget to the home screen */ "vpn.settings.add.widget" = "Ajouter le widget VPN à l'écran d'accueil"; /* Disclaimer for the DNS Server section on the DNS Server screen */ -"vpn.settings.dns.section-disclaimer" = "Using a custom DNS server can impact browsing speeds and expose your activity to third parties if the server isn't secure or reliable."; +"vpn.settings.dns.section-disclaimer" = "L'utilisation d'un serveur DNS personnalisé peut avoir un impact sur la vitesse de navigation et exposer votre activité à des tiers si le serveur n'est pas sécurisé ou fiable."; /* Header text for the DNS section on the VPN Settings screen */ "vpn.settings.dns.section-header" = "DNS"; /* Title for the Apply button on the DNS Server setting screen */ -"vpn.settings.dns.server.apply.button.title" = "Apply"; +"vpn.settings.dns.server.apply.button.title" = "Appliquer"; /* Default value for the DNS Server row on the VPN Settings screen */ "vpn.settings.dns.server.default.value" = "DuckDuckGo"; /* Title for the IPv4 Address setting */ -"vpn.settings.dns.server.ipv4.title" = "IPv4 Address"; +"vpn.settings.dns.server.ipv4.title" = "Adresse IPv4"; /* Custom option for the DNS Server setting */ -"vpn.settings.dns.server.option.custom" = "Custom"; +"vpn.settings.dns.server.option.custom" = "Personnalisé"; /* Recommended option for the DNS Server setting */ -"vpn.settings.dns.server.option.default" = "DuckDuckGo (Recommended)"; +"vpn.settings.dns.server.option.default" = "DuckDuckGo (recommandé)"; /* Title for the DNS Server setting screen */ -"vpn.settings.dns.server.screen.title" = "DNS Server"; +"vpn.settings.dns.server.screen.title" = "Serveur DNS"; /* Title for the DNS Server row on the VPN Settings screen */ -"vpn.settings.dns.server.title" = "DNS Server"; +"vpn.settings.dns.server.title" = "Serveur DNS"; /* Title for the button to enable push notifications in system settings */ "waitlist.allow-notifications" = "Autoriser les notifications"; diff --git a/DuckDuckGo/fr.lproj/OmniBar.strings b/DuckDuckGo/fr.lproj/OmniBar.strings index 408372b3da..fe32acf288 100644 --- a/DuckDuckGo/fr.lproj/OmniBar.strings +++ b/DuckDuckGo/fr.lproj/OmniBar.strings @@ -7,6 +7,9 @@ /* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "Dt8-We-5tV"; */ "Dt8-We-5tV.accessibilityLabel" = "Effacer le texte"; +/* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "gb5-wL-4OW"; */ +"gb5-wL-4OW.accessibilityLabel" = "Effacer le texte"; + /* Class = "UIButton"; accessibilityLabel = "Share"; ObjectID = "hor-Jd-3kw"; */ "hor-Jd-3kw.accessibilityLabel" = "Partager"; diff --git a/DuckDuckGo/fr.lproj/Settings.strings b/DuckDuckGo/fr.lproj/Settings.strings index d31e63f098..a19290e77b 100644 --- a/DuckDuckGo/fr.lproj/Settings.strings +++ b/DuckDuckGo/fr.lproj/Settings.strings @@ -10,9 +10,6 @@ /* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "5YD-Od-uDU"; */ "5YD-Od-uDU.text" = "Effacer automatiquement les données"; -/* Class = "UIButton"; normalTitle = "Settings"; ObjectID = "8YG-7Q-Czd"; */ -"8YG-7Q-Czd.normalTitle" = "Paramètres"; - /* Class = "UILabel"; text = "App Exit, Inactive for 1 Hour"; ObjectID = "9EC-lx-77p"; */ "9EC-lx-77p.text" = "Quitter l'application, inactivité pendant 1 heure"; @@ -76,9 +73,6 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Sites non protégés"; -/* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ -"ssa-zd-L3T.title" = "Taille du texte"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Afficher le clavier"; diff --git a/DuckDuckGo/hr.lproj/DaxOnboarding.strings b/DuckDuckGo/hr.lproj/DaxOnboarding.strings index 4a2becfda2..5fd9c4bf63 100644 --- a/DuckDuckGo/hr.lproj/DaxOnboarding.strings +++ b/DuckDuckGo/hr.lproj/DaxOnboarding.strings @@ -1,12 +1,6 @@ /* Class = "UIButton"; normalTitle = "Hide"; ObjectID = "hgy-75-OvJ"; */ "hgy-75-OvJ.normalTitle" = "Sakrij"; -/* Class = "UILabel"; text = "Welcome to\nDuckDuckGo!"; ObjectID = "Lvy-9G-3gr"; */ -"Lvy-9G-3gr.text" = "Dobro došao/la u\nDuckDuckGo!"; - -/* Class = "UIButton"; normalTitle = "Let’s Do It!"; ObjectID = "oAv-Zt-uIt"; */ -"oAv-Zt-uIt.normalTitle" = "Učinimo to!"; - /* Class = "UIButton"; normalTitle = "Button"; ObjectID = "zJt-F9-L48"; */ "zJt-F9-L48.normalTitle" = "Gumb"; diff --git a/DuckDuckGo/hr.lproj/Localizable.strings b/DuckDuckGo/hr.lproj/Localizable.strings index cae52c7a73..5c79140a66 100644 --- a/DuckDuckGo/hr.lproj/Localizable.strings +++ b/DuckDuckGo/hr.lproj/Localizable.strings @@ -341,7 +341,7 @@ "autofill.keep-enabled.alert.disable" = "Onemogući"; /* A link that takes the user to the DuckDuckGo help pages explaining password managers */ -"autofill.learn.more.link.title" = "Learn More"; +"autofill.learn.more.link.title" = "Saznajte više"; /* Button displayed after saving/updating an autofill login that takes the user to the saved login */ "autofill.login-save-action-button.toast" = "Pregled"; @@ -977,9 +977,6 @@ /* ad = advertisment */ "dax.onboarding.home.subsequent" = "Možeš ti to!\n\nZapamtite: svaki put kada pregledavate sa mnom jeziva reklama gubi krila. 👍"; -/* No comment provided by engineer. */ -"dax.onboarding.message" = "Internet može biti pomalo neugodan.\n\nNe brini! Privatno pretraživanje i pregledavanje lakše je nego što misliš."; - /* No comment provided by engineer. */ "Debug" = "Ispravi pogreške"; @@ -1080,7 +1077,7 @@ "duckPlayer.never.label" = "Nikada"; /* Body text for the modal feature explanation */ -"duckplayer.presentation.modal.body" = "Duck Player omogućuje ti gledanje YouTubea bez ciljanih oglasa, što ti pruža doživljaj nalik kinu u DuckDuckGou, a ono što gledaš neće utjecati na tvoje preporuke."; +"duckplayer.presentation.modal.body" = "Duck Player ti omogućuje gledanje YouTubea bez ciljanih oglasa u DuckDuckGou, a ono što gledaš neće utjecati na tvoje preporuke."; /* Button that will dismiss the modal */ "duckplayer.presentation.modal.dismiss-button" = "Shvaćam!"; @@ -1088,17 +1085,23 @@ /* Two line title (separated by \n) for the feature explanation */ "duckplayer.presentation.modal.title" = "Utapaš se u oglasima na YouTubeu? Isprobaj Duck Player!"; +/* Two line title (separated by \n) for the feature explanation */ +"duckplayer.presentation.modal.title.youtube" = "Utapaš se u oglasima na YouTubeu? Ne s Duck Playerom!"; + /* Footer label in the settings screen for Duck Player */ "duckplayer.settings.footer" = "DuckDuckGo pruža sve bitne elemente Privacy Essentials koje su ti potrebne da se zaštitiš dok pregledavaš web."; /* Text explaining what Duck Player is in the settings screen. */ -"duckplayer.settings.info-text" = "Duck Player pruža čisti doživljaj gledanja bez personaliziranih oglasa i sprječava da aktivnosti gledanja utječu na tvoje preporuke na YouTubeu."; +"duckplayer.settings.info-text" = "Duck Player ti omogućuje gledanje YouTubea bez ciljanih oglasa u DuckDuckGou, a ono što gledaš neće utjecati na tvoje preporuke."; /* Button that takes the user to learn more about Duck Player. */ "duckplayer.settings.learn-more" = "Saznajte više"; +/* Settings screen cell text for DuckPlayer settings to open in new tab */ +"duckplayer.settings.open-new-tab-label" = "Otvori Duck Player u novoj kartici"; + /* Settings screen cell text for DuckPlayer settings */ -"duckplayer.settings.open-videos-in" = "Otvaraj videozapise u Duck Playeru"; +"duckplayer.settings.open-videos-in" = "Otvaraj YouTube videozapise u Duck Playeru"; /* Settings screen cell text for DuckPlayer settings */ "duckplayer.settings.title" = "Duck Player"; @@ -1169,9 +1172,6 @@ /* Title for prompt to sign up for email protection */ "email.signup-prompt.title" = "Sakrij svoju e-poštu i \n blokiraj tragače"; -/* No comment provided by engineer. */ -"empty!" = "empty!"; - /* Empty list state placholder */ "empty.bookmarks" = "Još nema dodanih knjižnih oznaka"; @@ -1322,6 +1322,39 @@ /* Confirmation button */ "feedback.form.submit" = "Pošalji"; +/* Title of the feedback form */ +"feedback.form.title" = "Pomogni poboljšati Privacy Pro"; + +/* Category for Identity Theft Restoration feedback */ +"feedback.general.category.itr" = "Identity Theft Restoration"; + +/* Category for Personal Info Removal feedback */ +"feedback.general.category.pir" = "Uklanjanje osobnih podataka"; + +/* Category for subscription and payments feedback */ +"feedback.general.category.ppro" = "Pretplata i plaćanja"; + +/* Prompt to select a category for general feedback */ +"feedback.general.category.select" = "ODABERI KATEGORIJU"; + +/* Category for VPN feedback */ +"feedback.general.category.vpn" = "VPN"; + +/* Category for access code issues */ +"feedback.itr.category.access-code" = "Problem s pristupnim kodom"; + +/* Category for issues contacting an advisor */ +"feedback.itr.category.cant-contact-advisor" = "Nije moguće kontaktirati savjetnika"; + +/* Prompt to select a category for Identity Theft Restoration feedback */ +"feedback.itr.category.select" = "ODABERI KATEGORIJU"; + +/* Category for other Identity Theft Restoration issues */ +"feedback.itr.category.something-else" = "Nešto drugo"; + +/* Category for unhelpful advisor calls */ +"feedback.itr.category.unhelpful" = "Poziv savjetniku nije bio od pomoći"; + /* No comment provided by engineer. */ "feedback.negative.form.genericPlaceholder" = "Budi što precizniji/a"; @@ -1361,6 +1394,24 @@ /* No comment provided by engineer. */ "feedback.performance.slowLoading" = "Web-stranice ili rezultati pretraživanja učitavaju se sporo"; +/* Category for when scan finds incorrect records */ +"feedback.pir.category.not-me" = "Skeniranje je pronašlo zapise koji nisu povezani sa mnom"; + +/* Category for when scan doesn't find info on a specific site */ +"feedback.pir.category.nothing-on-site" = "Skeniranje nije pronašlo moje podatke na određenom web-mjestu"; + +/* Category for other Personal Info Removal issues */ +"feedback.pir.category.other" = "Nešto drugo"; + +/* Category for when the removal process is stuck */ +"feedback.pir.category.removal-stuck" = "Proces uklanjanja ne može nastaviti"; + +/* Category for when the scan is stuck */ +"feedback.pir.category.scan-stuck" = "Skeniranje zapisa ne može nastaviti"; + +/* Prompt to select a category for Personal Info Removal feedback */ +"feedback.pir.category.select" = "ODABERI KATEGORIJU"; + /* Header above input field */ "feedback.positive.form.header" = "Podijeli pojedinosti"; @@ -1379,6 +1430,15 @@ /* Button encouraging uses to share details aboout their feedback */ "feedback.positive.submit" = "Podijeli pojedinosti"; +/* Category for other Privacy Pro issues */ +"feedback.ppro.category.other" = "Nešto drugo"; + +/* Category for one-time password issues */ +"feedback.ppro.category.otp" = "Problem s jednokratnom lozinkom"; + +/* Prompt to select a category for Privacy Pro feedback */ +"feedback.ppro.category.select" = "ODABERI KATEGORIJU"; + /* No comment provided by engineer. */ "feedback.start.footer" = "Tvoje anonimne povratne informacije važne za su nas."; @@ -1529,12 +1589,6 @@ /* No comment provided by engineer. */ "keyCommandShowAllTabs" = "Prikaži sve kartice"; -/* Please preserve newline character */ -"launchscreenWelcomeMessage" = "Dobro došao/la u\nDuckDuckGo!"; - -/* No comment provided by engineer. */ -"LOREM IPSUM" = "LOREM IPSUM"; - /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "Aplikacija DuckDuckGo za Mac pruža ti potrebnu brzinu i značajke pretraživanja kakve očekuješ, a isporučuje se s našim najboljim alatima za privatnost u svojoj klasi - privacy essentials."; @@ -1586,62 +1640,83 @@ /* Edit button */ "navigation.title.edit" = "Uredi"; -/* String indicating NetP is connected when viewed from the settings screen */ -"netP.cell.connected" = "Connected"; - -/* String indicating NetP is disconnected when viewed from the settings screen */ -"netP.cell.disconnected" = "Not connected"; - -/* Title for the DuckDuckGo VPN feature in settings */ -"netP.settings.title" = "VPN"; +/* String indicating NetP is snoozing when viewed from the settings screen */ +"netP.cell.snoozing" = "U stanju mirovanja"; /* Title for the DuckDuckGo VPN feature */ "netP.title" = "DuckDuckGo VPN"; +/* Button title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.action" = "Dodaj widget"; + +/* Message for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.message" = "Uključite i isključite VPN izravno s početnog zaslona."; + +/* Title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.title" = "Dodaj VPN widget"; + /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN je prekinut zbog istekle pretplate. Pretplati se na Privacy Pro kako bi ponovno povezao DuckDuckGo VPN."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Mrežna zaštita nije se uspjela povezati. Pokušaj ponovno nešto kasnije."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "DuckDuckGo VPN nije se mogao povezati. Pokušaj ponovno nešto kasnije."; /* Title for the VPN FAQ screen. */ "network.protection.faq.title" = "Česta pitanja o DuckDuckGo VPN-u"; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Mrežna zaštita je prekinuta. Sada se pokušava ponovno povezati..."; +/* Message for tooltip about geoswitching */ +"network.protection.geoswitching.tip.message" = "Svoju VPN lokaciju možete prilagoditi povezivanjem s bilo kojim od naših poslužitelja širom svijeta."; + +/* Title for tooltip about geoswitching */ +"network.protection.geoswitching.tip.title" = "Promijeni svoju lokaciju"; + +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "DuckDuckGo VPN je prekinut. Sada se pokušava ponovno povezati..."; /* Message for the network protection invite dialog */ -"network.protection.invite.dialog.message" = "Enter your invite code to get started."; +"network.protection.invite.dialog.message" = "Za početak, unesi svoj pozivni kôd."; /* Title for the network protection invite screen */ "network.protection.invite.dialog.title" = "Imaš poziv da isprobaš DuckDuckGo VPN"; /* Prompt for the network protection invite code text field */ -"network.protection.invite.field.prompt" = "Invite Code"; +"network.protection.invite.field.prompt" = "Pozivni kôd"; /* Message for the network protection invite success view */ -"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; +"network.protection.invite.success.message" = "Sakrij svoju lokaciju od web-mjesta i svoju aktivnost na internetu od davatelja internetskih usluga i drugih na tvojoj mreži."; /* Title for the network protection invite success view */ -"network.protection.invite.success.title" = "Success! You’re in."; +"network.protection.invite.success.title" = "Uspjeh! Ušao si."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; /* Title text for an iOS quick action that opens VPN settings */ "network.protection.quick-action.open-vpn" = "Otvori VPN"; +/* Button title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.action" = "Saznajte više"; + +/* Message for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.message" = "Web stranice ili aplikacije koje blokiraju VPN promet možete upotrijebiti pauziranjem VPN veze."; + +/* Title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.title" = "Izbjegavaj VPN sukobe"; + +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN je u stanju mirovanja %@"; + /* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ "network.protection.status.connected.format" = "Povezano · %@"; /* The label for the NetP VPN when connecting */ -"network.protection.status.connecting" = "Connecting..."; +"network.protection.status.connecting" = "Povezivanje u tijeku..."; /* The label for the NetP VPN when disconnected */ -"network.protection.status.disconnected" = "Not connected"; +"network.protection.status.disconnected" = "Nije povezano"; /* The label for the NetP VPN when disconnecting */ -"network.protection.status.disconnecting" = "Disconnecting..."; +"network.protection.status.disconnecting" = "Prekidanje veze..."; /* Message label text for the status view when VPN is disconnected */ "network.protection.status.header.message.off" = "Poveži se i osiguraj internetski promet\nna svim svojim uređajima."; @@ -1655,26 +1730,41 @@ /* Header title label text for the status view when VPN is connected */ "network.protection.status.header.title.on" = "DuckDuckGo VPN je uključen"; +/* Header title label text for the status view when VPN is snoozing */ +"network.protection.status.header.title.snoozed" = "DuckDuckGo VPN je u stanju mirovanja"; + /* The status view 'Share Feedback' button which is shown inline on the status view after the temporary free use footer text */ -"network.protection.status.menu.share.feedback" = "Share Feedback"; +"network.protection.status.menu.share.feedback" = "Podijeli povratne informacije"; + +/* The label for the NetP VPN when paused */ +"network.protection.status.paused" = "Zaustavljen"; + +/* The label for when NetP VPN is snoozing plus the length of time remaining formatted as '0:00' */ +"network.protection.status.snoozing.format" = "U stanju mirovanja, preostaje %@"; + +/* Snooze button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.snooze" = "Stavi u stanje mirovanja na 20 minuta"; + +/* Wake Up button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.wake-up" = "Probudi"; /* Connection details label shown in NetworkProtection's status view. */ -"network.protection.status.view.connection.details" = "Connection Details"; +"network.protection.status.view.connection.details" = "Detalji o vezi"; /* Custom DNS label shown in NetworkProtection's status view. */ -"network.protection.status.view.custom.dns" = "DNS Server"; +"network.protection.status.view.custom.dns" = "DNS poslužitelj"; /* Generic connection failed error message shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.message" = "Please try again later."; +"network.protection.status.view.error.connection.failed.message" = "Pokušaj ponovno nešto kasnije."; /* Generic connection failed error title shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; +"network.protection.status.view.error.connection.failed.title" = "Povezivanje nije uspjelo."; /* IP Address label shown in NetworkProtection's status view. */ -"network.protection.status.view.ip.address" = "IP Address"; +"network.protection.status.view.ip.address" = "IP adresa"; /* Location label shown in NetworkProtection's status view. */ -"network.protection.status.view.location" = "Location"; +"network.protection.status.view.location" = "Lokacija"; /* Label shown on the title of the settings section in NetworkProtection's status view. */ "network.protection.status.view.settings.section.title" = "Upravljanje"; @@ -1682,12 +1772,15 @@ /* Title label text for the status view when netP is disconnected */ "network.protection.status.view.title" = "VPN"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Uključena je mrežna zaštita. Tvoja lokacija i online aktivnost su zaštićeni."; +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "DuckDuckGo VPN je uključen. Tvoja lokacija i aktivnosti na internetu zaštićeni su."; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Usmjeravanje prometa uređaja kroz %@."; +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "Mirovanje VPN-a je završilo. Usmjeravanje prometa uređaja kroz %@."; + /* Title for the button to link to the iOS app settings and enable notifications app-wide. */ "network.protection.turn.on.notifications.button.title" = "Uključi obavijesti"; @@ -1698,13 +1791,13 @@ "network.protection.vpn.about" = "O nama"; /* Section header for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.section.header" = "Notifications"; +"network.protection.vpn.alerts.section.header" = "Obavijesti"; /* List section footer for the toggle for VPN alerts. */ "network.protection.vpn.alerts.toggle.section.footer" = "Primaj obavijesti ako se tvoja veza prekine ili se status VPN-a promijeni."; /* Title for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.toggle.title" = "VPN Notifications"; +"network.protection.vpn.alerts.toggle.title" = "VPN obavijesti"; /* Title for the data volume section in the VPN status screen */ "network.protection.vpn.data-volume" = "Količina podataka"; @@ -1713,13 +1806,13 @@ "network.protection.vpn.exclude.local.networks.setting.footer" = "Neka lokalni promet zaobiđe VPN i poveže se s uređajima na tvojoj lokalnoj mreži, poput pisača."; /* Header text for the Exclude Local Networks setting item. */ -"network.protection.vpn.exclude.local.networks.setting.header" = "General"; +"network.protection.vpn.exclude.local.networks.setting.header" = "Općenito"; /* Title for the Exclude Local Networks setting item. */ "network.protection.vpn.exclude.local.networks.setting.title" = "Izostavi lokalne mreže"; /* Title for the VPN Location screen's All Countries section. */ -"network.protection.vpn.location.all.countries.section.title" = "All Countries"; +"network.protection.vpn.location.all.countries.section.title" = "Sve zemlje"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.connected" = "Povezana lokacija"; @@ -1734,7 +1827,7 @@ "network.protection.vpn.location.recommended.section.footer" = "Automatski se poveži s najbližim poslužiteljem koji možemo pronaći."; /* Title for the VPN Location screen's Recommended section. */ -"network.protection.vpn.location.recommended.section.title" = "Recommended"; +"network.protection.vpn.location.recommended.section.title" = "Preporučeno"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.selected" = "Odabrana lokacija"; @@ -1752,13 +1845,13 @@ "network.protection.vpn.preferred.location.nearest" = "Najbliža lokacija"; /* Title for the Preferred Location VPN Settings item. */ -"network.protection.vpn.preferred.location.title" = "Preferred Location"; +"network.protection.vpn.preferred.location.title" = "Preferirana lokacija"; /* Footer text for the DNS server setting item. */ -"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo routes DNS queries through our DNS servers so your internet provider can't see what websites you visit."; +"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo usmjerava DNS upite putem naših DNS poslužitelja tako da tvoj davatelj internetskih usluga ne može vidjeti koje web stranice posjećuješ."; /* Title for the FAQ row in the VPN status screen. */ -"network.protection.vpn.settings.faq" = "FAQs and Support"; +"network.protection.vpn.settings.faq" = "Često postavljana pitanja i podrška"; /* Title for the feedback row in the VPN status screen. */ "network.protection.vpn.settings.share-feedback" = "Podijeli povratne informacije o VPN-u"; @@ -1940,6 +2033,66 @@ /* Deactivate button */ "pm.deactivate" = "Obustavi"; +/* Label for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.label" = "Adresa e-pošte (neobavezno)"; + +/* Placeholder for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.placeholder" = "ime@email.com"; + +/* Placeholder for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.placeholder" = "Pruži nam svoje povratne informacije…"; + +/* Title for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.title" = "Općenite povratne informacije"; + +/* Title for the ITR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-itr-problem.title" = "Identity Theft Restoration"; + +/* Title for the PIR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-pir-problem.title" = "Personal Information Removal"; + +/* Title for the Subscriptions and Payments category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-ppro-problem.title" = "Pretplate i plaćanja"; + +/* Placeholder for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.placeholder" = "Reci nam što se događa..."; + +/* Title for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.title" = "Prijavi problem"; + +/* Title for the VPN category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-vpn-problem.title" = "VPN"; + +/* Placeholder for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.placeholder" = "Koju bi značajku želio vidjeti?"; + +/* Title for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.title" = "Zahtjev za značajkom"; + +/* Title for the category selection section in the Privacy Pro feedback form */ +"ppro.feedback-form.select-category.title" = "ODABERI KATEGORIJU"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-1" = "Pronašao si problem koji nije pokriven u našem [centru za pomoć](duck://)? Definitivno želimo saznati više o tome.Navedi adresu e-pošte ako želiš da te kontaktiramo u vezi s ovim problemom (možda nećemo moći odgovoriti na sve probleme):"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-2" = "Osim gore unesenih pojedinosti, šaljemo neke anonimizirane podatke s tvojim povratnim informacijama:"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-3" = "• Jesu li neke značajke preglednika aktivne"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-4" = "• Zajednička dijagnostika aplikacija (npr. kôdovi pogrešaka)"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-5" = "Dodirom na \"Pošalji\" slažeš se da DuckDuckGo može koristiti poslane informacije za poboljšanje aplikacije."; + +/* Title for the text box in the Privacy Pro feedback form */ +"ppro.feedback-form.text-box.title" = "Povratne informacije"; + +/* Title for the Privacy Pro feedback form */ +"ppro.feedback-form.title" = "Pošalji povratnu informaciju"; + /* Button title for sync bookmarks limits exceeded warning to go to manage bookmarks */ "prefrences.sync.bookmarks-limit-exceeded-action" = "Upravljanje knjižnim oznakama"; @@ -2029,6 +2182,18 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Omiljeno"; +/* Name of the option the user can chose to give general browser feedback */ +"send.browser.feedback.general-feedback" = "Općenite povratne informacije"; + +/* Name of the option the user can chose to give browser feedback about a problem they enountered */ +"send.browser.feedback.report-problem" = "Prijavi problem"; + +/* Name of the option the user can chose to give browser feedback about a feature they would like */ +"send.browser.feedback.request-feature" = "Zatraži opciju"; + +/* Title of the picker where the user can chose the category of the feedback they want ot send. */ +"send.browser.feedback.select-category" = "ODABERI KATEGORIJU"; + /* Settings cell for About DDG */ "settings.about.ddg" = "O DuckDuckGou"; @@ -2084,6 +2249,9 @@ /* Section footer Autolock description */ "settings.autolock.description" = "Ako su omogućeni Touch ID, Face ID ili pristupna šifra sustava, od tebe će se tražiti da otključaš aplikaciju prilikom otvaranja."; +/* Settings cell for Browser Feedback */ +"settings.browser.feedback" = "Povratne informacije preglednika"; + /* Settings screen cell text for Automatically Clearing Data */ "settings.clear.data" = "Automatsko brisanje podataka"; @@ -2182,7 +2350,7 @@ "settings.ppro" = "Privacy Pro"; /* Title for Link in the Footer of Privacy Pro section */ -"settings.ppro.footer" = "Privacy Policy and Terms of Service"; +"settings.ppro.footer" = "Pravila privatnosti i Uvjeti korištenja usluge"; /* Settings screen cell for long press previews */ "settings.previews" = "Pretpregledi na dugi pritisak"; @@ -2208,23 +2376,20 @@ /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Postavi položaj adresne trake"; -/* Subscription activation pending description */ -"settings.subscription.activation.pending.description" = "This is taking longer than usual, please check back later."; +/* Privacy pro description subtitle in settings when the is activating */ +"settings.subscription.activating" = "Aktiviranje u tijeku..."; -/* Subscription activation pending title */ -"settings.subscription.activation.pending.title" = "Tvoja se pretplata aktivira"; +/* Subscription activation pending description */ +"settings.subscription.activation.pending.description" = "Ovo traje dulje nego inače, pokušaj kasnije."; /* Data Broker protection cell subtitle for privacy pro */ -"settings.subscription.DBP.subtitle" = "Remove your info from sites that sell it"; +"settings.subscription.DBP.subtitle" = "Ukloni svoje podatke s web-mjesta koje ih prodaju"; /* Data Broker protection cell title for privacy pro */ "settings.subscription.DBP.title" = "Personal Information Removal"; -/* Privacy pro description subtext */ -"settings.subscription.description" = "Više besprijekorne privatnosti uz tri nove zaštite:"; - /* I have a Subscription button text for privacy pro */ -"settings.subscription.existing.subscription" = "I Have a Subscription"; +"settings.subscription.existing.subscription" = "Imam pretplatu"; /* Subscription expired description */ "settings.subscription.expired.comment" = "Pretplati se ponovno i nastavi koristiti Privacy Pro"; @@ -2232,23 +2397,26 @@ /* Subscription expired tittle message */ "settings.subscription.expired.title" = "Tvoja pretplata na Privacy Pro je istekla"; -/* Privacy pro features list */ -"settings.subscription.features" = " • VPN\n • Personal Information Removal\n • Identity Theft Restoration"; - /* Identity theft restoration cell subtitle for privacy pro */ -"settings.subscription.ITR.subtitle" = "If your identity is stolen, we'll help restore it"; +"settings.subscription.ITR.subtitle" = "Ako ti je identitet ukraden, pomoći ćemo ti da ga vratiš"; /* Identity theft restoration cell title for privacy pro */ "settings.subscription.ITR.title" = "Identity Theft Restoration"; /* Get Privacy Pro button text for privacy pro */ -"settings.subscription.learn.more" = "Get Privacy Pro"; +"settings.subscription.learn.more" = "Nabavi Privacy Pro"; /* Subscription Settings button text for privacy pro */ -"settings.subscription.manage" = "Subscription Settings"; +"settings.subscription.manage" = "Postavke pretplate"; + +/* Privacy Pro description subtitle in settings */ +"settings.subscription.row.description" = "Uključuje naš VPN i Identity Theft Restoration (oporavak od krađe identiteta)."; -/* Call to action title for Privacy Pro */ -"settings.subscription.subscribe" = "Subscribe to Privacy Pro"; +/* Call to action title for Privacy Pro settings */ +"settings.subscription.subscribe" = "Zaštiti svoju vezu i identitet uz Privacy Pro"; + +/* Privacy pro description subtitle in settings */ +"settings.subscription.us.description" = "Uključuje naš VPN, Personal Information Removal i Identity Theft Restoration."; /* VPN cell title for privacy pro */ "settings.subscription.VPN.title" = "VPN"; @@ -2317,67 +2485,64 @@ "siteFeedback.urlPlaceholder" = "Koje je web-mjesto neispravno?"; /* Subscription Activation Info */ -"subscription.activate..header.description" = "Access your Privacy Pro subscription on this device via Apple ID or an email address."; +"subscription.activate..header.description" = "Pristupi svojoj pretplati na Privacy Pro na ovom uređaju putem Apple ID-a ili adrese e-pošte."; /* Button for adding email address to subscription */ -"subscription.activate.add.email.button" = "Add Email"; +"subscription.activate.add.email.button" = "Dodaj e-poštu"; /* Apple ID option for activation */ "subscription.activate.appleid" = "Apple ID"; /* Button text for restoring purchase via Apple ID */ -"subscription.activate.appleid.button" = "Restore Purchase"; +"subscription.activate.appleid.button" = "Vrati kupnju"; /* Description for Apple ID activation */ -"subscription.activate.appleid.description" = "Restore your purchase to activate your subscription on this device."; +"subscription.activate.appleid.description" = "Vrati svoju kupnju kako bi aktivirao pretplatu na ovom uređaju."; /* Subscription Activation Info */ -"subscription.activate.description" = "Your subscription is automatically available in DuckDuckGo on any device signed in to your Apple ID."; +"subscription.activate.description" = "Tvoja pretplata automatski je dostupna u DuckDuckGo na bilo kojem uređaju prijavljenom na tvoj Apple ID."; /* Button for editing email address added to subscription */ -"subscription.activate.edit.email.button" = "Edit Email"; +"subscription.activate.edit.email.button" = "Uredi e-poštu"; /* View Title for editing your email account */ -"subscription.activate.edit.email.title" = "Edit Email"; +"subscription.activate.edit.email.title" = "Uredi e-poštu"; /* Email option for activation */ -"subscription.activate.email" = "Email"; +"subscription.activate.email" = "E-pošta"; /* Restore button title for Email */ -"subscription.activate.email.button" = "Enter Email"; +"subscription.activate.email.button" = "Unesi adresu e-pošte"; /* Description for Email activation */ -"subscription.activate.email.description" = "Use your email to activate your subscription on this device."; +"subscription.activate.email.description" = "Koristi svoju e-poštu za aktivaciju pretplate na ovom uređaju."; /* Activate subscription title */ "subscription.activate.email.title" = "Aktiviraj pretplatu"; /* Button title for cancelling email deletion */ -"subscription.activate.manage.email.cancel" = "Cancel"; +"subscription.activate.manage.email.cancel" = "Otkaži"; /* Button title for confirming email deletion */ -"subscription.activate.manage.email.OK" = "OK"; +"subscription.activate.manage.email.OK" = "U redu"; /* Restore button title for AppleID */ -"subscription.activate.restore.apple" = "Restore Purchase"; +"subscription.activate.restore.apple" = "Vrati kupnju"; /* Subscription Activation Title */ -"subscription.activate.title" = "Activate your subscription on this device"; +"subscription.activate.title" = "Aktiviraj svoju pretplatu na ovom uređaju"; /* Resend activation instructions button */ -"subscription.add.device.resend.instructions" = "Resend Instructions"; +"subscription.add.device.resend.instructions" = "Ponovno pošalji upute"; /* Add email to an existing subscription */ -"subscription.add.email" = "Add an email address to activate your subscription on your other devices. We’ll only use this address to verify your subscription."; +"subscription.add.email" = "Dodaj adresu e-pošte kako bi aktivirao pretplatu na svojim drugim uređajima. Ovu adresu ćemo koristiti samo za potvrdu tvoje pretplate."; /* View title for adding email to subscription */ -"subscription.add.email.title" = "Add Email"; - -/* Title for Alert messages */ -"subscription.alert.title" = "subscription.alert.title"; +"subscription.add.email.title" = "Dodaj e-poštu"; /* Subscription availability message on Apple devices */ -"subscription.available.apple" = "Privacy Pro is available on any device signed in to the same Apple ID."; +"subscription.available.apple" = "Privacy Pro je dostupan na bilo kojem uređaju koji je prijavljen na isti Apple ID."; /* Text for the manage billing page */ "subscription.billing.google.text" = "Tvoja pretplata kupljena je putem trgovine Google Play. Za obnavljanje pretplate, otvori postavke pretplate na Google Play Storeu na uređaju koji je prijavljen na isti Google račun koji je upotrijebljen za prvobitnu kupnju pretplate."; @@ -2385,89 +2550,86 @@ /* Title for the manage billing page */ "subscription.billing.google.title" = "Pretplatnički paketi"; -/* Subscription annual billing period type */ -"subscription.billing.period.annual" = "annual"; - -/* Subscription monthly billing period type */ -"subscription.billing.period.monthly" = "monthly"; - /* Subscription Removal confirmation message */ -"subscription.cancel.message" = "Your subscription has been removed from this device."; +"subscription.cancel.message" = "Tvoja je pretplata uklonjena s ovog uređaja."; /* Change plan or cancel title */ -"subscription.change.plan" = "Update Plan or Cancel"; +"subscription.change.plan" = "Ažuriraj plan ili odustani"; /* Navigation Button for closing subscription view */ -"subscription.close" = "Close"; +"subscription.close" = "Zatvori"; /* Title for Confirm messages */ -"subscription.confirm.title" = "Are you sure?"; +"subscription.confirm.title" = "Jesi li siguran?"; /* Header for section for activating subscription on other devices */ -"subscription.devices.header" = "Activate on Other Devices"; +"subscription.devices.header" = "Aktiviraj na drugim uređajima"; /* Footer for section for activating subscription on other devices when email was not yet added */ -"subscription.devices.no.email.footer" = "Add an optional email to your subscription or use your Apple ID to access Privacy Pro on other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.no.email.footer" = "Dodaj neobaveznu adresu e-pošte svojoj pretplati kako bi pristupio Privacy Prou na drugim uređajima. **[Saznaj više](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Footer for section for activating subscription on other devices when email is added */ -"subscription.devices.with.email.footer" = "Use this email to activate your subscription in Settings > Privacy Pro in the DuckDuckGo app on your other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.with.email.footer" = "Koristi ovu adresu e-pošte za aktiviranje pretplate u Postavke > Privacy Pro u aplikaciji DuckDuckGo na drugim uređajima. **[Saznaj više](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Alert content for not found subscription */ -"subscription.email.inactive.alert.message" = "The subscription associated with this email is no longer active."; +"subscription.email.inactive.alert.message" = "Tvoja pretplata povezana s ovom e-poštom više nije aktivna."; /* Alert title for not found subscription */ -"subscription.email.inactive.alert.title" = "Subscription Not Found"; +"subscription.email.inactive.alert.title" = "Pretplata nije pronađena"; /* Alert content for not found subscription */ -"subscription.expired.alert.message" = "The subscription associated with this Apple ID is no longer active."; +"subscription.expired.alert.message" = "Tvoja pretplata povezana s ovim Apple ID-om više nije aktivna."; /* FAQ Button */ -"subscription.faq" = "FAQs and Support"; +"subscription.faq" = "Često postavljana pitanja i podrška"; /* FAQ Description */ -"subscription.faq.description" = "Get answers to frequently asked questions or contact Privacy Pro support from our help pages."; +"subscription.faq.description" = "Pronađi odgovore na često postavljana pitanja ili kontaktiraj podršku Privacy Pro putem naših stranica pomoći."; + +/* Send Feedback Button */ +"subscription.feedback" = "Pošalji povratnu informaciju"; /* Cancel action for the existing subscription dialog */ -"subscription.found.cancel" = "Cancel"; +"subscription.found.cancel" = "Otkaži"; /* Restore action for the existing subscription dialog */ -"subscription.found.restore" = "Restore"; +"subscription.found.restore" = "Vrati"; /* Message for the existing subscription dialog */ -"subscription.found.text" = "We found a subscription associated with this Apple ID."; +"subscription.found.text" = "Pronašli smo pretplatu povezanu s ovim Apple ID-om."; /* Title for the existing subscription dialog */ -"subscription.found.title" = "Subscription Found"; +"subscription.found.title" = "Pretplata je pronađena"; /* Help and support Section header */ -"subscription.help" = "Help and support"; +"subscription.help" = "Pomoć i podrška"; /* Manage Plan header */ -"subscription.manage.plan" = "Manage Plan"; +"subscription.manage.plan" = "Upravljanje planom"; /* Header for the subscription section */ -"subscription.manage.title" = "Subscription"; +"subscription.manage.title" = "Pretplata"; /* Alert content for not found subscription */ -"subscription.notFound.alert.message" = "There is no subscription associated with this Apple ID."; +"subscription.notFound.alert.message" = "Nema pretplate povezane s ovim Apple ID-om."; /* Alert title for not found subscription */ -"subscription.notFound.alert.title" = "Subscription Not Found"; +"subscription.notFound.alert.title" = "Pretplata nije pronađena"; /* View plans button text */ -"subscription.notFound.view.plans" = "View Plans"; +"subscription.notFound.view.plans" = "Pregledaj planove"; /* Hero Text for Personal information removal */ -"subscription.pir.hero" = "Activate Privacy Pro on desktop to set up Personal Information Removal"; +"subscription.pir.hero" = "Aktiviraj Privacy Pro na radnoj površini kako bi postavio Personal Information Removal"; /* Description on how to use Personal information removal in desktop. The first placeholder references a location in the Desktop application. Privacy Pro>, and the second, the menu entry. i.e. */ -"subscription.pir.heroText" = "In the DuckDuckGo browser for desktop, go to %1$@ and click %2$@ to get started."; +"subscription.pir.heroText" = "Prvo, o pregledniku DuckDuckGo za stolna računala idi na %1$@ i klikni %2$@."; /* Settings references a menu in the Desktop app, Privacy Pro, references our product name */ -"subscription.pir.heroTextLocation" = "Settings > Privacy Pro"; +"subscription.pir.heroTextLocation" = "Postavke > Privacy Pro"; /* Menu item for enabling Personal Information Removal on Desktop */ -"subscription.pir.heroTextMenyEntry" = "I have a subscription"; +"subscription.pir.heroTextMenyEntry" = "Imam pretplatu"; /* Text for the 'macOS' button */ "subscription.pir.macos" = "Mac"; @@ -2476,28 +2638,28 @@ "subscription.pir.windows" = "Windows"; /* Progress view title when completing the purchase */ -"subscription.progress.view.completing.purchase" = "Completing purchase..."; +"subscription.progress.view.completing.purchase" = "Dovršavanje kupnje..."; /* Progress view title when starting the purchase */ -"subscription.progress.view.purchasing.subscription" = "Purchase in progress..."; +"subscription.progress.view.purchasing.subscription" = "Kupnja u tijeku..."; /* Progress view title when restoring past subscription purchase */ -"subscription.progress.view.restoring.subscription" = "Restoring subscription..."; +"subscription.progress.view.restoring.subscription" = "Obnavljanje pretplate..."; /* Remove from this device button */ -"subscription.remove.from.device.button" = "Remove From This Device"; +"subscription.remove.from.device.button" = "Ukloni s ovog uređaja"; /* Remove from device confirmation dialog text */ -"subscription.remove.from.device.text" = "You will no longer be able to access your Privacy Pro subscription on this device. This will not cancel your subscription, and it will remain active on your other devices."; +"subscription.remove.from.device.text" = "Više nećeš moći pristupiti svojoj pretplati na Privacy Pro na ovom uređaju. Ovo neće otkazati tvoju pretplatu i ostat će aktivna na tvojim drugim uređajima."; /* Remove from device confirmation dialog title */ -"subscription.remove.from.device.title" = "Remove from this device?"; +"subscription.remove.from.device.title" = "Ukloni s ovog uređaja?"; /* Remove subscription button text */ -"subscription.remove.subscription" = "Remove Subscription"; +"subscription.remove.subscription" = "Ukloni pretplatu"; /* Remove subscription cancel button text */ -"subscription.remove.subscription.cancel" = "Cancel"; +"subscription.remove.subscription.cancel" = "Otkaži"; /* Button text for general error message */ "subscription.restore.backend.error.button" = "Povratak na postavke"; @@ -2509,28 +2671,43 @@ "subscription.restore.backend.error.title" = "Nešto nije bilo kako treba"; /* Alert for general error message */ -"subscription.restore.general.error.message" = "The App Store was unable to process your purchase. Please try again later."; +"subscription.restore.general.error.message" = "App Store nije mogao obraditi tvoju kupnju. Pokušaj ponovno nešto kasnije."; /* Alert for general error title */ -"subscription.restore.general.error.title" = "Something Went Wrong"; +"subscription.restore.general.error.title" = "Nešto nije bilo kako treba"; /* Alert button text for restored purchase alert */ -"subscription.restore.success.alert.button" = "OK"; +"subscription.restore.success.alert.button" = "U redu"; /* Alert message for restored purchase */ -"subscription.restore.success.alert.message" = "Your purchases have been restored."; +"subscription.restore.success.alert.message" = "Tvoje kupnje su vraćene."; /* Alert title for restored purchase */ -"subscription.restore.success.alert.title" = "You’re all set."; +"subscription.restore.success.alert.title" = "Sve je spremno."; + +/* Subtitle in header when subscribed */ +"subscription.subscribed" = "Pretplaćen"; /* Subscription Expired Data. This reads as 'Your subscription expired on (date)' */ "subscription.subscription.expired.caption" = "Tvoja je pretplata istekla %@"; -/* Subscription expiration info. This reads as 'Your (monthly or annual) subscription expires on (date)' */ -"subscription.subscription.expiring.caption" = "Your %1$@ subscription expires on %2$@."; +/* Monthly subscription expiration info where parameter is expiration date. This reads as 'Your monthly subscription expires on (date)' */ +"subscription.subscription.expiring.monthly.caption" = "Tvoja mjesečna pretplata istječe %@."; + +/* Unknown period subscription expiration info where parameter is expiration date. This reads as 'Your subscription expires on (date)' */ +"subscription.subscription.expiring.unknown.caption" = "Tvoja pretplata istječe %@."; + +/* Annual subscription expiration info where parameter is expiration date. This reads as 'Your annual subscription expires on (date)' */ +"subscription.subscription.expiring.yearly.caption" = "Tvoja godišnja pretplata istječe %@."; + +/* Monthly subscription renewal info where parameter is renewal date. This reads as 'Your monthly subscription renews on (date)' */ +"subscription.subscription.renewing.monthly.caption" = "Tvoja će se mjesečna pretplata obnoviti %@."; + +/* Unknown period subscription renewal info where parameter is renewal date. This reads as 'Your subscription renews on (date)' */ +"subscription.subscription.renewing.unknown.caption" = "Tvoja će se pretplata obnoviti %@."; -/* Subscription renewal info. This reads as 'Your (monthly or annual) subscription renews on (date)' */ -"subscription.subscription.renewing.caption" = "Your %1$@ subscription renews on %2$@."; +/* Annual subscription renewal info where parameter is renewal date. This reads as 'Your annual subscription renews on (date)' */ +"subscription.subscription.renewing.yearly.caption" = "Tvoja će se godišnja pretplata obnoviti %@."; /* Navigation bar Title for subscriptions */ "subscription.title" = "Privacy Pro"; @@ -2677,118 +2854,118 @@ "voiceSearch.footer.note.old" = "Zvuk se obrađuje na uređaju. Ne pohranjuje se i ne dijeli ni s kim, uključujući DuckDuckGo."; /* Cancel action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.cancel" = "Dismiss"; +"vpn.access-revoked.alert.action.cancel" = "Odbaci"; /* Primary action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.subscribe" = "Subscribe"; +"vpn.access-revoked.alert.action.subscribe" = "Pretplati se"; /* Alert message for the alert when the Privacy Pro subscription expiress */ -"vpn.access-revoked.alert.message" = "Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"vpn.access-revoked.alert.message" = "Pretplati se na Privacy Pro kako bi ponovno povezao DuckDuckGo VPN."; /* Alert title for the alert when the Privacy Pro subscription expires */ -"vpn.access-revoked.alert.title" = "VPN disconnected due to expired subscription"; +"vpn.access-revoked.alert.title" = "VPN je isključen zbog isteka pretplate"; /* Title for the VPN widget onboarding screen */ "vpn.addWidget.settings.title" = "Pronađi i odaberi DuckDuckGo. Zatim prijeđi prstom do VPN-a i odaberi Dodaj widget."; /* Title for the Cancel button of the VPN feedback form */ -"vpn.feedback-form.button.cancel" = "Cancel"; +"vpn.feedback-form.button.cancel" = "Otkaži"; /* Title for the Done button of the VPN feedback form */ -"vpn.feedback-form.button.done" = "Done"; +"vpn.feedback-form.button.done" = "Gotovo"; /* Title for the Submit button of the VPN feedback form */ -"vpn.feedback-form.button.submit" = "Submit"; +"vpn.feedback-form.button.submit" = "Pošalji"; /* Title for the Submitting state of the VPN feedback form */ -"vpn.feedback-form.button.submitting" = "Submitting…"; +"vpn.feedback-form.button.submitting" = "Slanje u tijeku..."; /* Title for the browser crash/freeze category of the VPN feedback form */ -"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN causes browser to crash or freeze"; +"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN uzrokuje rušenje ili zamrzavanje preglednika"; /* Title for the 'VPN fails to connect' category of the VPN feedback form */ -"vpn.feedback-form.category.fails-to-connect" = "VPN fails to connect"; +"vpn.feedback-form.category.fails-to-connect" = "VPN se ne može povezati"; /* Title for the 'VPN feature request' category of the VPN feedback form */ -"vpn.feedback-form.category.feature-request" = "VPN feature request"; +"vpn.feedback-form.category.feature-request" = "Zahtjev za VPN značajkom"; /* Title for the category 'VPN causes issues with other apps or websites' category of the VPN feedback form */ -"vpn.feedback-form.category.issues-with-apps" = "VPN causes issues with other apps or websites"; +"vpn.feedback-form.category.issues-with-apps" = "VPN uzrokuje probleme s drugim aplikacijama ili web-mjestima"; /* Title for the local device connectivity category of the VPN feedback form */ -"vpn.feedback-form.category.local-device-connectivity" = "VPN won't let me connect to local device"; +"vpn.feedback-form.category.local-device-connectivity" = "VPN mi ne dopušta povezivanje s lokalnim uređajem"; /* Title for the 'other VPN feedback' category of the VPN feedback form */ -"vpn.feedback-form.category.other" = "Other VPN feedback"; +"vpn.feedback-form.category.other" = "Druge povratne informacije o VPN-u"; /* Title for the category selection state of the VPN feedback form */ -"vpn.feedback-form.category.select-category" = "Select a category"; +"vpn.feedback-form.category.select-category" = "ODABERI KATEGORIJU"; /* Title for the 'VPN is too slow' category of the VPN feedback form */ -"vpn.feedback-form.category.too-slow" = "VPN connection is too slow"; +"vpn.feedback-form.category.too-slow" = "VPN veza je prespora"; /* Title for the 'unable to install' category of the VPN feedback form */ -"vpn.feedback-form.category.unable-to-install" = "Unable to install VPN"; +"vpn.feedback-form.category.unable-to-install" = "Nije moguće instalirati VPN"; /* Title for the feedback sent view description of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.description" = "Your feedback will help us improve the\nDuckDuckGo VPN."; +"vpn.feedback-form.sending-confirmation.description" = "Tvoje povratne informacije pomoći će nam da poboljšamo\n DuckDuckGo VPN."; /* Title for the feedback sending error text of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.error" = "We couldn't send your feedback right now, please try again."; +"vpn.feedback-form.sending-confirmation.error" = "Nismo mogli poslati tvoje povratne informacije, pokušaj ponovno."; /* Title for the feedback sent view title of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.title" = "Thank you!"; +"vpn.feedback-form.sending-confirmation.title" = "Hvala!"; /* Toast message when the VPN feedback form is submitted successfully */ -"vpn.feedback-form.submitted.message" = "Thank You! Feedback submitted."; +"vpn.feedback-form.submitted.message" = "Hvala! Povratne informacije su poslane."; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-1" = "Please describe what's happening, what you expected to happen, and the steps that led to the issue:"; +"vpn.feedback-form.text-1" = "Opiši što se događa, što si očekivao da će se dogoditi i korake koji su doveli do problema"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-2" = "In addition to the details entered into this form, your app issue report will contain:"; +"vpn.feedback-form.text-2" = "Osim pojedinosti unesenih u ovaj obrazac, izvješće o problemu s aplikacijom sadržavat će:"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-3" = "• Whether specific DuckDuckGo features are enabled"; +"vpn.feedback-form.text-3" = "• jesu li omogućene određene značajke DuckDuckGoa,"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-4" = "• Aggregate DuckDuckGo app diagnostics"; +"vpn.feedback-form.text-4" = "• zajedničku dijagnostiku aplikacije DuckDuckGo."; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-5" = "By tapping \"Submit\" I agree that DuckDuckGo may use the information in this report for purposes of improving the app's features."; +"vpn.feedback-form.text-5" = "Dodirom na \"Pošalji\" slažem se da DuckDuckGo smije koristiti informacije u ovom izvješću u svrhu poboljšanja značajki aplikacije."; /* Title for each screen of the VPN feedback form */ -"vpn.feedback-form.title" = "Help Improve the DuckDuckGo VPN"; +"vpn.feedback-form.title" = "Pomozi poboljšati DuckDuckGo VPN"; /* VPN settings screen cell text for adding the VPN widget to the home screen */ "vpn.settings.add.widget" = "Dodaj VPN widget na početni zaslon"; /* Disclaimer for the DNS Server section on the DNS Server screen */ -"vpn.settings.dns.section-disclaimer" = "Using a custom DNS server can impact browsing speeds and expose your activity to third parties if the server isn't secure or reliable."; +"vpn.settings.dns.section-disclaimer" = "Korištenje prilagođenog DNS poslužitelja može utjecati na brzinu pregledavanja i izložiti tvoju aktivnost trećim stranama ako poslužitelj nije zaštićen ili pouzdan."; /* Header text for the DNS section on the VPN Settings screen */ "vpn.settings.dns.section-header" = "DNS"; /* Title for the Apply button on the DNS Server setting screen */ -"vpn.settings.dns.server.apply.button.title" = "Apply"; +"vpn.settings.dns.server.apply.button.title" = "Primijeni"; /* Default value for the DNS Server row on the VPN Settings screen */ "vpn.settings.dns.server.default.value" = "DuckDuckGo"; /* Title for the IPv4 Address setting */ -"vpn.settings.dns.server.ipv4.title" = "IPv4 Address"; +"vpn.settings.dns.server.ipv4.title" = "IPv4 adresa"; /* Custom option for the DNS Server setting */ -"vpn.settings.dns.server.option.custom" = "Custom"; +"vpn.settings.dns.server.option.custom" = "Prilagođeno"; /* Recommended option for the DNS Server setting */ -"vpn.settings.dns.server.option.default" = "DuckDuckGo (Recommended)"; +"vpn.settings.dns.server.option.default" = "DuckDuckGo (preporučeno)"; /* Title for the DNS Server setting screen */ -"vpn.settings.dns.server.screen.title" = "DNS Server"; +"vpn.settings.dns.server.screen.title" = "DNS poslužitelj"; /* Title for the DNS Server row on the VPN Settings screen */ -"vpn.settings.dns.server.title" = "DNS Server"; +"vpn.settings.dns.server.title" = "DNS poslužitelj"; /* Title for the button to enable push notifications in system settings */ "waitlist.allow-notifications" = "Dopusti obavijesti"; diff --git a/DuckDuckGo/hr.lproj/OmniBar.strings b/DuckDuckGo/hr.lproj/OmniBar.strings index 82e1805621..26207dbb27 100644 --- a/DuckDuckGo/hr.lproj/OmniBar.strings +++ b/DuckDuckGo/hr.lproj/OmniBar.strings @@ -7,6 +7,9 @@ /* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "Dt8-We-5tV"; */ "Dt8-We-5tV.accessibilityLabel" = "Obriši tekst"; +/* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "gb5-wL-4OW"; */ +"gb5-wL-4OW.accessibilityLabel" = "Obriši tekst"; + /* Class = "UIButton"; accessibilityLabel = "Share"; ObjectID = "hor-Jd-3kw"; */ "hor-Jd-3kw.accessibilityLabel" = "Podijeli"; diff --git a/DuckDuckGo/hr.lproj/Settings.strings b/DuckDuckGo/hr.lproj/Settings.strings index 59fb1f1d7c..26a36ab81e 100644 --- a/DuckDuckGo/hr.lproj/Settings.strings +++ b/DuckDuckGo/hr.lproj/Settings.strings @@ -10,9 +10,6 @@ /* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "5YD-Od-uDU"; */ "5YD-Od-uDU.text" = "Automatsko brisanje podataka"; -/* Class = "UIButton"; normalTitle = "Settings"; ObjectID = "8YG-7Q-Czd"; */ -"8YG-7Q-Czd.normalTitle" = "Postavke"; - /* Class = "UILabel"; text = "App Exit, Inactive for 1 Hour"; ObjectID = "9EC-lx-77p"; */ "9EC-lx-77p.text" = "Izlaz iz aplikacije, neaktivna 1 sat"; @@ -76,9 +73,6 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Nezaštićena web-mjesta"; -/* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ -"ssa-zd-L3T.title" = "Veličina teksta"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Prikaži tipkovnicu"; diff --git a/DuckDuckGo/hu.lproj/DaxOnboarding.strings b/DuckDuckGo/hu.lproj/DaxOnboarding.strings index d897768a70..5af44e9cdb 100644 --- a/DuckDuckGo/hu.lproj/DaxOnboarding.strings +++ b/DuckDuckGo/hu.lproj/DaxOnboarding.strings @@ -1,12 +1,6 @@ /* Class = "UIButton"; normalTitle = "Hide"; ObjectID = "hgy-75-OvJ"; */ "hgy-75-OvJ.normalTitle" = "Elrejtés"; -/* Class = "UILabel"; text = "Welcome to\nDuckDuckGo!"; ObjectID = "Lvy-9G-3gr"; */ -"Lvy-9G-3gr.text" = "Üdvözlünk a\nDuckDuckGo-ban!"; - -/* Class = "UIButton"; normalTitle = "Let’s Do It!"; ObjectID = "oAv-Zt-uIt"; */ -"oAv-Zt-uIt.normalTitle" = "Rajta, csináljuk!"; - /* Class = "UIButton"; normalTitle = "Button"; ObjectID = "zJt-F9-L48"; */ "zJt-F9-L48.normalTitle" = "Gomb"; diff --git a/DuckDuckGo/hu.lproj/Localizable.strings b/DuckDuckGo/hu.lproj/Localizable.strings index ef68e71d66..fa1d7a213b 100644 --- a/DuckDuckGo/hu.lproj/Localizable.strings +++ b/DuckDuckGo/hu.lproj/Localizable.strings @@ -341,7 +341,7 @@ "autofill.keep-enabled.alert.disable" = "Letilt"; /* A link that takes the user to the DuckDuckGo help pages explaining password managers */ -"autofill.learn.more.link.title" = "Learn More"; +"autofill.learn.more.link.title" = "További részletek"; /* Button displayed after saving/updating an autofill login that takes the user to the saved login */ "autofill.login-save-action-button.toast" = "Megtekintés"; @@ -977,9 +977,6 @@ /* ad = advertisment */ "dax.onboarding.home.subsequent" = "Megvan, ez az!\n\nNe feledd: minden alkalommal, amikor velem végzed a böngészést, egy hátborzongató hirdetés elveszíti az erejét. 👍"; -/* No comment provided by engineer. */ -"dax.onboarding.message" = "Az internet meglehetősen undok hely lehet.\n\nNe aggódj! A bizalmas keresés és böngészés egyszerűbb, mint hinnéd."; - /* No comment provided by engineer. */ "Debug" = "Hibakeresés"; @@ -1080,7 +1077,7 @@ "duckPlayer.never.label" = "Soha"; /* Body text for the modal feature explanation */ -"duckplayer.presentation.modal.body" = "A Duck Player segítségével a DuckDuckGóban célzott hirdetések nélkül, moziszerű élményben nézheted a YouTube-ot, és a megtekintett tartalmak nem befolyásolják a neked szóló ajánlásokat."; +"duckplayer.presentation.modal.body" = "A Duck Player segítségével a DuckDuckGóban célzott hirdetések nélkül nézheted a YouTube-ot, és a megtekintett tartalmak nem befolyásolják a neked szóló ajánlásokat."; /* Button that will dismiss the modal */ "duckplayer.presentation.modal.dismiss-button" = "Megvan!"; @@ -1088,17 +1085,23 @@ /* Two line title (separated by \n) for the feature explanation */ "duckplayer.presentation.modal.title" = "Elárasztanak a YouTube-hirdetések? Próbáld ki a Duck Player!"; +/* Two line title (separated by \n) for the feature explanation */ +"duckplayer.presentation.modal.title.youtube" = "Elárasztanak a YouTube-hirdetések? A Duck Player használatakor nem!"; + /* Footer label in the settings screen for Duck Player */ "duckplayer.settings.footer" = "A DuckDuckGóban minden olyan alapvető adatvédelmi funkciót megtalálsz, amellyel a webes böngészés során gondoskodhatsz saját védelmedről."; /* Text explaining what Duck Player is in the settings screen. */ -"duckplayer.settings.info-text" = "A Duck Player személyre szabott hirdetések nélküli, letisztult megtekintési élményt nyújt, és megakadályozza, hogy a megtekintési tevékenységed befolyásolja a neked szóló YouTube-ajánlásokat."; +"duckplayer.settings.info-text" = "A Duck Player segítségével a DuckDuckGóban célzott hirdetések nélkül nézheted a YouTube-ot, és a megtekintett tartalmak nem befolyásolják a neked szóló ajánlásokat."; /* Button that takes the user to learn more about Duck Player. */ "duckplayer.settings.learn-more" = "További részletek"; +/* Settings screen cell text for DuckPlayer settings to open in new tab */ +"duckplayer.settings.open-new-tab-label" = "Nyisd meg a Duck Playert egy új lapon"; + /* Settings screen cell text for DuckPlayer settings */ -"duckplayer.settings.open-videos-in" = "Videók megnyitása a Duck Playerben"; +"duckplayer.settings.open-videos-in" = "YouTube-videók megnyitása a Duck Playerrel"; /* Settings screen cell text for DuckPlayer settings */ "duckplayer.settings.title" = "Duck Player"; @@ -1169,9 +1172,6 @@ /* Title for prompt to sign up for email protection */ "email.signup-prompt.title" = "E-mail elrejtése és\nnyomkövetők blokkolása"; -/* No comment provided by engineer. */ -"empty!" = "üres!"; - /* Empty list state placholder */ "empty.bookmarks" = "Még nincsenek könyvjelzők hozzáadva"; @@ -1322,6 +1322,39 @@ /* Confirmation button */ "feedback.form.submit" = "Elküldés"; +/* Title of the feedback form */ +"feedback.form.title" = "Segíts a Privacy Pro fejlesztésében"; + +/* Category for Identity Theft Restoration feedback */ +"feedback.general.category.itr" = "Identity Theft Restoration"; + +/* Category for Personal Info Removal feedback */ +"feedback.general.category.pir" = "Személyes információ eltávolítása"; + +/* Category for subscription and payments feedback */ +"feedback.general.category.ppro" = "Előfizetés és befizetések"; + +/* Prompt to select a category for general feedback */ +"feedback.general.category.select" = "VÁLASSZ KATEGÓRIÁT"; + +/* Category for VPN feedback */ +"feedback.general.category.vpn" = "VPN"; + +/* Category for access code issues */ +"feedback.itr.category.access-code" = "Hozzáférési kóddal kapcsolatos probléma"; + +/* Category for issues contacting an advisor */ +"feedback.itr.category.cant-contact-advisor" = "Nem sikerült felvenni a kapcsolatot az ügyintézővel"; + +/* Prompt to select a category for Identity Theft Restoration feedback */ +"feedback.itr.category.select" = "VÁLASSZ KATEGÓRIÁT"; + +/* Category for other Identity Theft Restoration issues */ +"feedback.itr.category.something-else" = "Valami más"; + +/* Category for unhelpful advisor calls */ +"feedback.itr.category.unhelpful" = "Az ügyintézővel folytatott hívás nem volt hasznos"; + /* No comment provided by engineer. */ "feedback.negative.form.genericPlaceholder" = "Kérjük, a lehető legpontosabban írj meg mindent"; @@ -1361,6 +1394,24 @@ /* No comment provided by engineer. */ "feedback.performance.slowLoading" = "A weboldalak vagy a keresési eredmények lassan töltődnek be"; +/* Category for when scan finds incorrect records */ +"feedback.pir.category.not-me" = "A vizsgálat olyan adatokat talált, amelyek nem hozzám tartoznak"; + +/* Category for when scan doesn't find info on a specific site */ +"feedback.pir.category.nothing-on-site" = "A vizsgálat nem találta meg az adataimat egy konkrét weboldalon"; + +/* Category for other Personal Info Removal issues */ +"feedback.pir.category.other" = "Valami más"; + +/* Category for when the removal process is stuck */ +"feedback.pir.category.removal-stuck" = "Az eltávolítási folyamat elakadt"; + +/* Category for when the scan is stuck */ +"feedback.pir.category.scan-stuck" = "Az adatok vizsgálata elakadt"; + +/* Prompt to select a category for Personal Info Removal feedback */ +"feedback.pir.category.select" = "VÁLASSZ KATEGÓRIÁT"; + /* Header above input field */ "feedback.positive.form.header" = "Részletek megosztása"; @@ -1379,6 +1430,15 @@ /* Button encouraging uses to share details aboout their feedback */ "feedback.positive.submit" = "Részletek megosztása"; +/* Category for other Privacy Pro issues */ +"feedback.ppro.category.other" = "Valami más"; + +/* Category for one-time password issues */ +"feedback.ppro.category.otp" = "Egyszeri jelszóval kapcsolatos probléma"; + +/* Prompt to select a category for Privacy Pro feedback */ +"feedback.ppro.category.select" = "VÁLASSZ KATEGÓRIÁT"; + /* No comment provided by engineer. */ "feedback.start.footer" = "Anonim visszajelzésed fontos számunkra."; @@ -1529,12 +1589,6 @@ /* No comment provided by engineer. */ "keyCommandShowAllTabs" = "Összes lap megjelenítése"; -/* Please preserve newline character */ -"launchscreenWelcomeMessage" = "Üdvözlünk a\nDuckDuckGo-ban!"; - -/* No comment provided by engineer. */ -"LOREM IPSUM" = "LOREM IPSUM"; - /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "A DuckDuckGo Mac verziója gyors, böngészési funkciókban gazdag, és kategóriája legjobb adatvédelmi megoldásait kínálja."; @@ -1586,62 +1640,83 @@ /* Edit button */ "navigation.title.edit" = "Szerkesztés"; -/* String indicating NetP is connected when viewed from the settings screen */ -"netP.cell.connected" = "Connected"; - -/* String indicating NetP is disconnected when viewed from the settings screen */ -"netP.cell.disconnected" = "Not connected"; - -/* Title for the DuckDuckGo VPN feature in settings */ -"netP.settings.title" = "VPN"; +/* String indicating NetP is snoozing when viewed from the settings screen */ +"netP.cell.snoozing" = "Szundi"; /* Title for the DuckDuckGo VPN feature */ "netP.title" = "DuckDuckGo VPN"; +/* Button title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.action" = "Minialkalmazás hozzáadása"; + +/* Message for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.message" = "Kapcsold be vagy ki a VPN-t közvetlenül a kezdőképernyőn."; + +/* Title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.title" = "VPN-minialkalmazás hozzáadása"; + /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "A VPN-kapcsolat megszakadt, mert lejárt az előfizetésed. Fizess elő a Privacy Pro szolgáltatásra a DuckDuckGo VPN újracsatlakoztatásához."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "A hálózatvédelmi funkció csatlakozása sikertelen. Próbálkozz újra később."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "A DuckDuckGo VPN nem tudott csatlakozni. Próbálkozz újra később."; /* Title for the VPN FAQ screen. */ "network.protection.faq.title" = "DuckDuckGo VPN – gyakori kérdések"; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "A hálózatvédelmi funkció kapcsolata megszakadt. Újracsatlakozási kísérlet folyamatban…"; +/* Message for tooltip about geoswitching */ +"network.protection.geoswitching.tip.message" = "Megválaszthatod a VPN szerinti tartózkodási helyedet, ha csatlakozol bármely, a világ különböző pontjain lévő szerverünkhöz."; + +/* Title for tooltip about geoswitching */ +"network.protection.geoswitching.tip.title" = "Tartózkodási hely módosítása"; + +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "A DuckDuckGo VPN-kapcsolat megszakadt. Újracsatlakozási kísérlet folyamatban…"; /* Message for the network protection invite dialog */ -"network.protection.invite.dialog.message" = "Enter your invite code to get started."; +"network.protection.invite.dialog.message" = "A kezdéshez írd be a meghívókódodat."; /* Title for the network protection invite screen */ "network.protection.invite.dialog.title" = "Meghívót kaptál a DuckDuckGo VPN kipróbálására"; /* Prompt for the network protection invite code text field */ -"network.protection.invite.field.prompt" = "Invite Code"; +"network.protection.invite.field.prompt" = "Meghívókód"; /* Message for the network protection invite success view */ -"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; +"network.protection.invite.success.message" = "Rejtsd el tartózkodási helyedet a webhelyek elől, az online tevékenységedet pedig az internetszolgáltatók és a hálózatodhoz csatlakozó más személyek elől."; /* Title for the network protection invite success view */ -"network.protection.invite.success.title" = "Success! You’re in."; +"network.protection.invite.success.title" = "Sikeres belépés."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; /* Title text for an iOS quick action that opens VPN settings */ "network.protection.quick-action.open-vpn" = "VPN megnyitása"; +/* Button title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.action" = "További részletek"; + +/* Message for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.message" = "Olyan weboldalakat vagy alkalmazásokat is használhatsz, amelyek blokkolják a VPN-forgalmat, ha ideiglenesen szünetelteted a VPN-kapcsolatot."; + +/* Title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.title" = "VPN-konfliktusok elkerülése"; + +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN szüneteltetve ennyi időre: %@"; + /* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ "network.protection.status.connected.format" = "Csatlakoztatva · %@"; /* The label for the NetP VPN when connecting */ -"network.protection.status.connecting" = "Connecting..."; +"network.protection.status.connecting" = "Csatlakozás…"; /* The label for the NetP VPN when disconnected */ -"network.protection.status.disconnected" = "Not connected"; +"network.protection.status.disconnected" = "Nincs csatlakoztatva"; /* The label for the NetP VPN when disconnecting */ -"network.protection.status.disconnecting" = "Disconnecting..."; +"network.protection.status.disconnecting" = "Leválasztás…"; /* Message label text for the status view when VPN is disconnected */ "network.protection.status.header.message.off" = "Csatlakozz az összes eszköz internetes\nforgalmának védelméhez."; @@ -1655,26 +1730,41 @@ /* Header title label text for the status view when VPN is connected */ "network.protection.status.header.title.on" = "A DuckDuckGo VPN be van kapcsolva"; +/* Header title label text for the status view when VPN is snoozing */ +"network.protection.status.header.title.snoozed" = "A DuckDuckGo VPN szüneteltetve van"; + /* The status view 'Share Feedback' button which is shown inline on the status view after the temporary free use footer text */ -"network.protection.status.menu.share.feedback" = "Share Feedback"; +"network.protection.status.menu.share.feedback" = "Visszajelzés megosztása"; + +/* The label for the NetP VPN when paused */ +"network.protection.status.paused" = "Szüneteltetve"; + +/* The label for when NetP VPN is snoozing plus the length of time remaining formatted as '0:00' */ +"network.protection.status.snoozing.format" = "Szundi, %@ maradt hátra"; + +/* Snooze button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.snooze" = "Szundi 20 percig"; + +/* Wake Up button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.wake-up" = "Ébredj fel"; /* Connection details label shown in NetworkProtection's status view. */ -"network.protection.status.view.connection.details" = "Connection Details"; +"network.protection.status.view.connection.details" = "Kapcsolat adatai"; /* Custom DNS label shown in NetworkProtection's status view. */ -"network.protection.status.view.custom.dns" = "DNS Server"; +"network.protection.status.view.custom.dns" = "DNS-kiszolgáló"; /* Generic connection failed error message shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.message" = "Please try again later."; +"network.protection.status.view.error.connection.failed.message" = "Próbálkozz újra később."; /* Generic connection failed error title shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; +"network.protection.status.view.error.connection.failed.title" = "Nem sikerült csatlakozni."; /* IP Address label shown in NetworkProtection's status view. */ -"network.protection.status.view.ip.address" = "IP Address"; +"network.protection.status.view.ip.address" = "IP-cím"; /* Location label shown in NetworkProtection's status view. */ -"network.protection.status.view.location" = "Location"; +"network.protection.status.view.location" = "Hely"; /* Label shown on the title of the settings section in NetworkProtection's status view. */ "network.protection.status.view.settings.section.title" = "Kezelés"; @@ -1682,12 +1772,15 @@ /* Title label text for the status view when netP is disconnected */ "network.protection.status.view.title" = "VPN"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "A hálózatvédelem be van kapcsolva. A tartózkodási helyed és online tevékenységed védelme aktív."; +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "A DuckDuckGo VPN be van kapcsolva. A tartózkodási helyed és az online tevékenységed védelme aktív."; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Az eszköz forgalmának átirányítási helyszíne: %@."; +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "A VPN szüneteltetése véget ért. Az eszköz forgalmának átirányítása ezen keresztül: %@."; + /* Title for the button to link to the iOS app settings and enable notifications app-wide. */ "network.protection.turn.on.notifications.button.title" = "Értesítések bekapcsolása"; @@ -1698,13 +1791,13 @@ "network.protection.vpn.about" = "Rólunk"; /* Section header for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.section.header" = "Notifications"; +"network.protection.vpn.alerts.section.header" = "Értesítések"; /* List section footer for the toggle for VPN alerts. */ "network.protection.vpn.alerts.toggle.section.footer" = "Kapj értesítést, ha a kapcsolat megszakad vagy a VPN állapota megváltozik."; /* Title for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.toggle.title" = "VPN Notifications"; +"network.protection.vpn.alerts.toggle.title" = "VPN-értesítések"; /* Title for the data volume section in the VPN status screen */ "network.protection.vpn.data-volume" = "Adatmennyiség"; @@ -1713,13 +1806,13 @@ "network.protection.vpn.exclude.local.networks.setting.footer" = "Engedd, hogy a helyi forgalom megkerülje a VPN-t, és csatlakozz a helyi hálózaton található eszközökhöz úgy, mint egy nyomtatóhoz."; /* Header text for the Exclude Local Networks setting item. */ -"network.protection.vpn.exclude.local.networks.setting.header" = "General"; +"network.protection.vpn.exclude.local.networks.setting.header" = "Általános"; /* Title for the Exclude Local Networks setting item. */ "network.protection.vpn.exclude.local.networks.setting.title" = "Helyi hálózatok kizárása"; /* Title for the VPN Location screen's All Countries section. */ -"network.protection.vpn.location.all.countries.section.title" = "All Countries"; +"network.protection.vpn.location.all.countries.section.title" = "Minden ország"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.connected" = "Csatlakoztatott hely"; @@ -1734,7 +1827,7 @@ "network.protection.vpn.location.recommended.section.footer" = "Automatikus csatlakozás az általunk talált legközelebbi szerverhez."; /* Title for the VPN Location screen's Recommended section. */ -"network.protection.vpn.location.recommended.section.title" = "Recommended"; +"network.protection.vpn.location.recommended.section.title" = "Ajánlott"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.selected" = "Kiválasztott hely"; @@ -1752,13 +1845,13 @@ "network.protection.vpn.preferred.location.nearest" = "Legközelebbi hely"; /* Title for the Preferred Location VPN Settings item. */ -"network.protection.vpn.preferred.location.title" = "Preferred Location"; +"network.protection.vpn.preferred.location.title" = "Preferált helyszín"; /* Footer text for the DNS server setting item. */ -"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo routes DNS queries through our DNS servers so your internet provider can't see what websites you visit."; +"network.protection.vpn.secure.dns.setting.footer" = "A DuckDuckGo a saját DNS-kiszolgálóin keresztül irányítja át a DNS-lekérdezéseket, így az internetszolgáltatód nem láthatja, hogy milyen webhelyeket látogatsz meg."; /* Title for the FAQ row in the VPN status screen. */ -"network.protection.vpn.settings.faq" = "FAQs and Support"; +"network.protection.vpn.settings.faq" = "GYIK és segítségnyújtás"; /* Title for the feedback row in the VPN status screen. */ "network.protection.vpn.settings.share-feedback" = "VPN-visszajelzés megosztása"; @@ -1940,6 +2033,66 @@ /* Deactivate button */ "pm.deactivate" = "Deaktiválás"; +/* Label for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.label" = "E-mail-cím (opcionális)"; + +/* Placeholder for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.placeholder" = "neved@email.com"; + +/* Placeholder for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.placeholder" = "Küldj nekünk visszajelzést…"; + +/* Title for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.title" = "Általános visszajelzés"; + +/* Title for the ITR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-itr-problem.title" = "Identity Theft Restoration"; + +/* Title for the PIR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-pir-problem.title" = "Personal Information Removal"; + +/* Title for the Subscriptions and Payments category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-ppro-problem.title" = "Előfizetések és fizetések"; + +/* Placeholder for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.placeholder" = "Mondd el, mit tapasztaltál…"; + +/* Title for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.title" = "Probléma bejelentése"; + +/* Title for the VPN category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-vpn-problem.title" = "VPN"; + +/* Placeholder for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.placeholder" = "Milyen funkciót szeretnél látni?"; + +/* Title for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.title" = "Funkciókérés"; + +/* Title for the category selection section in the Privacy Pro feedback form */ +"ppro.feedback-form.select-category.title" = "VÁLASSZ KATEGÓRIÁT"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-1" = "Olyan problémát találtál, amelyet a [súgóközpont](duck://) nem tárgyal? Mindenképpen jelezd felénk!\n\nAdj meg egy e-mail-címet, ha szeretnéd, hogy a problémára vonatkozóan kapcsolatba lépjünk veled (előfordulhat, hogy nem tudunk minden bejelentésre reagálni):"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-2" = "A fent megadott adatokon kívül néhány anonimizált adatot is elküldünk a visszajelzéseddel együtt:"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-3" = "• Aktívak-e bizonyos böngészőfunkciók"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-4" = "• Összesített alkalmazásdiagnosztika (pl. hibakódok)"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-5" = "Az „Elküldés” gombra koppintva elfogadod, hogy a DuckDuckGo felhasználhatja a megadott információkat az alkalmazás fejlesztése céljából."; + +/* Title for the text box in the Privacy Pro feedback form */ +"ppro.feedback-form.text-box.title" = "Visszajelzés"; + +/* Title for the Privacy Pro feedback form */ +"ppro.feedback-form.title" = "Visszajelzés küldése"; + /* Button title for sync bookmarks limits exceeded warning to go to manage bookmarks */ "prefrences.sync.bookmarks-limit-exceeded-action" = "Könyvjelzők kezelése"; @@ -2029,6 +2182,18 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Kedvencek"; +/* Name of the option the user can chose to give general browser feedback */ +"send.browser.feedback.general-feedback" = "Általános visszajelzés"; + +/* Name of the option the user can chose to give browser feedback about a problem they enountered */ +"send.browser.feedback.report-problem" = "Probléma bejelentése"; + +/* Name of the option the user can chose to give browser feedback about a feature they would like */ +"send.browser.feedback.request-feature" = "Funkcióra irányuló kérés"; + +/* Title of the picker where the user can chose the category of the feedback they want ot send. */ +"send.browser.feedback.select-category" = "VÁLASSZ KATEGÓRIÁT"; + /* Settings cell for About DDG */ "settings.about.ddg" = "A DuckDuckGóról"; @@ -2084,6 +2249,9 @@ /* Section footer Autolock description */ "settings.autolock.description" = "Ha az ujjlenyomat- vagy az arcfelismerés, illetve rendszerjelszó engedélyezve van, az alkalmazást annak megnyitásakor fel kell oldanod."; +/* Settings cell for Browser Feedback */ +"settings.browser.feedback" = "Böngésző-visszajelzés"; + /* Settings screen cell text for Automatically Clearing Data */ "settings.clear.data" = "Adatok automatikus törlése"; @@ -2182,7 +2350,7 @@ "settings.ppro" = "Privacy Pro"; /* Title for Link in the Footer of Privacy Pro section */ -"settings.ppro.footer" = "Privacy Policy and Terms of Service"; +"settings.ppro.footer" = "Adatvédelmi szabályzat és szolgáltatási feltételek"; /* Settings screen cell for long press previews */ "settings.previews" = "Hosszú lenyomásos előnézetek"; @@ -2208,23 +2376,20 @@ /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Állítsd be a címsor elhelyezkedését"; -/* Subscription activation pending description */ -"settings.subscription.activation.pending.description" = "This is taking longer than usual, please check back later."; +/* Privacy pro description subtitle in settings when the is activating */ +"settings.subscription.activating" = "Aktiválás"; -/* Subscription activation pending title */ -"settings.subscription.activation.pending.title" = "Az előfizetésed aktiválása folyamatban van"; +/* Subscription activation pending description */ +"settings.subscription.activation.pending.description" = "Ez a szokásosnál tovább tart, nézz vissza később."; /* Data Broker protection cell subtitle for privacy pro */ -"settings.subscription.DBP.subtitle" = "Remove your info from sites that sell it"; +"settings.subscription.DBP.subtitle" = "Távolítsd el az adataidat az azokat árusító webhelyekről"; /* Data Broker protection cell title for privacy pro */ "settings.subscription.DBP.title" = "Personal Information Removal"; -/* Privacy pro description subtext */ -"settings.subscription.description" = "Zökkenőmentesebb adatvédelem három új védelmi funkcióval:"; - /* I have a Subscription button text for privacy pro */ -"settings.subscription.existing.subscription" = "I Have a Subscription"; +"settings.subscription.existing.subscription" = "Van előfizetésem"; /* Subscription expired description */ "settings.subscription.expired.comment" = "Fizess elő újra, hogy tovább használhasd a Privacy Pro szolgáltatást"; @@ -2232,23 +2397,26 @@ /* Subscription expired tittle message */ "settings.subscription.expired.title" = "Lejárt a Privacy Pro-előfizetésed"; -/* Privacy pro features list */ -"settings.subscription.features" = " • VPN\n • Personal Information Removal\n • Identity Theft Restoration"; - /* Identity theft restoration cell subtitle for privacy pro */ -"settings.subscription.ITR.subtitle" = "If your identity is stolen, we'll help restore it"; +"settings.subscription.ITR.subtitle" = "Ha ellopták a személyazonosságod, segítünk helyreállítani"; /* Identity theft restoration cell title for privacy pro */ "settings.subscription.ITR.title" = "Identity Theft Restoration"; /* Get Privacy Pro button text for privacy pro */ -"settings.subscription.learn.more" = "Get Privacy Pro"; +"settings.subscription.learn.more" = "Privacy Pro előfizetése"; /* Subscription Settings button text for privacy pro */ -"settings.subscription.manage" = "Subscription Settings"; +"settings.subscription.manage" = "Előfizetési beállítások"; + +/* Privacy Pro description subtitle in settings */ +"settings.subscription.row.description" = "Tartalmazza a VPN-megoldásunkat és a személyazonosság helyreállításához használható Identity Theft Restoration funkciókat."; -/* Call to action title for Privacy Pro */ -"settings.subscription.subscribe" = "Subscribe to Privacy Pro"; +/* Call to action title for Privacy Pro settings */ +"settings.subscription.subscribe" = "Kapcsolat és a személyazonosság védelme a Privacy Pro segítségével"; + +/* Privacy pro description subtitle in settings */ +"settings.subscription.us.description" = "Tartalmazza a VPN-megoldásunkat, a személyes adatok eltávolítására szolgáló Personal Information Removal és a személyazonosság helyreállításához használható Identity Theft Restoration funkciókat."; /* VPN cell title for privacy pro */ "settings.subscription.VPN.title" = "VPN"; @@ -2317,67 +2485,64 @@ "siteFeedback.urlPlaceholder" = "Melyik weboldal nem működik?”"; /* Subscription Activation Info */ -"subscription.activate..header.description" = "Access your Privacy Pro subscription on this device via Apple ID or an email address."; +"subscription.activate..header.description" = "Az eszközön Apple-fiókkal vagy e-mail-címmel férhetsz hozzá a Privacy Pro-előfizetésedhez."; /* Button for adding email address to subscription */ -"subscription.activate.add.email.button" = "Add Email"; +"subscription.activate.add.email.button" = "E-mail-cím hozzáadása"; /* Apple ID option for activation */ -"subscription.activate.appleid" = "Apple ID"; +"subscription.activate.appleid" = "Apple-fiók"; /* Button text for restoring purchase via Apple ID */ -"subscription.activate.appleid.button" = "Restore Purchase"; +"subscription.activate.appleid.button" = "Vásárlás visszaállítása"; /* Description for Apple ID activation */ -"subscription.activate.appleid.description" = "Restore your purchase to activate your subscription on this device."; +"subscription.activate.appleid.description" = "Előfizetésedet a vásárlás visszaállításával aktiválhatod ezen az eszközön."; /* Subscription Activation Info */ -"subscription.activate.description" = "Your subscription is automatically available in DuckDuckGo on any device signed in to your Apple ID."; +"subscription.activate.description" = "Az előfizetésed automatikusan elérhető a DuckDuckGo-ban minden olyan eszközön, amelyen be vagy jelentkezve az Apple-fiókodba."; /* Button for editing email address added to subscription */ -"subscription.activate.edit.email.button" = "Edit Email"; +"subscription.activate.edit.email.button" = "E-mail-cím szerkesztése"; /* View Title for editing your email account */ -"subscription.activate.edit.email.title" = "Edit Email"; +"subscription.activate.edit.email.title" = "E-mail-cím szerkesztése"; /* Email option for activation */ -"subscription.activate.email" = "Email"; +"subscription.activate.email" = "E-mail"; /* Restore button title for Email */ -"subscription.activate.email.button" = "Enter Email"; +"subscription.activate.email.button" = "E-mail-cím megadása"; /* Description for Email activation */ -"subscription.activate.email.description" = "Use your email to activate your subscription on this device."; +"subscription.activate.email.description" = "Előfizetésedet az e-mail-címed használatával aktiválhatod ezen az eszközön."; /* Activate subscription title */ "subscription.activate.email.title" = "Előfizetés aktiválása"; /* Button title for cancelling email deletion */ -"subscription.activate.manage.email.cancel" = "Cancel"; +"subscription.activate.manage.email.cancel" = "Mégsem"; /* Button title for confirming email deletion */ "subscription.activate.manage.email.OK" = "OK"; /* Restore button title for AppleID */ -"subscription.activate.restore.apple" = "Restore Purchase"; +"subscription.activate.restore.apple" = "Vásárlás visszaállítása"; /* Subscription Activation Title */ -"subscription.activate.title" = "Activate your subscription on this device"; +"subscription.activate.title" = "Előfizetés aktiválása ezen az eszközön"; /* Resend activation instructions button */ -"subscription.add.device.resend.instructions" = "Resend Instructions"; +"subscription.add.device.resend.instructions" = "Útmutató újraküldése"; /* Add email to an existing subscription */ -"subscription.add.email" = "Add an email address to activate your subscription on your other devices. We’ll only use this address to verify your subscription."; +"subscription.add.email" = "Az előfizetés más eszközeiden való aktiválásához adj meg egy e-mail-címet. Ezt a címet csak az előfizetésed megerősítésére fogjuk használni."; /* View title for adding email to subscription */ -"subscription.add.email.title" = "Add Email"; - -/* Title for Alert messages */ -"subscription.alert.title" = "subscription.alert.title"; +"subscription.add.email.title" = "E-mail-cím hozzáadása"; /* Subscription availability message on Apple devices */ -"subscription.available.apple" = "Privacy Pro is available on any device signed in to the same Apple ID."; +"subscription.available.apple" = "A Privacy Pro az ugyanabba az Apple-fiókba bejelentkezett minden eszközön elérhető."; /* Text for the manage billing page */ "subscription.billing.google.text" = "Előfizetésed a Google Play áruházban lett megvásárolva. Az előfizetés megújításához nyisd meg a Google Play áruház előfizetési beállításait egy olyan eszközön, amely ugyanabba a Google-fiókba van bejelentkezve, amellyel eredetileg megvásároltad az előfizetést."; @@ -2385,89 +2550,86 @@ /* Title for the manage billing page */ "subscription.billing.google.title" = "Előfizetési csomagok"; -/* Subscription annual billing period type */ -"subscription.billing.period.annual" = "annual"; - -/* Subscription monthly billing period type */ -"subscription.billing.period.monthly" = "monthly"; - /* Subscription Removal confirmation message */ -"subscription.cancel.message" = "Your subscription has been removed from this device."; +"subscription.cancel.message" = "Az előfizetésed el lett távolítva erről az eszközről."; /* Change plan or cancel title */ -"subscription.change.plan" = "Update Plan or Cancel"; +"subscription.change.plan" = "Csomag frissítése vagy lemondása"; /* Navigation Button for closing subscription view */ -"subscription.close" = "Close"; +"subscription.close" = "Bezárás"; /* Title for Confirm messages */ -"subscription.confirm.title" = "Are you sure?"; +"subscription.confirm.title" = "Biztos vagy benne?"; /* Header for section for activating subscription on other devices */ -"subscription.devices.header" = "Activate on Other Devices"; +"subscription.devices.header" = "Aktiválás más eszközökön"; /* Footer for section for activating subscription on other devices when email was not yet added */ -"subscription.devices.no.email.footer" = "Add an optional email to your subscription or use your Apple ID to access Privacy Pro on other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.no.email.footer" = "Egy opcionális e-mail-címnek az előfizetéshez való hozzáadásával más eszközökön is hozzáférhetsz a Privacy Pro-előfizetésedhez. **[További információ](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Footer for section for activating subscription on other devices when email is added */ -"subscription.devices.with.email.footer" = "Use this email to activate your subscription in Settings > Privacy Pro in the DuckDuckGo app on your other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.with.email.footer" = "Ezzel az e-mail-címmel aktiválhatod az előfizetésedet a többi eszközödön a DuckDuckGo alkalmazás Beállítások > Privacy Pro menüpontjában. **[További információ](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Alert content for not found subscription */ -"subscription.email.inactive.alert.message" = "The subscription associated with this email is no longer active."; +"subscription.email.inactive.alert.message" = "Az ehhez az e-mail-címhez tartozó előfizetés már nem aktív."; /* Alert title for not found subscription */ -"subscription.email.inactive.alert.title" = "Subscription Not Found"; +"subscription.email.inactive.alert.title" = "Előfizetés nem található"; /* Alert content for not found subscription */ -"subscription.expired.alert.message" = "The subscription associated with this Apple ID is no longer active."; +"subscription.expired.alert.message" = "Az ehhez az Apple-fiókhoz tartozó előfizetés már nem aktív."; /* FAQ Button */ -"subscription.faq" = "FAQs and Support"; +"subscription.faq" = "GYIK és segítségnyújtás"; /* FAQ Description */ -"subscription.faq.description" = "Get answers to frequently asked questions or contact Privacy Pro support from our help pages."; +"subscription.faq.description" = "A súgóoldalainkon választ kaphatsz a gyakran ismételt kérdésekre, vagy felveheted a kapcsolatot a Privacy Pro-támogatást nyújtó ügyfélszolgálattal."; + +/* Send Feedback Button */ +"subscription.feedback" = "Visszajelzés küldése"; /* Cancel action for the existing subscription dialog */ -"subscription.found.cancel" = "Cancel"; +"subscription.found.cancel" = "Mégsem"; /* Restore action for the existing subscription dialog */ -"subscription.found.restore" = "Restore"; +"subscription.found.restore" = "Visszaállítás"; /* Message for the existing subscription dialog */ -"subscription.found.text" = "We found a subscription associated with this Apple ID."; +"subscription.found.text" = "Találtunk egy ehhez az Apple-fiókhoz tartozó előfizetést."; /* Title for the existing subscription dialog */ -"subscription.found.title" = "Subscription Found"; +"subscription.found.title" = "Megtalált előfizetés"; /* Help and support Section header */ -"subscription.help" = "Help and support"; +"subscription.help" = "Segítség és támogatás"; /* Manage Plan header */ -"subscription.manage.plan" = "Manage Plan"; +"subscription.manage.plan" = "Csomag kezelése"; /* Header for the subscription section */ -"subscription.manage.title" = "Subscription"; +"subscription.manage.title" = "Előfizetés"; /* Alert content for not found subscription */ -"subscription.notFound.alert.message" = "There is no subscription associated with this Apple ID."; +"subscription.notFound.alert.message" = "Ehhez az Apple-fiókhoz nem tartozik előfizetés."; /* Alert title for not found subscription */ -"subscription.notFound.alert.title" = "Subscription Not Found"; +"subscription.notFound.alert.title" = "Előfizetés nem található"; /* View plans button text */ -"subscription.notFound.view.plans" = "View Plans"; +"subscription.notFound.view.plans" = "Csomagok megtekintése"; /* Hero Text for Personal information removal */ -"subscription.pir.hero" = "Activate Privacy Pro on desktop to set up Personal Information Removal"; +"subscription.pir.hero" = "Privacy Pro aktiválása asztali számítógépen a Personal Information Removal beállításához"; /* Description on how to use Personal information removal in desktop. The first placeholder references a location in the Desktop application. Privacy Pro>, and the second, the menu entry. i.e. */ -"subscription.pir.heroText" = "In the DuckDuckGo browser for desktop, go to %1$@ and click %2$@ to get started."; +"subscription.pir.heroText" = "A kezdéshez a DuckDuckGo böngésző asztali verziójában lépj a %1$@ menüpontra, és kattints a %2$@ lehetőségre."; /* Settings references a menu in the Desktop app, Privacy Pro, references our product name */ -"subscription.pir.heroTextLocation" = "Settings > Privacy Pro"; +"subscription.pir.heroTextLocation" = "Beállítások > Privacy Pro"; /* Menu item for enabling Personal Information Removal on Desktop */ -"subscription.pir.heroTextMenyEntry" = "I have a subscription"; +"subscription.pir.heroTextMenyEntry" = "Van előfizetésem"; /* Text for the 'macOS' button */ "subscription.pir.macos" = "Mac"; @@ -2476,28 +2638,28 @@ "subscription.pir.windows" = "Windows"; /* Progress view title when completing the purchase */ -"subscription.progress.view.completing.purchase" = "Completing purchase..."; +"subscription.progress.view.completing.purchase" = "A vásárlás befejezése…"; /* Progress view title when starting the purchase */ -"subscription.progress.view.purchasing.subscription" = "Purchase in progress..."; +"subscription.progress.view.purchasing.subscription" = "Vásárlás folyamatban…"; /* Progress view title when restoring past subscription purchase */ -"subscription.progress.view.restoring.subscription" = "Restoring subscription..."; +"subscription.progress.view.restoring.subscription" = "Előfizetés visszaállítása…"; /* Remove from this device button */ -"subscription.remove.from.device.button" = "Remove From This Device"; +"subscription.remove.from.device.button" = "Eltávolítás erről az eszközről"; /* Remove from device confirmation dialog text */ -"subscription.remove.from.device.text" = "You will no longer be able to access your Privacy Pro subscription on this device. This will not cancel your subscription, and it will remain active on your other devices."; +"subscription.remove.from.device.text" = "Ezen az eszközön többé nem férhetsz hozzá a Privacy Pro-előfizetésedhez. Ez nem szünteti meg az előfizetésedet, amely a többi eszközödön aktív marad."; /* Remove from device confirmation dialog title */ -"subscription.remove.from.device.title" = "Remove from this device?"; +"subscription.remove.from.device.title" = "Eltávolítod erről az eszközről?"; /* Remove subscription button text */ -"subscription.remove.subscription" = "Remove Subscription"; +"subscription.remove.subscription" = "Előfizetés eltávolítása"; /* Remove subscription cancel button text */ -"subscription.remove.subscription.cancel" = "Cancel"; +"subscription.remove.subscription.cancel" = "Mégsem"; /* Button text for general error message */ "subscription.restore.backend.error.button" = "Vissza a Beállításokhoz"; @@ -2509,28 +2671,43 @@ "subscription.restore.backend.error.title" = "Hiba történt"; /* Alert for general error message */ -"subscription.restore.general.error.message" = "The App Store was unable to process your purchase. Please try again later."; +"subscription.restore.general.error.message" = "Az App Store nem tudta feldolgozni a vásárlást. Próbálkozz újra később."; /* Alert for general error title */ -"subscription.restore.general.error.title" = "Something Went Wrong"; +"subscription.restore.general.error.title" = "Hiba történt"; /* Alert button text for restored purchase alert */ "subscription.restore.success.alert.button" = "OK"; /* Alert message for restored purchase */ -"subscription.restore.success.alert.message" = "Your purchases have been restored."; +"subscription.restore.success.alert.message" = "A vásárlások vissza lettek állítva."; /* Alert title for restored purchase */ -"subscription.restore.success.alert.title" = "You’re all set."; +"subscription.restore.success.alert.title" = "Minden készen áll."; + +/* Subtitle in header when subscribed */ +"subscription.subscribed" = "Előfizetve"; /* Subscription Expired Data. This reads as 'Your subscription expired on (date)' */ "subscription.subscription.expired.caption" = "Az előfizetésed lejárt ekkor: %@"; -/* Subscription expiration info. This reads as 'Your (monthly or annual) subscription expires on (date)' */ -"subscription.subscription.expiring.caption" = "Your %1$@ subscription expires on %2$@."; +/* Monthly subscription expiration info where parameter is expiration date. This reads as 'Your monthly subscription expires on (date)' */ +"subscription.subscription.expiring.monthly.caption" = "A havi előfizetésed lejár ekkor: %@."; + +/* Unknown period subscription expiration info where parameter is expiration date. This reads as 'Your subscription expires on (date)' */ +"subscription.subscription.expiring.unknown.caption" = "Az előfizetésed lejár ekkor: %@."; + +/* Annual subscription expiration info where parameter is expiration date. This reads as 'Your annual subscription expires on (date)' */ +"subscription.subscription.expiring.yearly.caption" = "Az éves előfizetésed lejár ekkor: %@."; + +/* Monthly subscription renewal info where parameter is renewal date. This reads as 'Your monthly subscription renews on (date)' */ +"subscription.subscription.renewing.monthly.caption" = "A havi előfizetésed megújul ekkor: %@."; + +/* Unknown period subscription renewal info where parameter is renewal date. This reads as 'Your subscription renews on (date)' */ +"subscription.subscription.renewing.unknown.caption" = "Az előfizetésed megújul ekkor: %@."; -/* Subscription renewal info. This reads as 'Your (monthly or annual) subscription renews on (date)' */ -"subscription.subscription.renewing.caption" = "Your %1$@ subscription renews on %2$@."; +/* Annual subscription renewal info where parameter is renewal date. This reads as 'Your annual subscription renews on (date)' */ +"subscription.subscription.renewing.yearly.caption" = "Az éves előfizetésed megújul ekkor: %@."; /* Navigation bar Title for subscriptions */ "subscription.title" = "Privacy Pro"; @@ -2677,118 +2854,118 @@ "voiceSearch.footer.note.old" = "A hang feldolgozása az eszközön történik. Nem tároljuk és nem osztjuk meg senkivel, még a DuckDuckGóval sem."; /* Cancel action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.cancel" = "Dismiss"; +"vpn.access-revoked.alert.action.cancel" = "Elutasítás"; /* Primary action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.subscribe" = "Subscribe"; +"vpn.access-revoked.alert.action.subscribe" = "Feliratkozás"; /* Alert message for the alert when the Privacy Pro subscription expiress */ -"vpn.access-revoked.alert.message" = "Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"vpn.access-revoked.alert.message" = "Fizess elő a Privacy Pro szolgáltatásra a DuckDuckGo VPN újracsatlakoztatásához."; /* Alert title for the alert when the Privacy Pro subscription expires */ -"vpn.access-revoked.alert.title" = "VPN disconnected due to expired subscription"; +"vpn.access-revoked.alert.title" = "A VPN megszakadt, mert lejárt az előfizetésed"; /* Title for the VPN widget onboarding screen */ "vpn.addWidget.settings.title" = "Keresd meg, és jelöld ki a DuckDuckGo elemet. Ezután csúsztasd az ujjad a VPN elemre, majd válaszd a Minialkalmazás hozzáadása lehetőséget."; /* Title for the Cancel button of the VPN feedback form */ -"vpn.feedback-form.button.cancel" = "Cancel"; +"vpn.feedback-form.button.cancel" = "Mégsem"; /* Title for the Done button of the VPN feedback form */ -"vpn.feedback-form.button.done" = "Done"; +"vpn.feedback-form.button.done" = "Kész"; /* Title for the Submit button of the VPN feedback form */ -"vpn.feedback-form.button.submit" = "Submit"; +"vpn.feedback-form.button.submit" = "Elküldés"; /* Title for the Submitting state of the VPN feedback form */ -"vpn.feedback-form.button.submitting" = "Submitting…"; +"vpn.feedback-form.button.submitting" = "Beküldés…"; /* Title for the browser crash/freeze category of the VPN feedback form */ -"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN causes browser to crash or freeze"; +"vpn.feedback-form.category.browser-crash-or-freeze" = "A VPN a böngésző összeomlását vagy lefagyását okozza"; /* Title for the 'VPN fails to connect' category of the VPN feedback form */ -"vpn.feedback-form.category.fails-to-connect" = "VPN fails to connect"; +"vpn.feedback-form.category.fails-to-connect" = "Sikertelen VPN-csatlakozás"; /* Title for the 'VPN feature request' category of the VPN feedback form */ -"vpn.feedback-form.category.feature-request" = "VPN feature request"; +"vpn.feedback-form.category.feature-request" = "VPN-funkcióra vonatkozó kérés"; /* Title for the category 'VPN causes issues with other apps or websites' category of the VPN feedback form */ -"vpn.feedback-form.category.issues-with-apps" = "VPN causes issues with other apps or websites"; +"vpn.feedback-form.category.issues-with-apps" = "A VPN problémákat okoz más alkalmazásokkal vagy weboldalakkal"; /* Title for the local device connectivity category of the VPN feedback form */ -"vpn.feedback-form.category.local-device-connectivity" = "VPN won't let me connect to local device"; +"vpn.feedback-form.category.local-device-connectivity" = "A VPN nem enged helyi eszközhöz csatlakozni"; /* Title for the 'other VPN feedback' category of the VPN feedback form */ -"vpn.feedback-form.category.other" = "Other VPN feedback"; +"vpn.feedback-form.category.other" = "Egyéb VPN-visszajelzés"; /* Title for the category selection state of the VPN feedback form */ -"vpn.feedback-form.category.select-category" = "Select a category"; +"vpn.feedback-form.category.select-category" = "VÁLASSZ KATEGÓRIÁT"; /* Title for the 'VPN is too slow' category of the VPN feedback form */ -"vpn.feedback-form.category.too-slow" = "VPN connection is too slow"; +"vpn.feedback-form.category.too-slow" = "Túl lassú VPN-kapcsolat"; /* Title for the 'unable to install' category of the VPN feedback form */ -"vpn.feedback-form.category.unable-to-install" = "Unable to install VPN"; +"vpn.feedback-form.category.unable-to-install" = "Nem sikerült telepíteni a VPN-t"; /* Title for the feedback sent view description of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.description" = "Your feedback will help us improve the\nDuckDuckGo VPN."; +"vpn.feedback-form.sending-confirmation.description" = "A visszajelzésed segít bennünket a DuckDuckGo VPN fejlesztésében."; /* Title for the feedback sending error text of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.error" = "We couldn't send your feedback right now, please try again."; +"vpn.feedback-form.sending-confirmation.error" = "Jelenleg nem sikerült beküldeni a visszajelzését, próbálkozz újra."; /* Title for the feedback sent view title of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.title" = "Thank you!"; +"vpn.feedback-form.sending-confirmation.title" = "Köszönjük!"; /* Toast message when the VPN feedback form is submitted successfully */ -"vpn.feedback-form.submitted.message" = "Thank You! Feedback submitted."; +"vpn.feedback-form.submitted.message" = "Köszönjük! Visszajelzés elküldve."; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-1" = "Please describe what's happening, what you expected to happen, and the steps that led to the issue:"; +"vpn.feedback-form.text-1" = "Írd le, mi történik, mire számítottál, és milyen lépések vezettek a problémához:"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-2" = "In addition to the details entered into this form, your app issue report will contain:"; +"vpn.feedback-form.text-2" = "Az űrlapon megadott adatokon kívül az alkalmazás problémáiról szóló jelentés a következőket is tartalmazza:"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-3" = "• Whether specific DuckDuckGo features are enabled"; +"vpn.feedback-form.text-3" = "• Engedélyezve vannak-e bizonyos DuckDuckGo-funkciók"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-4" = "• Aggregate DuckDuckGo app diagnostics"; +"vpn.feedback-form.text-4" = "• Összesített DuckDuckGo alkalmazásdiagnosztika"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-5" = "By tapping \"Submit\" I agree that DuckDuckGo may use the information in this report for purposes of improving the app's features."; +"vpn.feedback-form.text-5" = "A „Beküldés” gombra koppintva elfogadom, hogy a DuckDuckGo az alkalmazás funkcióinak javítása céljából felhasználhatja a jelentésben szereplő adatokat."; /* Title for each screen of the VPN feedback form */ -"vpn.feedback-form.title" = "Help Improve the DuckDuckGo VPN"; +"vpn.feedback-form.title" = "Segíts javítani a DuckDuckGo VPN-t"; /* VPN settings screen cell text for adding the VPN widget to the home screen */ "vpn.settings.add.widget" = "VPN-minialkalmazás hozzáadása a kezdőképernyőhöz"; /* Disclaimer for the DNS Server section on the DNS Server screen */ -"vpn.settings.dns.section-disclaimer" = "Using a custom DNS server can impact browsing speeds and expose your activity to third parties if the server isn't secure or reliable."; +"vpn.settings.dns.section-disclaimer" = "Egy egyedi DNS-kiszolgáló használata befolyásolhatja a böngészési sebességet, és harmadik felek számára is láthatóvá teheti a tevékenységedet, ha a kiszolgáló nem biztonságos vagy nem megbízható."; /* Header text for the DNS section on the VPN Settings screen */ "vpn.settings.dns.section-header" = "DNS"; /* Title for the Apply button on the DNS Server setting screen */ -"vpn.settings.dns.server.apply.button.title" = "Apply"; +"vpn.settings.dns.server.apply.button.title" = "Alkalmaz"; /* Default value for the DNS Server row on the VPN Settings screen */ "vpn.settings.dns.server.default.value" = "DuckDuckGo"; /* Title for the IPv4 Address setting */ -"vpn.settings.dns.server.ipv4.title" = "IPv4 Address"; +"vpn.settings.dns.server.ipv4.title" = "IPv4-cím"; /* Custom option for the DNS Server setting */ -"vpn.settings.dns.server.option.custom" = "Custom"; +"vpn.settings.dns.server.option.custom" = "Egyéni"; /* Recommended option for the DNS Server setting */ -"vpn.settings.dns.server.option.default" = "DuckDuckGo (Recommended)"; +"vpn.settings.dns.server.option.default" = "DuckDuckGo (ajánlott)"; /* Title for the DNS Server setting screen */ -"vpn.settings.dns.server.screen.title" = "DNS Server"; +"vpn.settings.dns.server.screen.title" = "DNS-kiszolgáló"; /* Title for the DNS Server row on the VPN Settings screen */ -"vpn.settings.dns.server.title" = "DNS Server"; +"vpn.settings.dns.server.title" = "DNS-kiszolgáló"; /* Title for the button to enable push notifications in system settings */ "waitlist.allow-notifications" = "Értesítések engedélyezése"; diff --git a/DuckDuckGo/hu.lproj/OmniBar.strings b/DuckDuckGo/hu.lproj/OmniBar.strings index c00b446839..c0adb385a1 100644 --- a/DuckDuckGo/hu.lproj/OmniBar.strings +++ b/DuckDuckGo/hu.lproj/OmniBar.strings @@ -7,6 +7,9 @@ /* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "Dt8-We-5tV"; */ "Dt8-We-5tV.accessibilityLabel" = "Szöveg törlése"; +/* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "gb5-wL-4OW"; */ +"gb5-wL-4OW.accessibilityLabel" = "Szöveg törlése"; + /* Class = "UIButton"; accessibilityLabel = "Share"; ObjectID = "hor-Jd-3kw"; */ "hor-Jd-3kw.accessibilityLabel" = "Megosztás"; diff --git a/DuckDuckGo/hu.lproj/Settings.strings b/DuckDuckGo/hu.lproj/Settings.strings index 04065a78e1..5b1d1306de 100644 --- a/DuckDuckGo/hu.lproj/Settings.strings +++ b/DuckDuckGo/hu.lproj/Settings.strings @@ -10,9 +10,6 @@ /* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "5YD-Od-uDU"; */ "5YD-Od-uDU.text" = "Adatok automatikus törlése"; -/* Class = "UIButton"; normalTitle = "Settings"; ObjectID = "8YG-7Q-Czd"; */ -"8YG-7Q-Czd.normalTitle" = "Beállítások"; - /* Class = "UILabel"; text = "App Exit, Inactive for 1 Hour"; ObjectID = "9EC-lx-77p"; */ "9EC-lx-77p.text" = "Kilépés az alkalmazásból 1 óra inaktivitás után"; @@ -76,9 +73,6 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Védelem nélküli weboldalak"; -/* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ -"ssa-zd-L3T.title" = "Szövegméret"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Billentyűzet megjelenítése bekapcsolva"; diff --git a/DuckDuckGo/it.lproj/Bookmarks.strings b/DuckDuckGo/it.lproj/Bookmarks.strings index 85fec710c1..5029897ebb 100644 --- a/DuckDuckGo/it.lproj/Bookmarks.strings +++ b/DuckDuckGo/it.lproj/Bookmarks.strings @@ -8,7 +8,7 @@ "cV4-pj-JNb.text" = "Etichetta"; /* Class = "UILabel"; text = "Delete"; ObjectID = "hEJ-T8-Tdl"; */ -"hEJ-T8-Tdl.text" = "Cancella"; +"hEJ-T8-Tdl.text" = "Elimina"; /* Class = "UILabel"; text = "No matches found"; ObjectID = "hqG-b3-Fq3"; */ "hqG-b3-Fq3.text" = "Nessuna corrispondenza trovata"; diff --git a/DuckDuckGo/it.lproj/DaxOnboarding.strings b/DuckDuckGo/it.lproj/DaxOnboarding.strings index e06ce729a5..5b5b0277cb 100644 --- a/DuckDuckGo/it.lproj/DaxOnboarding.strings +++ b/DuckDuckGo/it.lproj/DaxOnboarding.strings @@ -1,12 +1,6 @@ /* Class = "UIButton"; normalTitle = "Hide"; ObjectID = "hgy-75-OvJ"; */ "hgy-75-OvJ.normalTitle" = "Nascondi"; -/* Class = "UILabel"; text = "Welcome to\nDuckDuckGo!"; ObjectID = "Lvy-9G-3gr"; */ -"Lvy-9G-3gr.text" = "DuckDuckGo\nti dà il benvenuto!"; - -/* Class = "UIButton"; normalTitle = "Let’s Do It!"; ObjectID = "oAv-Zt-uIt"; */ -"oAv-Zt-uIt.normalTitle" = "Continua"; - /* Class = "UIButton"; normalTitle = "Button"; ObjectID = "zJt-F9-L48"; */ "zJt-F9-L48.normalTitle" = "Pulsante"; diff --git a/DuckDuckGo/it.lproj/Localizable.strings b/DuckDuckGo/it.lproj/Localizable.strings index 8c8191f7f5..546e285a6f 100644 --- a/DuckDuckGo/it.lproj/Localizable.strings +++ b/DuckDuckGo/it.lproj/Localizable.strings @@ -50,7 +50,7 @@ "action.title.copy.message" = "URL copiato"; /* Delete action - button shown in alert */ -"action.title.delete" = "Cancella"; +"action.title.delete" = "Elimina"; /* Disable protection action */ "action.title.disable.protection" = "Disattiva la tutela della privacy"; @@ -341,7 +341,7 @@ "autofill.keep-enabled.alert.disable" = "Disabilita"; /* A link that takes the user to the DuckDuckGo help pages explaining password managers */ -"autofill.learn.more.link.title" = "Learn More"; +"autofill.learn.more.link.title" = "Ulteriori informazioni"; /* Button displayed after saving/updating an autofill login that takes the user to the saved login */ "autofill.login-save-action-button.toast" = "Visualizza"; @@ -687,7 +687,7 @@ "bookmark.deleted.toast" = "Segnalibro eliminato"; /* Delete bookmark folder alert delete button */ -"bookmark.deleteFolderAlert.deleteButton" = "Cancella"; +"bookmark.deleteFolderAlert.deleteButton" = "Elimina"; /* Do not translate - stringsdict entry */ "bookmark.deleteFolderAlert.message" = "bookmark.deleteFolderAlert.message"; @@ -977,9 +977,6 @@ /* ad = advertisment */ "dax.onboarding.home.subsequent" = "Ben fatto!\n\nRicorda: quando navighi con me, gli annunci inquietanti non possono seguirti. 👍"; -/* No comment provided by engineer. */ -"dax.onboarding.message" = "Internet può essere un po' inquietante.\n\nNon preoccuparti! Effettuare ricerche e navigare in modo privato è più facile di quanto pensi."; - /* No comment provided by engineer. */ "Debug" = "Debug"; @@ -1080,7 +1077,7 @@ "duckPlayer.never.label" = "Mai"; /* Body text for the modal feature explanation */ -"duckplayer.presentation.modal.body" = "Duck Player ti consente di guardare YouTube senza annunci mirati in un'esperienza analoga a quella cinematografica in DuckDuckGo e ciò che guardi non influenzerà i tuoi consigli."; +"duckplayer.presentation.modal.body" = "Duck Player ti consente di guardare YouTube senza annunci mirati in DuckDuckGo e ciò che guardi non inciderà sui consigli che ricevi."; /* Button that will dismiss the modal */ "duckplayer.presentation.modal.dismiss-button" = "Ho capito!"; @@ -1088,17 +1085,23 @@ /* Two line title (separated by \n) for the feature explanation */ "duckplayer.presentation.modal.title" = "YouTube ti inonda di annunci? Prova Duck Player!"; +/* Two line title (separated by \n) for the feature explanation */ +"duckplayer.presentation.modal.title.youtube" = "YouTube ti inonda di annunci? Non con Duck Player!"; + /* Footer label in the settings screen for Duck Player */ "duckplayer.settings.footer" = "DuckDuckGo fornisce tutti gli elementi essenziali per la privacy di cui hai bisogno per proteggerti mentre navighi sul Web."; /* Text explaining what Duck Player is in the settings screen. */ -"duckplayer.settings.info-text" = "Duck Player offre un'esperienza di visualizzazione pulita, senza annunci personalizzati, e impedisce che l'attività di visualizzazione incida sulle raccomandazioni di YouTube."; +"duckplayer.settings.info-text" = "Duck Player ti consente di guardare YouTube senza annunci mirati in DuckDuckGo e ciò che guardi non inciderà sui consigli che ricevi."; /* Button that takes the user to learn more about Duck Player. */ "duckplayer.settings.learn-more" = "Ulteriori informazioni"; +/* Settings screen cell text for DuckPlayer settings to open in new tab */ +"duckplayer.settings.open-new-tab-label" = "Apri Duck Player in una nuova scheda"; + /* Settings screen cell text for DuckPlayer settings */ -"duckplayer.settings.open-videos-in" = "Apri i video in Duck Player"; +"duckplayer.settings.open-videos-in" = "Apri i video di YouTube in Duck Player"; /* Settings screen cell text for DuckPlayer settings */ "duckplayer.settings.title" = "Duck Player"; @@ -1169,9 +1172,6 @@ /* Title for prompt to sign up for email protection */ "email.signup-prompt.title" = "Nascondi il tuo indirizzo e-mail e \n blocca i sistemi di tracciamento"; -/* No comment provided by engineer. */ -"empty!" = "empty!"; - /* Empty list state placholder */ "empty.bookmarks" = "Non è ancora stato aggiunto nessun segnalibro"; @@ -1322,6 +1322,39 @@ /* Confirmation button */ "feedback.form.submit" = "Invia"; +/* Title of the feedback form */ +"feedback.form.title" = "Aiuta a migliorare Privacy Pro"; + +/* Category for Identity Theft Restoration feedback */ +"feedback.general.category.itr" = "Identity Theft Restoration"; + +/* Category for Personal Info Removal feedback */ +"feedback.general.category.pir" = "Rimozione delle informazioni personali"; + +/* Category for subscription and payments feedback */ +"feedback.general.category.ppro" = "Abbonamento e pagamenti"; + +/* Prompt to select a category for general feedback */ +"feedback.general.category.select" = "SELEZIONA UNA CATEGORIA"; + +/* Category for VPN feedback */ +"feedback.general.category.vpn" = "VPN"; + +/* Category for access code issues */ +"feedback.itr.category.access-code" = "Problema con il codice di accesso"; + +/* Category for issues contacting an advisor */ +"feedback.itr.category.cant-contact-advisor" = "Impossibile contattare il consulente"; + +/* Prompt to select a category for Identity Theft Restoration feedback */ +"feedback.itr.category.select" = "SELEZIONA UNA CATEGORIA"; + +/* Category for other Identity Theft Restoration issues */ +"feedback.itr.category.something-else" = "Qualcos'altro"; + +/* Category for unhelpful advisor calls */ +"feedback.itr.category.unhelpful" = "Chiamata al consulente inutile"; + /* No comment provided by engineer. */ "feedback.negative.form.genericPlaceholder" = "Sii quanto più specifico possibile"; @@ -1361,6 +1394,24 @@ /* No comment provided by engineer. */ "feedback.performance.slowLoading" = "Il caricamento delle pagine web o dei risultati di ricerca è lento"; +/* Category for when scan finds incorrect records */ +"feedback.pir.category.not-me" = "La scansione ha trovato dei record che non sono miei"; + +/* Category for when scan doesn't find info on a specific site */ +"feedback.pir.category.nothing-on-site" = "La scansione non ha trovato le mie informazioni su un sito specifico"; + +/* Category for other Personal Info Removal issues */ +"feedback.pir.category.other" = "Qualcos'altro"; + +/* Category for when the removal process is stuck */ +"feedback.pir.category.removal-stuck" = "Il processo di rimozione è bloccato"; + +/* Category for when the scan is stuck */ +"feedback.pir.category.scan-stuck" = "La scansione dei record è bloccata"; + +/* Prompt to select a category for Personal Info Removal feedback */ +"feedback.pir.category.select" = "SELEZIONA UNA CATEGORIA"; + /* Header above input field */ "feedback.positive.form.header" = "Condividi maggiori dettagli"; @@ -1379,6 +1430,15 @@ /* Button encouraging uses to share details aboout their feedback */ "feedback.positive.submit" = "Condividi maggiori dettagli"; +/* Category for other Privacy Pro issues */ +"feedback.ppro.category.other" = "Qualcos'altro"; + +/* Category for one-time password issues */ +"feedback.ppro.category.otp" = "Problema con la password monouso"; + +/* Prompt to select a category for Privacy Pro feedback */ +"feedback.ppro.category.select" = "SELEZIONA UNA CATEGORIA"; + /* No comment provided by engineer. */ "feedback.start.footer" = "Il tuo feedback anonimo è importante per noi."; @@ -1529,12 +1589,6 @@ /* No comment provided by engineer. */ "keyCommandShowAllTabs" = "Mostra tutte le schede"; -/* Please preserve newline character */ -"launchscreenWelcomeMessage" = "DuckDuckGo\nti dà il benvenuto!"; - -/* No comment provided by engineer. */ -"LOREM IPSUM" = "LOREM IPSUM"; - /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "DuckDuckGo per Mac ti offre la velocità di cui hai bisogno, le funzioni di navigazione che desideri e i migliori strumenti per la privacy."; @@ -1586,62 +1640,83 @@ /* Edit button */ "navigation.title.edit" = "Modifica"; -/* String indicating NetP is connected when viewed from the settings screen */ -"netP.cell.connected" = "Connected"; - -/* String indicating NetP is disconnected when viewed from the settings screen */ -"netP.cell.disconnected" = "Not connected"; - -/* Title for the DuckDuckGo VPN feature in settings */ -"netP.settings.title" = "VPN"; +/* String indicating NetP is snoozing when viewed from the settings screen */ +"netP.cell.snoozing" = "In sospensione"; /* Title for the DuckDuckGo VPN feature */ "netP.title" = "VPN DuckDuckGo"; +/* Button title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.action" = "Aggiungi widget"; + +/* Message for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.message" = "Attiva e disattiva la VPN direttamente dalla schermata iniziale."; + +/* Title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.title" = "Aggiungi widget VPN"; + /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "La VPN si è scollegata perché l'abbonamento è scaduto. Iscriviti a Privacy Pro per riconnettere la VPN di DuckDuckGo."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Connessione di Network Protection non riuscita. Riprova più tardi."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "La VPN di DuckDuckGo non è riuscita a connettersi. Riprova in seguito."; /* Title for the VPN FAQ screen. */ "network.protection.faq.title" = "Domande frequenti su DuckDuckGo VPN"; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "La funzione Network Protection è stata interrotta. Tentativo di riconnessione in corso..."; +/* Message for tooltip about geoswitching */ +"network.protection.geoswitching.tip.message" = "Puoi personalizzare la tua posizione VPN connettendoti a uno qualsiasi dei nostri server in tutto il mondo."; + +/* Title for tooltip about geoswitching */ +"network.protection.geoswitching.tip.title" = "Cambia posizione"; + +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "La VPN di DuckDuckGo è stata interrotta. Tentativo di riconnessione in corso..."; /* Message for the network protection invite dialog */ -"network.protection.invite.dialog.message" = "Enter your invite code to get started."; +"network.protection.invite.dialog.message" = "Inserisci il tuo codice invito per iniziare."; /* Title for the network protection invite screen */ "network.protection.invite.dialog.title" = "Hai ricevuto un invito a provare DuckDuckGo VPN"; /* Prompt for the network protection invite code text field */ -"network.protection.invite.field.prompt" = "Invite Code"; +"network.protection.invite.field.prompt" = "Codice invito"; /* Message for the network protection invite success view */ -"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; +"network.protection.invite.success.message" = "Nascondi la tua posizione dai siti web e la tua attività online ai provider internet e ad altri utenti sulla tua rete."; /* Title for the network protection invite success view */ -"network.protection.invite.success.title" = "Success! You’re in."; +"network.protection.invite.success.title" = "Operazione completata."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; /* Title text for an iOS quick action that opens VPN settings */ "network.protection.quick-action.open-vpn" = "Apri VPN"; +/* Button title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.action" = "Ulteriori informazioni"; + +/* Message for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.message" = "Puoi utilizzare siti o app che bloccano il traffico VPN disattivando temporaneamente la connessione VPN."; + +/* Title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.title" = "Evita i conflitti VPN"; + +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "La VPN è sospesa per %@"; + /* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ "network.protection.status.connected.format" = "Connesso · %@"; /* The label for the NetP VPN when connecting */ -"network.protection.status.connecting" = "Connecting..."; +"network.protection.status.connecting" = "Connessione in corso..."; /* The label for the NetP VPN when disconnected */ -"network.protection.status.disconnected" = "Not connected"; +"network.protection.status.disconnected" = "Non collegata"; /* The label for the NetP VPN when disconnecting */ -"network.protection.status.disconnecting" = "Disconnecting..."; +"network.protection.status.disconnecting" = "Disconnessione in corso..."; /* Message label text for the status view when VPN is disconnected */ "network.protection.status.header.message.off" = "Connetti per proteggere tutto il traffico\nInternet del tuo dispositivo."; @@ -1655,26 +1730,41 @@ /* Header title label text for the status view when VPN is connected */ "network.protection.status.header.title.on" = "DuckDuckGo VPN è attiva"; +/* Header title label text for the status view when VPN is snoozing */ +"network.protection.status.header.title.snoozed" = "DuckDuckGo VPN è sospesa"; + /* The status view 'Share Feedback' button which is shown inline on the status view after the temporary free use footer text */ -"network.protection.status.menu.share.feedback" = "Share Feedback"; +"network.protection.status.menu.share.feedback" = "Condividi feedback"; + +/* The label for the NetP VPN when paused */ +"network.protection.status.paused" = "In pausa"; + +/* The label for when NetP VPN is snoozing plus the length of time remaining formatted as '0:00' */ +"network.protection.status.snoozing.format" = "In sospensione, %@ rimanenti"; + +/* Snooze button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.snooze" = "Posponi di 20 minuti"; + +/* Wake Up button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.wake-up" = "Sveglia"; /* Connection details label shown in NetworkProtection's status view. */ -"network.protection.status.view.connection.details" = "Connection Details"; +"network.protection.status.view.connection.details" = "Dettagli della connessione"; /* Custom DNS label shown in NetworkProtection's status view. */ -"network.protection.status.view.custom.dns" = "DNS Server"; +"network.protection.status.view.custom.dns" = "Server DNS"; /* Generic connection failed error message shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.message" = "Please try again later."; +"network.protection.status.view.error.connection.failed.message" = "Riprova più tardi."; /* Generic connection failed error title shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; +"network.protection.status.view.error.connection.failed.title" = "Connessione non riuscita."; /* IP Address label shown in NetworkProtection's status view. */ -"network.protection.status.view.ip.address" = "IP Address"; +"network.protection.status.view.ip.address" = "Indirizzo IP"; /* Location label shown in NetworkProtection's status view. */ -"network.protection.status.view.location" = "Location"; +"network.protection.status.view.location" = "Posizione"; /* Label shown on the title of the settings section in NetworkProtection's status view. */ "network.protection.status.view.settings.section.title" = "Gestisci"; @@ -1682,12 +1772,15 @@ /* Title label text for the status view when netP is disconnected */ "network.protection.status.view.title" = "VPN"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Network Protection è attiva. La tua posizione e le tue attività online sono protette."; +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "La VPN di DuckDuckGo è attiva. La tua posizione e le tue attività online sono protette."; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Instradamento del traffico del dispositivo tramite %@."; +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "La sospensione della VPN è finita. Instradamento del traffico del dispositivo tramite %@."; + /* Title for the button to link to the iOS app settings and enable notifications app-wide. */ "network.protection.turn.on.notifications.button.title" = "Attiva le notifiche"; @@ -1698,13 +1791,13 @@ "network.protection.vpn.about" = "Informazioni"; /* Section header for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.section.header" = "Notifications"; +"network.protection.vpn.alerts.section.header" = "Notifiche"; /* List section footer for the toggle for VPN alerts. */ "network.protection.vpn.alerts.toggle.section.footer" = "Ricevi una notifica se la tua connessione si interrompe o lo stato della VPN cambia."; /* Title for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.toggle.title" = "VPN Notifications"; +"network.protection.vpn.alerts.toggle.title" = "Notifiche VPN"; /* Title for the data volume section in the VPN status screen */ "network.protection.vpn.data-volume" = "Volume dati"; @@ -1713,13 +1806,13 @@ "network.protection.vpn.exclude.local.networks.setting.footer" = "Consenti al traffico locale di bypassare la VPN e connettersi ai dispositivi sulla tua rete locale, come una stampante."; /* Header text for the Exclude Local Networks setting item. */ -"network.protection.vpn.exclude.local.networks.setting.header" = "General"; +"network.protection.vpn.exclude.local.networks.setting.header" = "Generale"; /* Title for the Exclude Local Networks setting item. */ "network.protection.vpn.exclude.local.networks.setting.title" = "Escludi reti locali"; /* Title for the VPN Location screen's All Countries section. */ -"network.protection.vpn.location.all.countries.section.title" = "All Countries"; +"network.protection.vpn.location.all.countries.section.title" = "Tutti i paesi"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.connected" = "Posizione connessa"; @@ -1734,7 +1827,7 @@ "network.protection.vpn.location.recommended.section.footer" = "Connettiti automaticamente al server più vicino che riusciamo a trovare."; /* Title for the VPN Location screen's Recommended section. */ -"network.protection.vpn.location.recommended.section.title" = "Recommended"; +"network.protection.vpn.location.recommended.section.title" = "Consigliato"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.selected" = "Posizione selezionata"; @@ -1752,13 +1845,13 @@ "network.protection.vpn.preferred.location.nearest" = "Posizione più vicina"; /* Title for the Preferred Location VPN Settings item. */ -"network.protection.vpn.preferred.location.title" = "Preferred Location"; +"network.protection.vpn.preferred.location.title" = "Posizione preferita"; /* Footer text for the DNS server setting item. */ -"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo routes DNS queries through our DNS servers so your internet provider can't see what websites you visit."; +"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo instrada le query DNS tramite i nostri server DNS in modo che il tuo provider internet non possa vedere quali siti web visiti."; /* Title for the FAQ row in the VPN status screen. */ -"network.protection.vpn.settings.faq" = "FAQs and Support"; +"network.protection.vpn.settings.faq" = "Domande frequenti e assistenza"; /* Title for the feedback row in the VPN status screen. */ "network.protection.vpn.settings.share-feedback" = "Condividi feedback sulla VPN"; @@ -1940,6 +2033,66 @@ /* Deactivate button */ "pm.deactivate" = "Disattiva"; +/* Label for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.label" = "Email (facoltativo)"; + +/* Placeholder for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.placeholder" = "name@email.com"; + +/* Placeholder for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.placeholder" = "Comunicaci la tua opinione..."; + +/* Title for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.title" = "Feedback generale"; + +/* Title for the ITR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-itr-problem.title" = "Identity Theft Restoration"; + +/* Title for the PIR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-pir-problem.title" = "Personal Information Removal"; + +/* Title for the Subscriptions and Payments category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-ppro-problem.title" = "Abbonamenti e pagamenti"; + +/* Placeholder for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.placeholder" = "Raccontaci cosa succede..."; + +/* Title for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.title" = "Segnala un problema"; + +/* Title for the VPN category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-vpn-problem.title" = "VPN"; + +/* Placeholder for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.placeholder" = "Quale funzionalità vorresti vedere?"; + +/* Title for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.title" = "Richiesta di funzionalità"; + +/* Title for the category selection section in the Privacy Pro feedback form */ +"ppro.feedback-form.select-category.title" = "SELEZIONA UNA CATEGORIA"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-1" = "Hai trovato un problema non trattato nel nostro [centro assistenza](duck://)? Ci piacerebbe saperlo.\n\nIndica un indirizzo e-mail se vuoi che ti contattiamo in merito a questo problema (potremmo non essere in grado di rispondere a tutti i problemi):"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-2" = "Oltre ai dettagli inseriti sopra, inviamo alcune informazioni anonime con il tuo feedback:"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-3" = "• Se alcune funzioni del browser sono attive"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-4" = "• Diagnostica aggregata dell'applicazione (ad esempio, codici di errore)"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-5" = "Toccando \"Invia\" accetti che DuckDuckGo possa usare le informazioni inviate per migliorare l'app."; + +/* Title for the text box in the Privacy Pro feedback form */ +"ppro.feedback-form.text-box.title" = "Feedback"; + +/* Title for the Privacy Pro feedback form */ +"ppro.feedback-form.title" = "Invia feedback"; + /* Button title for sync bookmarks limits exceeded warning to go to manage bookmarks */ "prefrences.sync.bookmarks-limit-exceeded-action" = "Gestisci segnalibri"; @@ -2029,6 +2182,18 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Preferiti"; +/* Name of the option the user can chose to give general browser feedback */ +"send.browser.feedback.general-feedback" = "Feedback generale"; + +/* Name of the option the user can chose to give browser feedback about a problem they enountered */ +"send.browser.feedback.report-problem" = "Segnala un problema"; + +/* Name of the option the user can chose to give browser feedback about a feature they would like */ +"send.browser.feedback.request-feature" = "Richiedi una funzionalità"; + +/* Title of the picker where the user can chose the category of the feedback they want ot send. */ +"send.browser.feedback.select-category" = "SELEZIONA UNA CATEGORIA"; + /* Settings cell for About DDG */ "settings.about.ddg" = "Info su DuckDuckGo"; @@ -2084,6 +2249,9 @@ /* Section footer Autolock description */ "settings.autolock.description" = "Se hai attivato Touch ID, Face ID o un passcode di sistema, ti verrà chiesto di sbloccare l'app al momento dell'apertura."; +/* Settings cell for Browser Feedback */ +"settings.browser.feedback" = "Feedback sul browser"; + /* Settings screen cell text for Automatically Clearing Data */ "settings.clear.data" = "Cancellazione automatica dei dati"; @@ -2182,7 +2350,7 @@ "settings.ppro" = "Privacy Pro"; /* Title for Link in the Footer of Privacy Pro section */ -"settings.ppro.footer" = "Privacy Policy and Terms of Service"; +"settings.ppro.footer" = "Privacy policy e Termini di servizio"; /* Settings screen cell for long press previews */ "settings.previews" = "Anteprime con pressione prolungata"; @@ -2208,23 +2376,20 @@ /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Imposta la posizione della barra degli indirizzi"; -/* Subscription activation pending description */ -"settings.subscription.activation.pending.description" = "This is taking longer than usual, please check back later."; +/* Privacy pro description subtitle in settings when the is activating */ +"settings.subscription.activating" = "Attivazione in corso…"; -/* Subscription activation pending title */ -"settings.subscription.activation.pending.title" = "Il tuo abbonamento è in fase di attivazione"; +/* Subscription activation pending description */ +"settings.subscription.activation.pending.description" = "Richiede più tempo del solito, ricontrolla più tardi."; /* Data Broker protection cell subtitle for privacy pro */ -"settings.subscription.DBP.subtitle" = "Remove your info from sites that sell it"; +"settings.subscription.DBP.subtitle" = "Rimuovi le tue info dai siti che le vendono"; /* Data Broker protection cell title for privacy pro */ "settings.subscription.DBP.title" = "Personal Information Removal"; -/* Privacy pro description subtext */ -"settings.subscription.description" = "Più tutela della privacy senza interruzioni con tre nuove protezioni:"; - /* I have a Subscription button text for privacy pro */ -"settings.subscription.existing.subscription" = "I Have a Subscription"; +"settings.subscription.existing.subscription" = "Ho un abbonamento"; /* Subscription expired description */ "settings.subscription.expired.comment" = "Iscriviti di nuovo per continuare a utilizzare Privacy Pro"; @@ -2232,23 +2397,26 @@ /* Subscription expired tittle message */ "settings.subscription.expired.title" = "Il tuo abbonamento Privacy Pro è scaduto"; -/* Privacy pro features list */ -"settings.subscription.features" = " • VPN\n • Personal Information Removal\n • Identity Theft Restoration"; - /* Identity theft restoration cell subtitle for privacy pro */ -"settings.subscription.ITR.subtitle" = "If your identity is stolen, we'll help restore it"; +"settings.subscription.ITR.subtitle" = "Se la tua identità viene rubata, ti aiuteremo a ripristinarla"; /* Identity theft restoration cell title for privacy pro */ "settings.subscription.ITR.title" = "Identity Theft Restoration"; /* Get Privacy Pro button text for privacy pro */ -"settings.subscription.learn.more" = "Get Privacy Pro"; +"settings.subscription.learn.more" = "Ottieni Privacy Pro"; /* Subscription Settings button text for privacy pro */ -"settings.subscription.manage" = "Subscription Settings"; +"settings.subscription.manage" = "Impostazioni dell'abbonamento"; + +/* Privacy Pro description subtitle in settings */ +"settings.subscription.row.description" = "Include i nostri servizi di VPN e Identity Theft Restoration."; -/* Call to action title for Privacy Pro */ -"settings.subscription.subscribe" = "Subscribe to Privacy Pro"; +/* Call to action title for Privacy Pro settings */ +"settings.subscription.subscribe" = "Proteggi la tua connessione e la tua identità con Privacy Pro"; + +/* Privacy pro description subtitle in settings */ +"settings.subscription.us.description" = "Includi VPN, Personal Information Removal e Identity Theft Restoration."; /* VPN cell title for privacy pro */ "settings.subscription.VPN.title" = "VPN"; @@ -2317,67 +2485,64 @@ "siteFeedback.urlPlaceholder" = "Quale sito web è danneggiato?"; /* Subscription Activation Info */ -"subscription.activate..header.description" = "Access your Privacy Pro subscription on this device via Apple ID or an email address."; +"subscription.activate..header.description" = "Accedi al tuo abbonamento Privacy Pro su questo dispositivo tramite ID Apple o un indirizzo e-mail."; /* Button for adding email address to subscription */ -"subscription.activate.add.email.button" = "Add Email"; +"subscription.activate.add.email.button" = "Aggiungi e-mail"; /* Apple ID option for activation */ -"subscription.activate.appleid" = "Apple ID"; +"subscription.activate.appleid" = "ID Apple"; /* Button text for restoring purchase via Apple ID */ -"subscription.activate.appleid.button" = "Restore Purchase"; +"subscription.activate.appleid.button" = "Ripristina acquisto"; /* Description for Apple ID activation */ -"subscription.activate.appleid.description" = "Restore your purchase to activate your subscription on this device."; +"subscription.activate.appleid.description" = "Ripristina il tuo acquisto per attivare l'abbonamento su questo dispositivo."; /* Subscription Activation Info */ -"subscription.activate.description" = "Your subscription is automatically available in DuckDuckGo on any device signed in to your Apple ID."; +"subscription.activate.description" = "Il tuo abbonamento è automaticamente disponibile in DuckDuckGo su qualsiasi dispositivo connesso al tuo ID Apple."; /* Button for editing email address added to subscription */ -"subscription.activate.edit.email.button" = "Edit Email"; +"subscription.activate.edit.email.button" = "Modifica email"; /* View Title for editing your email account */ -"subscription.activate.edit.email.title" = "Edit Email"; +"subscription.activate.edit.email.title" = "Modifica email"; /* Email option for activation */ "subscription.activate.email" = "Email"; /* Restore button title for Email */ -"subscription.activate.email.button" = "Enter Email"; +"subscription.activate.email.button" = "Inserisci l'e-mail"; /* Description for Email activation */ -"subscription.activate.email.description" = "Use your email to activate your subscription on this device."; +"subscription.activate.email.description" = "Usa la tua e-mail per attivare l'abbonamento su questo dispositivo."; /* Activate subscription title */ "subscription.activate.email.title" = "Attiva abbonamento"; /* Button title for cancelling email deletion */ -"subscription.activate.manage.email.cancel" = "Cancel"; +"subscription.activate.manage.email.cancel" = "Annulla"; /* Button title for confirming email deletion */ "subscription.activate.manage.email.OK" = "OK"; /* Restore button title for AppleID */ -"subscription.activate.restore.apple" = "Restore Purchase"; +"subscription.activate.restore.apple" = "Ripristina acquisto"; /* Subscription Activation Title */ -"subscription.activate.title" = "Activate your subscription on this device"; +"subscription.activate.title" = "Attiva il tuo abbonamento su questo dispositivo"; /* Resend activation instructions button */ -"subscription.add.device.resend.instructions" = "Resend Instructions"; +"subscription.add.device.resend.instructions" = "Invia nuovamente le istruzioni"; /* Add email to an existing subscription */ -"subscription.add.email" = "Add an email address to activate your subscription on your other devices. We’ll only use this address to verify your subscription."; +"subscription.add.email" = "Aggiungi un indirizzo e-mail per attivare il tuo abbonamento sugli altri dispositivi. Utilizzeremo questo indirizzo solo per verificare l'abbonamento."; /* View title for adding email to subscription */ -"subscription.add.email.title" = "Add Email"; - -/* Title for Alert messages */ -"subscription.alert.title" = "subscription.alert.title"; +"subscription.add.email.title" = "Aggiungi e-mail"; /* Subscription availability message on Apple devices */ -"subscription.available.apple" = "Privacy Pro is available on any device signed in to the same Apple ID."; +"subscription.available.apple" = "Privacy Pro è disponibile su qualsiasi dispositivo connesso allo stesso ID Apple."; /* Text for the manage billing page */ "subscription.billing.google.text" = "Il tuo abbonamento è stato acquistato tramite Google Play Store. Per effettuare il rinnovo, accedi alle impostazioni dell'abbonamento di Google Play Store su un dispositivo collegato allo stesso account Google utilizzato per effettuare l'acquisto originale dell'abbonamento."; @@ -2385,89 +2550,86 @@ /* Title for the manage billing page */ "subscription.billing.google.title" = "Piani di abbonamento"; -/* Subscription annual billing period type */ -"subscription.billing.period.annual" = "annual"; - -/* Subscription monthly billing period type */ -"subscription.billing.period.monthly" = "monthly"; - /* Subscription Removal confirmation message */ -"subscription.cancel.message" = "Your subscription has been removed from this device."; +"subscription.cancel.message" = "Il tuo abbonamento è stato rimosso da questo dispositivo."; /* Change plan or cancel title */ -"subscription.change.plan" = "Update Plan or Cancel"; +"subscription.change.plan" = "Aggiorna o o annulla il piano"; /* Navigation Button for closing subscription view */ -"subscription.close" = "Close"; +"subscription.close" = "Chiudi"; /* Title for Confirm messages */ -"subscription.confirm.title" = "Are you sure?"; +"subscription.confirm.title" = "Procedere?"; /* Header for section for activating subscription on other devices */ -"subscription.devices.header" = "Activate on Other Devices"; +"subscription.devices.header" = "Attiva sugli altri dispositivi"; /* Footer for section for activating subscription on other devices when email was not yet added */ -"subscription.devices.no.email.footer" = "Add an optional email to your subscription or use your Apple ID to access Privacy Pro on other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.no.email.footer" = "Aggiungi un'e-mail opzionale al tuo abbonamento per accedere a Privacy Pro su altri dispositivi. **[Scopri di più](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Footer for section for activating subscription on other devices when email is added */ -"subscription.devices.with.email.footer" = "Use this email to activate your subscription in Settings > Privacy Pro in the DuckDuckGo app on your other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.with.email.footer" = "Usa questa e-mail per attivare il tuo abbonamento in Impostazioni > Privacy Pro nell'app DuckDuckGo sugli altri tuoi dispositivi. **[Scopri di più](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Alert content for not found subscription */ -"subscription.email.inactive.alert.message" = "The subscription associated with this email is no longer active."; +"subscription.email.inactive.alert.message" = "L'abbonamento associato a questa e-mail non è più attivo."; /* Alert title for not found subscription */ -"subscription.email.inactive.alert.title" = "Subscription Not Found"; +"subscription.email.inactive.alert.title" = "Abbonamento non trovato"; /* Alert content for not found subscription */ -"subscription.expired.alert.message" = "The subscription associated with this Apple ID is no longer active."; +"subscription.expired.alert.message" = "L'abbonamento associato a questo ID Apple non è più attivo."; /* FAQ Button */ -"subscription.faq" = "FAQs and Support"; +"subscription.faq" = "Domande frequenti e assistenza"; /* FAQ Description */ -"subscription.faq.description" = "Get answers to frequently asked questions or contact Privacy Pro support from our help pages."; +"subscription.faq.description" = "Ottieni risposte alle domande frequenti o contatta l'assistenza di Privacy Pro dalle nostre pagine di assistenza."; + +/* Send Feedback Button */ +"subscription.feedback" = "Invia feedback"; /* Cancel action for the existing subscription dialog */ -"subscription.found.cancel" = "Cancel"; +"subscription.found.cancel" = "Annulla"; /* Restore action for the existing subscription dialog */ -"subscription.found.restore" = "Restore"; +"subscription.found.restore" = "Ripristina"; /* Message for the existing subscription dialog */ -"subscription.found.text" = "We found a subscription associated with this Apple ID."; +"subscription.found.text" = "Abbiamo trovato un abbonamento associato a questo ID Apple."; /* Title for the existing subscription dialog */ -"subscription.found.title" = "Subscription Found"; +"subscription.found.title" = "Abbonamento trovato"; /* Help and support Section header */ -"subscription.help" = "Help and support"; +"subscription.help" = "Aiuto e assistenza"; /* Manage Plan header */ -"subscription.manage.plan" = "Manage Plan"; +"subscription.manage.plan" = "Gestisci il piano"; /* Header for the subscription section */ -"subscription.manage.title" = "Subscription"; +"subscription.manage.title" = "Abbonamento"; /* Alert content for not found subscription */ -"subscription.notFound.alert.message" = "There is no subscription associated with this Apple ID."; +"subscription.notFound.alert.message" = "Nessun abbonamento associato a questo ID Apple."; /* Alert title for not found subscription */ -"subscription.notFound.alert.title" = "Subscription Not Found"; +"subscription.notFound.alert.title" = "Abbonamento non trovato"; /* View plans button text */ -"subscription.notFound.view.plans" = "View Plans"; +"subscription.notFound.view.plans" = "Visualizza i piani"; /* Hero Text for Personal information removal */ -"subscription.pir.hero" = "Activate Privacy Pro on desktop to set up Personal Information Removal"; +"subscription.pir.hero" = "Attiva Privacy Pro sul desktop per impostare Personal Information Removal"; /* Description on how to use Personal information removal in desktop. The first placeholder references a location in the Desktop application. Privacy Pro>, and the second, the menu entry. i.e. */ -"subscription.pir.heroText" = "In the DuckDuckGo browser for desktop, go to %1$@ and click %2$@ to get started."; +"subscription.pir.heroText" = "Nel browser DuckDuckGo per desktop, vai a %1$@ e fai clic su %2$@ per iniziare."; /* Settings references a menu in the Desktop app, Privacy Pro, references our product name */ -"subscription.pir.heroTextLocation" = "Settings > Privacy Pro"; +"subscription.pir.heroTextLocation" = "Impostazioni > Privacy Pro"; /* Menu item for enabling Personal Information Removal on Desktop */ -"subscription.pir.heroTextMenyEntry" = "I have a subscription"; +"subscription.pir.heroTextMenyEntry" = "Ho un abbonamento"; /* Text for the 'macOS' button */ "subscription.pir.macos" = "Mac"; @@ -2476,28 +2638,28 @@ "subscription.pir.windows" = "Windows"; /* Progress view title when completing the purchase */ -"subscription.progress.view.completing.purchase" = "Completing purchase..."; +"subscription.progress.view.completing.purchase" = "Stiamo completando l'acquisto..."; /* Progress view title when starting the purchase */ -"subscription.progress.view.purchasing.subscription" = "Purchase in progress..."; +"subscription.progress.view.purchasing.subscription" = "Acquisto in corso..."; /* Progress view title when restoring past subscription purchase */ -"subscription.progress.view.restoring.subscription" = "Restoring subscription..."; +"subscription.progress.view.restoring.subscription" = "Stiamo ripristinando l'abbonamento..."; /* Remove from this device button */ -"subscription.remove.from.device.button" = "Remove From This Device"; +"subscription.remove.from.device.button" = "Rimuovi da questo dispositivo"; /* Remove from device confirmation dialog text */ -"subscription.remove.from.device.text" = "You will no longer be able to access your Privacy Pro subscription on this device. This will not cancel your subscription, and it will remain active on your other devices."; +"subscription.remove.from.device.text" = "Non potrai più accedere al tuo abbonamento Privacy Pro su questo dispositivo. Questo non annulla il tuo abbonamento, che rimane attivo sugli altri tuoi dispositivi."; /* Remove from device confirmation dialog title */ -"subscription.remove.from.device.title" = "Remove from this device?"; +"subscription.remove.from.device.title" = "Rimuovere da questo dispositivo?"; /* Remove subscription button text */ -"subscription.remove.subscription" = "Remove Subscription"; +"subscription.remove.subscription" = "Rimuovi abbonamento"; /* Remove subscription cancel button text */ -"subscription.remove.subscription.cancel" = "Cancel"; +"subscription.remove.subscription.cancel" = "Annulla"; /* Button text for general error message */ "subscription.restore.backend.error.button" = "Torna a Impostazioni"; @@ -2509,28 +2671,43 @@ "subscription.restore.backend.error.title" = "Si è verificato un errore"; /* Alert for general error message */ -"subscription.restore.general.error.message" = "The App Store was unable to process your purchase. Please try again later."; +"subscription.restore.general.error.message" = "L'App Store non è riuscito a elaborare l'acquisto. Riprova in seguito."; /* Alert for general error title */ -"subscription.restore.general.error.title" = "Something Went Wrong"; +"subscription.restore.general.error.title" = "Si è verificato un errore"; /* Alert button text for restored purchase alert */ "subscription.restore.success.alert.button" = "OK"; /* Alert message for restored purchase */ -"subscription.restore.success.alert.message" = "Your purchases have been restored."; +"subscription.restore.success.alert.message" = "I tuoi acquisti sono stati ripristinati."; /* Alert title for restored purchase */ -"subscription.restore.success.alert.title" = "You’re all set."; +"subscription.restore.success.alert.title" = "Tutto pronto."; + +/* Subtitle in header when subscribed */ +"subscription.subscribed" = "Iscritto"; /* Subscription Expired Data. This reads as 'Your subscription expired on (date)' */ "subscription.subscription.expired.caption" = "Il tuo abbonamento è scaduto il % @"; -/* Subscription expiration info. This reads as 'Your (monthly or annual) subscription expires on (date)' */ -"subscription.subscription.expiring.caption" = "Your %1$@ subscription expires on %2$@."; +/* Monthly subscription expiration info where parameter is expiration date. This reads as 'Your monthly subscription expires on (date)' */ +"subscription.subscription.expiring.monthly.caption" = "Il tuo abbonamento mensile scade il %@."; + +/* Unknown period subscription expiration info where parameter is expiration date. This reads as 'Your subscription expires on (date)' */ +"subscription.subscription.expiring.unknown.caption" = "Il tuo abbonamento scade il %@."; + +/* Annual subscription expiration info where parameter is expiration date. This reads as 'Your annual subscription expires on (date)' */ +"subscription.subscription.expiring.yearly.caption" = "Il tuo abbonamento annuale scade il %@."; + +/* Monthly subscription renewal info where parameter is renewal date. This reads as 'Your monthly subscription renews on (date)' */ +"subscription.subscription.renewing.monthly.caption" = "Il tuo abbonamento mensile si rinnova il %@."; + +/* Unknown period subscription renewal info where parameter is renewal date. This reads as 'Your subscription renews on (date)' */ +"subscription.subscription.renewing.unknown.caption" = "Il tuo abbonamento si rinnova il %@."; -/* Subscription renewal info. This reads as 'Your (monthly or annual) subscription renews on (date)' */ -"subscription.subscription.renewing.caption" = "Your %1$@ subscription renews on %2$@."; +/* Annual subscription renewal info where parameter is renewal date. This reads as 'Your annual subscription renews on (date)' */ +"subscription.subscription.renewing.yearly.caption" = "Il tuo abbonamento annuale si rinnova il %@."; /* Navigation bar Title for subscriptions */ "subscription.title" = "Privacy Pro"; @@ -2677,118 +2854,118 @@ "voiceSearch.footer.note.old" = "L'audio viene elaborato sul dispositivo e non viene memorizzato né condiviso con altri utenti, nemmeno con DuckDuckGo."; /* Cancel action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.cancel" = "Dismiss"; +"vpn.access-revoked.alert.action.cancel" = "Ignora"; /* Primary action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.subscribe" = "Subscribe"; +"vpn.access-revoked.alert.action.subscribe" = "Iscriviti"; /* Alert message for the alert when the Privacy Pro subscription expiress */ -"vpn.access-revoked.alert.message" = "Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"vpn.access-revoked.alert.message" = "Iscriviti a Privacy Pro per riconnettere la VPN di DuckDuckGo."; /* Alert title for the alert when the Privacy Pro subscription expires */ -"vpn.access-revoked.alert.title" = "VPN disconnected due to expired subscription"; +"vpn.access-revoked.alert.title" = "La VPN si è scollegata a causa di un abbonamento scaduto"; /* Title for the VPN widget onboarding screen */ "vpn.addWidget.settings.title" = "Trova e seleziona DuckDuckGo. Quindi scorri fino a VPN e seleziona Aggiungi widget."; /* Title for the Cancel button of the VPN feedback form */ -"vpn.feedback-form.button.cancel" = "Cancel"; +"vpn.feedback-form.button.cancel" = "Annulla"; /* Title for the Done button of the VPN feedback form */ -"vpn.feedback-form.button.done" = "Done"; +"vpn.feedback-form.button.done" = "Fatto"; /* Title for the Submit button of the VPN feedback form */ -"vpn.feedback-form.button.submit" = "Submit"; +"vpn.feedback-form.button.submit" = "Invia"; /* Title for the Submitting state of the VPN feedback form */ -"vpn.feedback-form.button.submitting" = "Submitting…"; +"vpn.feedback-form.button.submitting" = "Invio in corso..."; /* Title for the browser crash/freeze category of the VPN feedback form */ -"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN causes browser to crash or freeze"; +"vpn.feedback-form.category.browser-crash-or-freeze" = "La VPN causa l'arresto anomalo o il blocco del browser"; /* Title for the 'VPN fails to connect' category of the VPN feedback form */ -"vpn.feedback-form.category.fails-to-connect" = "VPN fails to connect"; +"vpn.feedback-form.category.fails-to-connect" = "La VPN non si connette"; /* Title for the 'VPN feature request' category of the VPN feedback form */ -"vpn.feedback-form.category.feature-request" = "VPN feature request"; +"vpn.feedback-form.category.feature-request" = "Richiesta di funzionalità VPN"; /* Title for the category 'VPN causes issues with other apps or websites' category of the VPN feedback form */ -"vpn.feedback-form.category.issues-with-apps" = "VPN causes issues with other apps or websites"; +"vpn.feedback-form.category.issues-with-apps" = "La VPN causa problemi con altre app o siti web"; /* Title for the local device connectivity category of the VPN feedback form */ -"vpn.feedback-form.category.local-device-connectivity" = "VPN won't let me connect to local device"; +"vpn.feedback-form.category.local-device-connectivity" = "Non mi consente di connettermi al dispositivo locale"; /* Title for the 'other VPN feedback' category of the VPN feedback form */ -"vpn.feedback-form.category.other" = "Other VPN feedback"; +"vpn.feedback-form.category.other" = "Altri feedback sulla VPN"; /* Title for the category selection state of the VPN feedback form */ -"vpn.feedback-form.category.select-category" = "Select a category"; +"vpn.feedback-form.category.select-category" = "SELEZIONA UNA CATEGORIA"; /* Title for the 'VPN is too slow' category of the VPN feedback form */ -"vpn.feedback-form.category.too-slow" = "VPN connection is too slow"; +"vpn.feedback-form.category.too-slow" = "La connessione VPN è troppo lenta"; /* Title for the 'unable to install' category of the VPN feedback form */ -"vpn.feedback-form.category.unable-to-install" = "Unable to install VPN"; +"vpn.feedback-form.category.unable-to-install" = "Impossibile installare la VPN"; /* Title for the feedback sent view description of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.description" = "Your feedback will help us improve the\nDuckDuckGo VPN."; +"vpn.feedback-form.sending-confirmation.description" = "La tua opinione ci aiuterà a migliorare la VPN DuckDuckGo."; /* Title for the feedback sending error text of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.error" = "We couldn't send your feedback right now, please try again."; +"vpn.feedback-form.sending-confirmation.error" = "Non siamo riusciti a inviare il tuo feedback in questo momento, riprova."; /* Title for the feedback sent view title of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.title" = "Thank you!"; +"vpn.feedback-form.sending-confirmation.title" = "Grazie!"; /* Toast message when the VPN feedback form is submitted successfully */ -"vpn.feedback-form.submitted.message" = "Thank You! Feedback submitted."; +"vpn.feedback-form.submitted.message" = "Grazie! Il feedback è stato inviato."; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-1" = "Please describe what's happening, what you expected to happen, and the steps that led to the issue:"; +"vpn.feedback-form.text-1" = "Descrivi cosa sta succedendo, cosa ti aspettavi che accadesse e i passaggi che hanno portato al problema:"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-2" = "In addition to the details entered into this form, your app issue report will contain:"; +"vpn.feedback-form.text-2" = "Oltre ai dettagli inseriti in questo modulo, la segnalazione sui problemi dell'app conterrà le seguenti informazioni:"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-3" = "• Whether specific DuckDuckGo features are enabled"; +"vpn.feedback-form.text-3" = "• Comunicherà se specifiche funzioni di DuckDuckGo sono attive"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-4" = "• Aggregate DuckDuckGo app diagnostics"; +"vpn.feedback-form.text-4" = "• Aggregherà le diagnostiche dell'app DuckDuckGo"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-5" = "By tapping \"Submit\" I agree that DuckDuckGo may use the information in this report for purposes of improving the app's features."; +"vpn.feedback-form.text-5" = "Toccando \"Invia\" accetto che DuckDuckGo possa usare le informazioni in questo rapporto per migliorare le funzionalità dell'app."; /* Title for each screen of the VPN feedback form */ -"vpn.feedback-form.title" = "Help Improve the DuckDuckGo VPN"; +"vpn.feedback-form.title" = "Aiuta a migliorare la VPN di DuckDuckGo"; /* VPN settings screen cell text for adding the VPN widget to the home screen */ "vpn.settings.add.widget" = "Aggiungi il widget VPN alla schermata iniziale"; /* Disclaimer for the DNS Server section on the DNS Server screen */ -"vpn.settings.dns.section-disclaimer" = "Using a custom DNS server can impact browsing speeds and expose your activity to third parties if the server isn't secure or reliable."; +"vpn.settings.dns.section-disclaimer" = "L'utilizzo di un server DNS personalizzato può incidere sulla velocità di navigazione ed esporre la tua attività a terzi se il server non è sicuro o affidabile."; /* Header text for the DNS section on the VPN Settings screen */ -"vpn.settings.dns.section-header" = "DNS"; +"vpn.settings.dns.section-header" = "dns"; /* Title for the Apply button on the DNS Server setting screen */ -"vpn.settings.dns.server.apply.button.title" = "Apply"; +"vpn.settings.dns.server.apply.button.title" = "Applica"; /* Default value for the DNS Server row on the VPN Settings screen */ "vpn.settings.dns.server.default.value" = "DuckDuckGo"; /* Title for the IPv4 Address setting */ -"vpn.settings.dns.server.ipv4.title" = "IPv4 Address"; +"vpn.settings.dns.server.ipv4.title" = "Indirizzo IPv4"; /* Custom option for the DNS Server setting */ -"vpn.settings.dns.server.option.custom" = "Custom"; +"vpn.settings.dns.server.option.custom" = "Personalizzato"; /* Recommended option for the DNS Server setting */ -"vpn.settings.dns.server.option.default" = "DuckDuckGo (Recommended)"; +"vpn.settings.dns.server.option.default" = "DuckDuckGo (consigliato)"; /* Title for the DNS Server setting screen */ -"vpn.settings.dns.server.screen.title" = "DNS Server"; +"vpn.settings.dns.server.screen.title" = "Server DNS"; /* Title for the DNS Server row on the VPN Settings screen */ -"vpn.settings.dns.server.title" = "DNS Server"; +"vpn.settings.dns.server.title" = "Server DNS"; /* Title for the button to enable push notifications in system settings */ "waitlist.allow-notifications" = "Consenti notifiche"; diff --git a/DuckDuckGo/it.lproj/OmniBar.strings b/DuckDuckGo/it.lproj/OmniBar.strings index 853bd8f1f3..6f9b0fa2ea 100644 --- a/DuckDuckGo/it.lproj/OmniBar.strings +++ b/DuckDuckGo/it.lproj/OmniBar.strings @@ -7,6 +7,9 @@ /* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "Dt8-We-5tV"; */ "Dt8-We-5tV.accessibilityLabel" = "Cancella testo"; +/* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "gb5-wL-4OW"; */ +"gb5-wL-4OW.accessibilityLabel" = "Cancella testo"; + /* Class = "UIButton"; accessibilityLabel = "Share"; ObjectID = "hor-Jd-3kw"; */ "hor-Jd-3kw.accessibilityLabel" = "Condividi"; diff --git a/DuckDuckGo/it.lproj/Settings.strings b/DuckDuckGo/it.lproj/Settings.strings index 983d5e7c25..f3c80f6a9a 100644 --- a/DuckDuckGo/it.lproj/Settings.strings +++ b/DuckDuckGo/it.lproj/Settings.strings @@ -10,9 +10,6 @@ /* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "5YD-Od-uDU"; */ "5YD-Od-uDU.text" = "Cancellazione automatica dei dati"; -/* Class = "UIButton"; normalTitle = "Settings"; ObjectID = "8YG-7Q-Czd"; */ -"8YG-7Q-Czd.normalTitle" = "Impostazioni"; - /* Class = "UILabel"; text = "App Exit, Inactive for 1 Hour"; ObjectID = "9EC-lx-77p"; */ "9EC-lx-77p.text" = "Uscita dall'app, inattività di 1 ora"; @@ -76,9 +73,6 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Siti non protetti"; -/* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ -"ssa-zd-L3T.title" = "Dimensione testo"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Visualizza tastiera su"; diff --git a/DuckDuckGo/lt.lproj/DaxOnboarding.strings b/DuckDuckGo/lt.lproj/DaxOnboarding.strings index 63f9e199b9..70e8818674 100644 --- a/DuckDuckGo/lt.lproj/DaxOnboarding.strings +++ b/DuckDuckGo/lt.lproj/DaxOnboarding.strings @@ -1,12 +1,6 @@ /* Class = "UIButton"; normalTitle = "Hide"; ObjectID = "hgy-75-OvJ"; */ "hgy-75-OvJ.normalTitle" = "Slėpti"; -/* Class = "UILabel"; text = "Welcome to\nDuckDuckGo!"; ObjectID = "Lvy-9G-3gr"; */ -"Lvy-9G-3gr.text" = "Sveiki atvykę į\n„DuckDuckGo“!"; - -/* Class = "UIButton"; normalTitle = "Let’s Do It!"; ObjectID = "oAv-Zt-uIt"; */ -"oAv-Zt-uIt.normalTitle" = "Pirmyn!"; - /* Class = "UIButton"; normalTitle = "Button"; ObjectID = "zJt-F9-L48"; */ "zJt-F9-L48.normalTitle" = "Mygtukas"; diff --git a/DuckDuckGo/lt.lproj/Localizable.strings b/DuckDuckGo/lt.lproj/Localizable.strings index a4a5da7f0e..ec407015d1 100644 --- a/DuckDuckGo/lt.lproj/Localizable.strings +++ b/DuckDuckGo/lt.lproj/Localizable.strings @@ -341,7 +341,7 @@ "autofill.keep-enabled.alert.disable" = "Išjungti"; /* A link that takes the user to the DuckDuckGo help pages explaining password managers */ -"autofill.learn.more.link.title" = "Learn More"; +"autofill.learn.more.link.title" = "Sužinoti daugiau"; /* Button displayed after saving/updating an autofill login that takes the user to the saved login */ "autofill.login-save-action-button.toast" = "Peržiūrėti"; @@ -977,9 +977,6 @@ /* ad = advertisment */ "dax.onboarding.home.subsequent" = "Atlikai!\n\nĮsidėmėkite: kiekvieną kartą, kai naršote su manimi, bauginantis skelbimas praranda galią. 👍"; -/* No comment provided by engineer. */ -"dax.onboarding.message" = "Internetas gali būti bauginantis.\n\nNesijaudink! Ieškoti ir naršyti privačiai yra lengviau, nei manai."; - /* No comment provided by engineer. */ "Debug" = "Derinimas"; @@ -1080,7 +1077,7 @@ "duckPlayer.never.label" = "Niekada"; /* Body text for the modal feature explanation */ -"duckplayer.presentation.modal.body" = "„Duck Player“ leidžia sistemoje „DuckDuckGo“ tarsi kino teatre žiūrėti „YouTube“ be taikomų reklamų, o tai, ką žiūrite, neturės įtakos jūsų rekomendacijoms."; +"duckplayer.presentation.modal.body" = "„Duck Player“ leidžia sistemoje „DuckDuckGo“ žiūrėti „YouTube“ be taikomų reklamų, o tai, ką žiūrite, neturės įtakos jūsų rekomendacijoms."; /* Button that will dismiss the modal */ "duckplayer.presentation.modal.dismiss-button" = "Supratau!"; @@ -1088,17 +1085,23 @@ /* Two line title (separated by \n) for the feature explanation */ "duckplayer.presentation.modal.title" = "Matote per daug „YouTube“ skelbimų? Išbandykite „Duck Player“!"; +/* Two line title (separated by \n) for the feature explanation */ +"duckplayer.presentation.modal.title.youtube" = "Matote per daug „YouTube“ skelbimų? Ne su „Duck Player“!"; + /* Footer label in the settings screen for Duck Player */ "duckplayer.settings.footer" = "„DuckDuckGo“ suteikia visas pagrindines privatumą užtikrinančias priemones, kurių reikia norint apsisaugoti naršant internete."; /* Text explaining what Duck Player is in the settings screen. */ -"duckplayer.settings.info-text" = "„Duck Player“ užtikrina nepriekaištingą žiūrėjimo patirtį be suasmenintų reklamų ir neleidžia žiūrėjimo veiklai daryti įtakos „YouTube“ rekomendacijoms."; +"duckplayer.settings.info-text" = "„Duck Player“ leidžia sistemoje „DuckDuckGo“ žiūrėti „YouTube“ be taikomų reklamų, o tai, ką žiūrite, neturės įtakos jūsų rekomendacijoms."; /* Button that takes the user to learn more about Duck Player. */ "duckplayer.settings.learn-more" = "Sužinoti daugiau"; +/* Settings screen cell text for DuckPlayer settings to open in new tab */ +"duckplayer.settings.open-new-tab-label" = "Atidarykite „Duck Player“ naujame skirtuke"; + /* Settings screen cell text for DuckPlayer settings */ -"duckplayer.settings.open-videos-in" = "Atidarykite vaizdo įrašus „Duck Player“."; +"duckplayer.settings.open-videos-in" = "Atidaryti „YouTube“ vaizdo įrašus „Duck Player“"; /* Settings screen cell text for DuckPlayer settings */ "duckplayer.settings.title" = "Duck Player"; @@ -1169,9 +1172,6 @@ /* Title for prompt to sign up for email protection */ "email.signup-prompt.title" = "Paslėpkite savo el. paštą ir \n blokuokite stebėjimo priemones"; -/* No comment provided by engineer. */ -"empty!" = "tuščia!"; - /* Empty list state placholder */ "empty.bookmarks" = "Žymių dar nepridėta"; @@ -1322,6 +1322,39 @@ /* Confirmation button */ "feedback.form.submit" = "Pateikti"; +/* Title of the feedback form */ +"feedback.form.title" = "Padėk pagerinti „Privacy Pro“"; + +/* Category for Identity Theft Restoration feedback */ +"feedback.general.category.itr" = "Identity Theft Restoration"; + +/* Category for Personal Info Removal feedback */ +"feedback.general.category.pir" = "Asmeninės informacijos pašalinimas"; + +/* Category for subscription and payments feedback */ +"feedback.general.category.ppro" = "Prenumerata ir mokėjimai"; + +/* Prompt to select a category for general feedback */ +"feedback.general.category.select" = "PASIRINKITE KATEGORIJĄ"; + +/* Category for VPN feedback */ +"feedback.general.category.vpn" = "VPN"; + +/* Category for access code issues */ +"feedback.itr.category.access-code" = "Problema su prieigos kodu"; + +/* Category for issues contacting an advisor */ +"feedback.itr.category.cant-contact-advisor" = "Nepavyksta susisiekti su konsultantu"; + +/* Prompt to select a category for Identity Theft Restoration feedback */ +"feedback.itr.category.select" = "PASIRINKITE KATEGORIJĄ"; + +/* Category for other Identity Theft Restoration issues */ +"feedback.itr.category.something-else" = "Kažkas kito"; + +/* Category for unhelpful advisor calls */ +"feedback.itr.category.unhelpful" = "Skambutis konsultantui buvo nenaudingas"; + /* No comment provided by engineer. */ "feedback.negative.form.genericPlaceholder" = "Nurodykite kuo konkrečiau"; @@ -1361,6 +1394,24 @@ /* No comment provided by engineer. */ "feedback.performance.slowLoading" = "Žiniatinklio puslapiai arba paieškos rezultatai įkeliami lėtai"; +/* Category for when scan finds incorrect records */ +"feedback.pir.category.not-me" = "Nuskaitymas aptiko įrašus, kurie nėra mano"; + +/* Category for when scan doesn't find info on a specific site */ +"feedback.pir.category.nothing-on-site" = "Nuskaitymas nerado mano informacijos konkrečioje svetainėje"; + +/* Category for other Personal Info Removal issues */ +"feedback.pir.category.other" = "Kažkas kito"; + +/* Category for when the removal process is stuck */ +"feedback.pir.category.removal-stuck" = "Pašalinimo procesas užstrigo"; + +/* Category for when the scan is stuck */ +"feedback.pir.category.scan-stuck" = "Įrašų nuskaitymas užstrigo"; + +/* Prompt to select a category for Personal Info Removal feedback */ +"feedback.pir.category.select" = "PASIRINKITE KATEGORIJĄ"; + /* Header above input field */ "feedback.positive.form.header" = "Dalintis informacija"; @@ -1379,6 +1430,15 @@ /* Button encouraging uses to share details aboout their feedback */ "feedback.positive.submit" = "Dalintis informacija"; +/* Category for other Privacy Pro issues */ +"feedback.ppro.category.other" = "Kažkas kito"; + +/* Category for one-time password issues */ +"feedback.ppro.category.otp" = "Problema su vienkartiniu slaptažodžiu"; + +/* Prompt to select a category for Privacy Pro feedback */ +"feedback.ppro.category.select" = "PASIRINKITE KATEGORIJĄ"; + /* No comment provided by engineer. */ "feedback.start.footer" = "Jūsų anoniminiai atsiliepimai mums labai svarbūs."; @@ -1529,12 +1589,6 @@ /* No comment provided by engineer. */ "keyCommandShowAllTabs" = "Rodyti visus skirtukus"; -/* Please preserve newline character */ -"launchscreenWelcomeMessage" = "Sveiki atvykę į\n„DuckDuckGo“!"; - -/* No comment provided by engineer. */ -"LOREM IPSUM" = "LOREM IPSUM"; - /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "„Mac“ skirta „DuckDuckGo“ pasižymi jums reikiama sparta, naršymo funkcijomis ir aukščiausios klasės privatumo įrankiais."; @@ -1586,62 +1640,83 @@ /* Edit button */ "navigation.title.edit" = "Redaguoti"; -/* String indicating NetP is connected when viewed from the settings screen */ -"netP.cell.connected" = "Connected"; - -/* String indicating NetP is disconnected when viewed from the settings screen */ -"netP.cell.disconnected" = "Not connected"; - -/* Title for the DuckDuckGo VPN feature in settings */ -"netP.settings.title" = "VPN"; +/* String indicating NetP is snoozing when viewed from the settings screen */ +"netP.cell.snoozing" = "Pristabdymas"; /* Title for the DuckDuckGo VPN feature */ "netP.title" = "DuckDuckGo VPN"; +/* Button title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.action" = "Pridėti valdiklį"; + +/* Message for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.message" = "Įjungti ir išjungti VPN tiesiai iš pagrindinio ekrano."; + +/* Title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.title" = "Pridėti VPN valdiklį"; + /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN atjungtas dėl pasibaigusios prenumeratos. „DuckDuckGo“ VPN galima naudoti nemokamai, kol veikia beta versija."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Nepavyko prisijungti prie tinklo apsaugos. Pabandykite dar kartą vėliau."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "„DuckDuckGo“ VPN nepavyko prisijungti. Bandykite dar kartą vėliau."; /* Title for the VPN FAQ screen. */ "network.protection.faq.title" = "„DuckDuckGo“ VPN DUK"; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Tinklo apsauga buvo nutraukta. Dabar bandome prisijungti..."; +/* Message for tooltip about geoswitching */ +"network.protection.geoswitching.tip.message" = "Galite pritaikyti savo VPN vietą prisijungdami prie bet kurio mūsų serverio visame pasaulyje."; + +/* Title for tooltip about geoswitching */ +"network.protection.geoswitching.tip.title" = "Keisti savo vietą"; + +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "„DuckDuckGo“ VPN buvo pertrauktas. Dabar bandome prisijungti..."; /* Message for the network protection invite dialog */ -"network.protection.invite.dialog.message" = "Enter your invite code to get started."; +"network.protection.invite.dialog.message" = "Norėdami pradėti, įveskite kvietimo kodą."; /* Title for the network protection invite screen */ "network.protection.invite.dialog.title" = "Esate kviečiami išbandyti „DuckDuckGo“ VPN"; /* Prompt for the network protection invite code text field */ -"network.protection.invite.field.prompt" = "Invite Code"; +"network.protection.invite.field.prompt" = "Pakvietimo kodas"; /* Message for the network protection invite success view */ -"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; +"network.protection.invite.success.message" = "Paslėpk savo buvimo vietą nuo svetainių ir nuslėpk savo veiklą internete nuo interneto paslaugų teikėjų ir kitų tinklo vartotojų."; /* Title for the network protection invite success view */ -"network.protection.invite.success.title" = "Success! You’re in."; +"network.protection.invite.success.title" = "Pavyko! Tu jau čia."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "„DuckDuckGo“"; /* Title text for an iOS quick action that opens VPN settings */ "network.protection.quick-action.open-vpn" = "Atviras VPN"; +/* Button title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.action" = "Sužinoti daugiau"; + +/* Message for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.message" = "Galite naudoti svetaines ar programas, kurios blokuoja VPN srautą, sulėtindamos VPN ryšį."; + +/* Title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.title" = "Vengti VPN konfliktų"; + +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN pristabdytas %@ laikui"; + /* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ "network.protection.status.connected.format" = "Prisijungta · %@"; /* The label for the NetP VPN when connecting */ -"network.protection.status.connecting" = "Connecting..."; +"network.protection.status.connecting" = "Jungiamasi..."; /* The label for the NetP VPN when disconnected */ -"network.protection.status.disconnected" = "Not connected"; +"network.protection.status.disconnected" = "Neprijungta"; /* The label for the NetP VPN when disconnecting */ -"network.protection.status.disconnecting" = "Disconnecting..."; +"network.protection.status.disconnecting" = "Atsijungiama..."; /* Message label text for the status view when VPN is disconnected */ "network.protection.status.header.message.off" = "Prisijunkite, kad apsaugotumėte visų savo įrenginių\ninterneto srautą."; @@ -1655,26 +1730,41 @@ /* Header title label text for the status view when VPN is connected */ "network.protection.status.header.title.on" = "„DuckDuckGo“ VPN įjungtas"; +/* Header title label text for the status view when VPN is snoozing */ +"network.protection.status.header.title.snoozed" = "„DuckDuckGo“ VPN pristabdyta"; + /* The status view 'Share Feedback' button which is shown inline on the status view after the temporary free use footer text */ -"network.protection.status.menu.share.feedback" = "Share Feedback"; +"network.protection.status.menu.share.feedback" = "Bendrinti atsiliepimą"; + +/* The label for the NetP VPN when paused */ +"network.protection.status.paused" = "Pristabdyta"; + +/* The label for when NetP VPN is snoozing plus the length of time remaining formatted as '0:00' */ +"network.protection.status.snoozing.format" = "Pristabdyta, liko %@"; + +/* Snooze button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.snooze" = "Pristabdyta 20 min."; + +/* Wake Up button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.wake-up" = "Atšaukti pristabdymą"; /* Connection details label shown in NetworkProtection's status view. */ -"network.protection.status.view.connection.details" = "Connection Details"; +"network.protection.status.view.connection.details" = "Išsami ryšio informacija"; /* Custom DNS label shown in NetworkProtection's status view. */ -"network.protection.status.view.custom.dns" = "DNS Server"; +"network.protection.status.view.custom.dns" = "DNS serveris"; /* Generic connection failed error message shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.message" = "Please try again later."; +"network.protection.status.view.error.connection.failed.message" = "Bandykite dar kartą vėliau."; /* Generic connection failed error title shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; +"network.protection.status.view.error.connection.failed.title" = "Nepavyko prisijungti."; /* IP Address label shown in NetworkProtection's status view. */ -"network.protection.status.view.ip.address" = "IP Address"; +"network.protection.status.view.ip.address" = "IP adresas"; /* Location label shown in NetworkProtection's status view. */ -"network.protection.status.view.location" = "Location"; +"network.protection.status.view.location" = "Vieta"; /* Label shown on the title of the settings section in NetworkProtection's status view. */ "network.protection.status.view.settings.section.title" = "Tvarkyti"; @@ -1682,12 +1772,15 @@ /* Title label text for the status view when netP is disconnected */ "network.protection.status.view.title" = "VPN"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Tinklo apsauga įjungta. Jūsų vieta ir veikla internete yra apsaugota."; +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "„DuckDuckGo“ VPN įjungtas. Jūsų vieta ir veikla internete yra apsaugota."; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Nukreipiamas įrenginio srautas per %@."; +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "VPN pristabdymas baigėsi. Nukreipiamas įrenginio srautas per %@."; + /* Title for the button to link to the iOS app settings and enable notifications app-wide. */ "network.protection.turn.on.notifications.button.title" = "Įjungti pranešimus"; @@ -1698,13 +1791,13 @@ "network.protection.vpn.about" = "Apie"; /* Section header for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.section.header" = "Notifications"; +"network.protection.vpn.alerts.section.header" = "Pranešimai"; /* List section footer for the toggle for VPN alerts. */ "network.protection.vpn.alerts.toggle.section.footer" = "Gaukite pranešimą, jei nutrūksta ryšys arba pasikeičia VPN būsena."; /* Title for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.toggle.title" = "VPN Notifications"; +"network.protection.vpn.alerts.toggle.title" = "VPN pranešimai"; /* Title for the data volume section in the VPN status screen */ "network.protection.vpn.data-volume" = "Duomenų kiekis"; @@ -1713,13 +1806,13 @@ "network.protection.vpn.exclude.local.networks.setting.footer" = "Leiskite vietiniam srautui apeiti VPN ir prisijungti prie vietinio tinklo įrenginių, pavyzdžiui, spausdintuvo."; /* Header text for the Exclude Local Networks setting item. */ -"network.protection.vpn.exclude.local.networks.setting.header" = "General"; +"network.protection.vpn.exclude.local.networks.setting.header" = "Bendrai"; /* Title for the Exclude Local Networks setting item. */ "network.protection.vpn.exclude.local.networks.setting.title" = "Neįtraukti vietinių tinklų"; /* Title for the VPN Location screen's All Countries section. */ -"network.protection.vpn.location.all.countries.section.title" = "All Countries"; +"network.protection.vpn.location.all.countries.section.title" = "Visos šalys"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.connected" = "Prijungta vieta"; @@ -1734,7 +1827,7 @@ "network.protection.vpn.location.recommended.section.footer" = "Automatiškai prisijungti prie artimiausio serverio, kurį randame."; /* Title for the VPN Location screen's Recommended section. */ -"network.protection.vpn.location.recommended.section.title" = "Recommended"; +"network.protection.vpn.location.recommended.section.title" = "Rekomenduojama"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.selected" = "Pasirinkta vieta"; @@ -1752,13 +1845,13 @@ "network.protection.vpn.preferred.location.nearest" = "Artimiausia vieta"; /* Title for the Preferred Location VPN Settings item. */ -"network.protection.vpn.preferred.location.title" = "Preferred Location"; +"network.protection.vpn.preferred.location.title" = "Pageidaujama vieta"; /* Footer text for the DNS server setting item. */ -"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo routes DNS queries through our DNS servers so your internet provider can't see what websites you visit."; +"network.protection.vpn.secure.dns.setting.footer" = "„DuckDuckGo“ nukreipia DNS užklausas per mūsų DNS serverius, kad tavo interneto tiekėjas negalėtų matyti, kokiose svetainėse lankaisi."; /* Title for the FAQ row in the VPN status screen. */ -"network.protection.vpn.settings.faq" = "FAQs and Support"; +"network.protection.vpn.settings.faq" = "DUK ir pagalba"; /* Title for the feedback row in the VPN status screen. */ "network.protection.vpn.settings.share-feedback" = "Pateikti atsiliepimą apie VPN"; @@ -1940,6 +2033,66 @@ /* Deactivate button */ "pm.deactivate" = "Deaktyvinti"; +/* Label for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.label" = "El. paštas (neprivaloma)"; + +/* Placeholder for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.placeholder" = "name@email.com"; + +/* Placeholder for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.placeholder" = "Pateikite mums savo atsiliepimą..."; + +/* Title for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.title" = "Bendro pobūdžio atsiliepimai"; + +/* Title for the ITR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-itr-problem.title" = "Identity Theft Restoration"; + +/* Title for the PIR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-pir-problem.title" = "Personal Information Removal"; + +/* Title for the Subscriptions and Payments category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-ppro-problem.title" = "Prenumeratos ir mokėjimai"; + +/* Placeholder for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.placeholder" = "Pasakykite mums, kas vyksta..."; + +/* Title for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.title" = "Praneškite apie problemą"; + +/* Title for the VPN category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-vpn-problem.title" = "VPN"; + +/* Placeholder for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.placeholder" = "Kokią funkciją norėtumėte pamatyti?"; + +/* Title for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.title" = "Funkcionalumo prašymas"; + +/* Title for the category selection section in the Privacy Pro feedback form */ +"ppro.feedback-form.select-category.title" = "PASIRINKITE KATEGORIJĄ"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-1" = "Aptikai problemą, kuri neaptarta mūsų [pagalbos centre](duck://)? Mes tikrai norime apie tai sužinoti.Pateik el. paštą, jei norėtum, kad susisiektume su tavimi dėl šios problemos (galbūt negalėsime atsakyti į visas problemas):"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-2" = "Be pirmiau įvestos informacijos, kartu su tavo atsiliepimais siunčiame anoniminę informaciją:"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-3" = "• Ar kai kurios naršyklės funkcijos yra aktyvios"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-4" = "• Apibendrinta programėlės diagnostika (pvz., klaidų kodai)"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-5" = "Bakstelėdamas „Pateikti“ sutinku, kad „DuckDuckGo“ gali naudoti pateiktą informaciją programėlės funkcijų gerinimo tikslais."; + +/* Title for the text box in the Privacy Pro feedback form */ +"ppro.feedback-form.text-box.title" = "Atsiliepimai"; + +/* Title for the Privacy Pro feedback form */ +"ppro.feedback-form.title" = "Siųsti atsiliepimą"; + /* Button title for sync bookmarks limits exceeded warning to go to manage bookmarks */ "prefrences.sync.bookmarks-limit-exceeded-action" = "Tvarkyti žymes"; @@ -2029,6 +2182,18 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Mėgstami"; +/* Name of the option the user can chose to give general browser feedback */ +"send.browser.feedback.general-feedback" = "Bendro pobūdžio atsiliepimai"; + +/* Name of the option the user can chose to give browser feedback about a problem they enountered */ +"send.browser.feedback.report-problem" = "Praneškite apie problemą"; + +/* Name of the option the user can chose to give browser feedback about a feature they would like */ +"send.browser.feedback.request-feature" = "Prašykite funkcijos"; + +/* Title of the picker where the user can chose the category of the feedback they want ot send. */ +"send.browser.feedback.select-category" = "PASIRINKITE KATEGORIJĄ"; + /* Settings cell for About DDG */ "settings.about.ddg" = "Apie DuckDuckGo"; @@ -2084,6 +2249,9 @@ /* Section footer Autolock description */ "settings.autolock.description" = "Jei įjungtas „Touch ID“, „Face ID“ arba sistemos slaptažodis, prieš atidarydami programą būsite paprašyti ją atrakinti."; +/* Settings cell for Browser Feedback */ +"settings.browser.feedback" = "Naršyklės atsiliepimai"; + /* Settings screen cell text for Automatically Clearing Data */ "settings.clear.data" = "Automatiškai valyti duomenis"; @@ -2179,10 +2347,10 @@ "settings.on" = "Įjungti"; /* Product name for the subscription bundle */ -"settings.ppro" = "Privacy Pro"; +"settings.ppro" = "„Privacy Pro“"; /* Title for Link in the Footer of Privacy Pro section */ -"settings.ppro.footer" = "Privacy Policy and Terms of Service"; +"settings.ppro.footer" = "Privatumo politika ir  paslaugų teikimo sąlygos"; /* Settings screen cell for long press previews */ "settings.previews" = "Peržiūros ilgai spaudžiant"; @@ -2208,23 +2376,20 @@ /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Nustatykite adreso juostos padėtį"; -/* Subscription activation pending description */ -"settings.subscription.activation.pending.description" = "This is taking longer than usual, please check back later."; +/* Privacy pro description subtitle in settings when the is activating */ +"settings.subscription.activating" = "Aktyvavimas"; -/* Subscription activation pending title */ -"settings.subscription.activation.pending.title" = "Jūsų prenumerata aktyvuojama"; +/* Subscription activation pending description */ +"settings.subscription.activation.pending.description" = "Tai užtrunka ilgiau nei įprastai, prašome sugrįžti vėliau."; /* Data Broker protection cell subtitle for privacy pro */ -"settings.subscription.DBP.subtitle" = "Remove your info from sites that sell it"; +"settings.subscription.DBP.subtitle" = "Pašalink savo informaciją iš svetainių, kurios ją parduoda"; /* Data Broker protection cell title for privacy pro */ "settings.subscription.DBP.title" = "Personal Information Removal"; -/* Privacy pro description subtext */ -"settings.subscription.description" = "Sklandžiau užtikrinamas privatumas su trimis naujomis apsaugomis:"; - /* I have a Subscription button text for privacy pro */ -"settings.subscription.existing.subscription" = "I Have a Subscription"; +"settings.subscription.existing.subscription" = "Turiu prenumeratą"; /* Subscription expired description */ "settings.subscription.expired.comment" = "Vėl užsiprenumeruokite, kad galėtumėte toliau naudoti „Privacy Pro“"; @@ -2232,23 +2397,26 @@ /* Subscription expired tittle message */ "settings.subscription.expired.title" = "Jūsų „Privacy Pro“ prenumerata baigėsi"; -/* Privacy pro features list */ -"settings.subscription.features" = " • VPN\n • Personal Information Removal\n • Identity Theft Restoration"; - /* Identity theft restoration cell subtitle for privacy pro */ -"settings.subscription.ITR.subtitle" = "If your identity is stolen, we'll help restore it"; +"settings.subscription.ITR.subtitle" = "Jei tavo tapatybė buvo pavogta, padėsime ją atkurti"; /* Identity theft restoration cell title for privacy pro */ "settings.subscription.ITR.title" = "Identity Theft Restoration"; /* Get Privacy Pro button text for privacy pro */ -"settings.subscription.learn.more" = "Get Privacy Pro"; +"settings.subscription.learn.more" = "Gaukite „Privacy Pro“"; /* Subscription Settings button text for privacy pro */ -"settings.subscription.manage" = "Subscription Settings"; +"settings.subscription.manage" = "Prenumeratos nustatymai"; + +/* Privacy Pro description subtitle in settings */ +"settings.subscription.row.description" = "Apima mūsų VPN ir Identity Theft Restoration."; -/* Call to action title for Privacy Pro */ -"settings.subscription.subscribe" = "Subscribe to Privacy Pro"; +/* Call to action title for Privacy Pro settings */ +"settings.subscription.subscribe" = "Apsaugokite savo ryšį ir tapatybę su „Privacy Pro“."; + +/* Privacy pro description subtitle in settings */ +"settings.subscription.us.description" = "Apima mūsų VPN, Asmens duomenų pašalinimą ir Tapatybės vagystės atkūrimą."; /* VPN cell title for privacy pro */ "settings.subscription.VPN.title" = "VPN"; @@ -2317,67 +2485,64 @@ "siteFeedback.urlPlaceholder" = "Kuri svetainė neveikia?"; /* Subscription Activation Info */ -"subscription.activate..header.description" = "Access your Privacy Pro subscription on this device via Apple ID or an email address."; +"subscription.activate..header.description" = "Pasiek savo „Privacy Pro“ prenumeratą šiame įrenginyje per „Apple ID“ arba el. pašto adresą."; /* Button for adding email address to subscription */ -"subscription.activate.add.email.button" = "Add Email"; +"subscription.activate.add.email.button" = "Pridėti el. paštą"; /* Apple ID option for activation */ "subscription.activate.appleid" = "Apple ID"; /* Button text for restoring purchase via Apple ID */ -"subscription.activate.appleid.button" = "Restore Purchase"; +"subscription.activate.appleid.button" = "Atkurti pirkimą"; /* Description for Apple ID activation */ -"subscription.activate.appleid.description" = "Restore your purchase to activate your subscription on this device."; +"subscription.activate.appleid.description" = "Atkurk pirkimą, kad aktyvuotum prenumeratą šiame įrenginyje."; /* Subscription Activation Info */ -"subscription.activate.description" = "Your subscription is automatically available in DuckDuckGo on any device signed in to your Apple ID."; +"subscription.activate.description" = "Tavo prenumerata automatiškai pasiekiama „DuckDuckGo“ bet kuriame įrenginyje, prisijungusiame prie tavo „Apple ID“."; /* Button for editing email address added to subscription */ -"subscription.activate.edit.email.button" = "Edit Email"; +"subscription.activate.edit.email.button" = "Redaguoti el. pašto adresą"; /* View Title for editing your email account */ -"subscription.activate.edit.email.title" = "Edit Email"; +"subscription.activate.edit.email.title" = "Redaguoti el. pašto adresą"; /* Email option for activation */ -"subscription.activate.email" = "Email"; +"subscription.activate.email" = "El. paštas"; /* Restore button title for Email */ -"subscription.activate.email.button" = "Enter Email"; +"subscription.activate.email.button" = "Įveskite el. paštą"; /* Description for Email activation */ -"subscription.activate.email.description" = "Use your email to activate your subscription on this device."; +"subscription.activate.email.description" = "Naudokite savo el. pašto adresą, kad aktyvintumėte prenumeratą šiame įrenginyje."; /* Activate subscription title */ "subscription.activate.email.title" = "Aktyvuoti prenumeratą"; /* Button title for cancelling email deletion */ -"subscription.activate.manage.email.cancel" = "Cancel"; +"subscription.activate.manage.email.cancel" = "Atšaukti"; /* Button title for confirming email deletion */ -"subscription.activate.manage.email.OK" = "OK"; +"subscription.activate.manage.email.OK" = "GERAI"; /* Restore button title for AppleID */ -"subscription.activate.restore.apple" = "Restore Purchase"; +"subscription.activate.restore.apple" = "Atkurti pirkimą"; /* Subscription Activation Title */ -"subscription.activate.title" = "Activate your subscription on this device"; +"subscription.activate.title" = "Aktyvuokite prenumeratą šiame įrenginyje"; /* Resend activation instructions button */ -"subscription.add.device.resend.instructions" = "Resend Instructions"; +"subscription.add.device.resend.instructions" = "Pakartotinai siųsti instrukcijas"; /* Add email to an existing subscription */ -"subscription.add.email" = "Add an email address to activate your subscription on your other devices. We’ll only use this address to verify your subscription."; +"subscription.add.email" = "Pridėk el. pašto adresą, kad aktyvintum prenumeratą kituose įrenginiuose. Šį adresą naudosime tik tam, kad patikrintume tavo prenumeratą."; /* View title for adding email to subscription */ -"subscription.add.email.title" = "Add Email"; - -/* Title for Alert messages */ -"subscription.alert.title" = "subscription.alert.title"; +"subscription.add.email.title" = "Pridėti el. paštą"; /* Subscription availability message on Apple devices */ -"subscription.available.apple" = "Privacy Pro is available on any device signed in to the same Apple ID."; +"subscription.available.apple" = "„Privacy Pro“ galima naudoti bet kuriame įrenginyje, prisijungusiame prie to paties „Apple ID“."; /* Text for the manage billing page */ "subscription.billing.google.text" = "Jūsų prenumerata buvo įsigyta per „Google Play“ parduotuvę. Norėdami atnaujinti prenumeratą, atidarykite „Google Play“ parduotuvės prenumeratos nustatymus įrenginyje, prisijungusiame prie tos pačios „Google“ paskyros, kuri buvo naudojama iš pradžių prenumeratai įsigyti."; @@ -2385,119 +2550,116 @@ /* Title for the manage billing page */ "subscription.billing.google.title" = "Prenumeratos planai"; -/* Subscription annual billing period type */ -"subscription.billing.period.annual" = "annual"; - -/* Subscription monthly billing period type */ -"subscription.billing.period.monthly" = "monthly"; - /* Subscription Removal confirmation message */ -"subscription.cancel.message" = "Your subscription has been removed from this device."; +"subscription.cancel.message" = "Tavo prenumerata buvo pašalinta iš šio įrenginio."; /* Change plan or cancel title */ -"subscription.change.plan" = "Update Plan or Cancel"; +"subscription.change.plan" = "Atnaujinkite planą arba atšaukite"; /* Navigation Button for closing subscription view */ -"subscription.close" = "Close"; +"subscription.close" = "Uždaryti"; /* Title for Confirm messages */ -"subscription.confirm.title" = "Are you sure?"; +"subscription.confirm.title" = "Ar esi tikras?"; /* Header for section for activating subscription on other devices */ -"subscription.devices.header" = "Activate on Other Devices"; +"subscription.devices.header" = "Aktyvuoti kituose įrenginiuose"; /* Footer for section for activating subscription on other devices when email was not yet added */ -"subscription.devices.no.email.footer" = "Add an optional email to your subscription or use your Apple ID to access Privacy Pro on other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.no.email.footer" = "Pridėk alternatyvų el. pašto adresą prie savo prenumeratos, kad galėtum pasiekti „Privacy Pro“ kituose įrenginiuose. **[Sužinokite daugiau](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Footer for section for activating subscription on other devices when email is added */ -"subscription.devices.with.email.footer" = "Use this email to activate your subscription in Settings > Privacy Pro in the DuckDuckGo app on your other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.with.email.footer" = "Naudokite šį el. laišką, kad aktyvintumėte prenumeratą nustatymuose > „Privacy Pro“ „DuckDuckGo“ programėlėje kituose įrenginiuose. **[Sužinokite daugiau](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Alert content for not found subscription */ -"subscription.email.inactive.alert.message" = "The subscription associated with this email is no longer active."; +"subscription.email.inactive.alert.message" = "Su šiuo el. paštu susijusi prenumerata nebėra aktyvi."; /* Alert title for not found subscription */ -"subscription.email.inactive.alert.title" = "Subscription Not Found"; +"subscription.email.inactive.alert.title" = "Prenumeratos nerasta"; /* Alert content for not found subscription */ -"subscription.expired.alert.message" = "The subscription associated with this Apple ID is no longer active."; +"subscription.expired.alert.message" = "Su šiuo „Apple ID“ susijusi prenumerata nebėra aktyvi."; /* FAQ Button */ -"subscription.faq" = "FAQs and Support"; +"subscription.faq" = "DUK ir pagalba"; /* FAQ Description */ -"subscription.faq.description" = "Get answers to frequently asked questions or contact Privacy Pro support from our help pages."; +"subscription.faq.description" = "Gaukite atsakymus į dažniausiai užduodamus klausimus arba susisiekite su „Privacy Pro“ palaikymo tarnyba mūsų pagalbos puslapiuose."; + +/* Send Feedback Button */ +"subscription.feedback" = "Siųsti atsiliepimą"; /* Cancel action for the existing subscription dialog */ -"subscription.found.cancel" = "Cancel"; +"subscription.found.cancel" = "Atšaukti"; /* Restore action for the existing subscription dialog */ -"subscription.found.restore" = "Restore"; +"subscription.found.restore" = "Atkurti"; /* Message for the existing subscription dialog */ -"subscription.found.text" = "We found a subscription associated with this Apple ID."; +"subscription.found.text" = "Radome prenumeratą, susietą su šiuo „Apple ID“."; /* Title for the existing subscription dialog */ -"subscription.found.title" = "Subscription Found"; +"subscription.found.title" = "Rasta prenumerata"; /* Help and support Section header */ -"subscription.help" = "Help and support"; +"subscription.help" = "Pagalba ir palaikymas"; /* Manage Plan header */ -"subscription.manage.plan" = "Manage Plan"; +"subscription.manage.plan" = "Tvarkyti planą"; /* Header for the subscription section */ -"subscription.manage.title" = "Subscription"; +"subscription.manage.title" = "Prenumerata"; /* Alert content for not found subscription */ -"subscription.notFound.alert.message" = "There is no subscription associated with this Apple ID."; +"subscription.notFound.alert.message" = "Su šiuo „Apple ID“ nėra susijusios prenumeratos."; /* Alert title for not found subscription */ -"subscription.notFound.alert.title" = "Subscription Not Found"; +"subscription.notFound.alert.title" = "Prenumeratos nerasta"; /* View plans button text */ -"subscription.notFound.view.plans" = "View Plans"; +"subscription.notFound.view.plans" = "Peržiūrėti planus"; /* Hero Text for Personal information removal */ -"subscription.pir.hero" = "Activate Privacy Pro on desktop to set up Personal Information Removal"; +"subscription.pir.hero" = "Aktyvuokite „Privacy Pro“ darbalaukyje, kad nustatytumėte Asmeninės informacijos pašalinimą."; /* Description on how to use Personal information removal in desktop. The first placeholder references a location in the Desktop application. Privacy Pro>, and the second, the menu entry. i.e. */ -"subscription.pir.heroText" = "In the DuckDuckGo browser for desktop, go to %1$@ and click %2$@ to get started."; +"subscription.pir.heroText" = "„DuckDuckGo“ naršyklėje, skirtoje darbalaukiui, eikite į %1$@ ir spustelėkite %2$@, kad pradėtumėte."; /* Settings references a menu in the Desktop app, Privacy Pro, references our product name */ -"subscription.pir.heroTextLocation" = "Settings > Privacy Pro"; +"subscription.pir.heroTextLocation" = "Nustatymai > „Privacy Pro“"; /* Menu item for enabling Personal Information Removal on Desktop */ -"subscription.pir.heroTextMenyEntry" = "I have a subscription"; +"subscription.pir.heroTextMenyEntry" = "Turiu prenumeratą"; /* Text for the 'macOS' button */ "subscription.pir.macos" = "Mac"; /* Text for the 'Windows' button */ -"subscription.pir.windows" = "Windows"; +"subscription.pir.windows" = "„Windows“"; /* Progress view title when completing the purchase */ -"subscription.progress.view.completing.purchase" = "Completing purchase..."; +"subscription.progress.view.completing.purchase" = "Užbaigiama pirkimą..."; /* Progress view title when starting the purchase */ -"subscription.progress.view.purchasing.subscription" = "Purchase in progress..."; +"subscription.progress.view.purchasing.subscription" = "Pirkimas vyksta..."; /* Progress view title when restoring past subscription purchase */ -"subscription.progress.view.restoring.subscription" = "Restoring subscription..."; +"subscription.progress.view.restoring.subscription" = "Atkuriama prenumerata..."; /* Remove from this device button */ -"subscription.remove.from.device.button" = "Remove From This Device"; +"subscription.remove.from.device.button" = "Pašalinti iš šio įrenginio"; /* Remove from device confirmation dialog text */ -"subscription.remove.from.device.text" = "You will no longer be able to access your Privacy Pro subscription on this device. This will not cancel your subscription, and it will remain active on your other devices."; +"subscription.remove.from.device.text" = "Šiame prietaise nebegalėsite naudotis „Privacy Pro“ prenumerata. Tai neatšauks jūsų prenumeratos ir ji liks aktyvi kituose įrenginiuose."; /* Remove from device confirmation dialog title */ -"subscription.remove.from.device.title" = "Remove from this device?"; +"subscription.remove.from.device.title" = "Pašalinti iš šio įrenginio?"; /* Remove subscription button text */ -"subscription.remove.subscription" = "Remove Subscription"; +"subscription.remove.subscription" = "Pašalinti prenumeratą"; /* Remove subscription cancel button text */ -"subscription.remove.subscription.cancel" = "Cancel"; +"subscription.remove.subscription.cancel" = "Atšaukti"; /* Button text for general error message */ "subscription.restore.backend.error.button" = "Grįžti į nustatymus"; @@ -2509,31 +2671,46 @@ "subscription.restore.backend.error.title" = "Įvyko klaida"; /* Alert for general error message */ -"subscription.restore.general.error.message" = "The App Store was unable to process your purchase. Please try again later."; +"subscription.restore.general.error.message" = "„App Store“ nepavyko apdoroti jūsų pirkinio. Bandykite dar kartą vėliau."; /* Alert for general error title */ -"subscription.restore.general.error.title" = "Something Went Wrong"; +"subscription.restore.general.error.title" = "Įvyko klaida"; /* Alert button text for restored purchase alert */ -"subscription.restore.success.alert.button" = "OK"; +"subscription.restore.success.alert.button" = "GERAI"; /* Alert message for restored purchase */ -"subscription.restore.success.alert.message" = "Your purchases have been restored."; +"subscription.restore.success.alert.message" = "Jūsų pirkiniai atkurti."; /* Alert title for restored purchase */ -"subscription.restore.success.alert.title" = "You’re all set."; +"subscription.restore.success.alert.title" = "Tu pasiruošęs."; + +/* Subtitle in header when subscribed */ +"subscription.subscribed" = "Prenumeruota"; /* Subscription Expired Data. This reads as 'Your subscription expired on (date)' */ "subscription.subscription.expired.caption" = "Jūsų prenumerata baigėsi %@"; -/* Subscription expiration info. This reads as 'Your (monthly or annual) subscription expires on (date)' */ -"subscription.subscription.expiring.caption" = "Your %1$@ subscription expires on %2$@."; +/* Monthly subscription expiration info where parameter is expiration date. This reads as 'Your monthly subscription expires on (date)' */ +"subscription.subscription.expiring.monthly.caption" = "Tavo mėnesio prenumerata baigiasi %@."; + +/* Unknown period subscription expiration info where parameter is expiration date. This reads as 'Your subscription expires on (date)' */ +"subscription.subscription.expiring.unknown.caption" = "Tavo prenumerata baigiasi %@."; + +/* Annual subscription expiration info where parameter is expiration date. This reads as 'Your annual subscription expires on (date)' */ +"subscription.subscription.expiring.yearly.caption" = "Tavo metinė prenumerata baigiasi %@."; + +/* Monthly subscription renewal info where parameter is renewal date. This reads as 'Your monthly subscription renews on (date)' */ +"subscription.subscription.renewing.monthly.caption" = "Tavo mėnesinė prenumerata atnaujinama %@."; + +/* Unknown period subscription renewal info where parameter is renewal date. This reads as 'Your subscription renews on (date)' */ +"subscription.subscription.renewing.unknown.caption" = "Tavo prenumerata atsinaujina %@."; -/* Subscription renewal info. This reads as 'Your (monthly or annual) subscription renews on (date)' */ -"subscription.subscription.renewing.caption" = "Your %1$@ subscription renews on %2$@."; +/* Annual subscription renewal info where parameter is renewal date. This reads as 'Your annual subscription renews on (date)' */ +"subscription.subscription.renewing.yearly.caption" = "Tavo metinė prenumerata atnaujinama %@."; /* Navigation bar Title for subscriptions */ -"subscription.title" = "Privacy Pro"; +"subscription.title" = "„Privacy Pro“"; /* Message confirming that recovery code was copied to clipboard */ "sync.code.copied" = "Atkūrimo kodas nukopijuotas į mainų sritį"; @@ -2677,118 +2854,118 @@ "voiceSearch.footer.note.old" = "Garsas apdorojamas įrenginyje. Jis nesaugomas ir su niekuo, įskaitant „DuckDuckGo“, nebendrinamas."; /* Cancel action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.cancel" = "Dismiss"; +"vpn.access-revoked.alert.action.cancel" = "Atmesti"; /* Primary action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.subscribe" = "Subscribe"; +"vpn.access-revoked.alert.action.subscribe" = "Prenumeruoti"; /* Alert message for the alert when the Privacy Pro subscription expiress */ -"vpn.access-revoked.alert.message" = "Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"vpn.access-revoked.alert.message" = "„DuckDuckGo“ VPN galima naudoti nemokamai, kol veikia beta versija."; /* Alert title for the alert when the Privacy Pro subscription expires */ -"vpn.access-revoked.alert.title" = "VPN disconnected due to expired subscription"; +"vpn.access-revoked.alert.title" = "VPN atjungtas dėl pasibaigusios prenumeratos."; /* Title for the VPN widget onboarding screen */ "vpn.addWidget.settings.title" = "Raskite ir pasirinkite „DuckDuckGo“. Tada perbraukite į VPN ir pasirinkite „Pridėti valdiklį“."; /* Title for the Cancel button of the VPN feedback form */ -"vpn.feedback-form.button.cancel" = "Cancel"; +"vpn.feedback-form.button.cancel" = "Atšaukti"; /* Title for the Done button of the VPN feedback form */ -"vpn.feedback-form.button.done" = "Done"; +"vpn.feedback-form.button.done" = "Atlikta"; /* Title for the Submit button of the VPN feedback form */ -"vpn.feedback-form.button.submit" = "Submit"; +"vpn.feedback-form.button.submit" = "Pateikti"; /* Title for the Submitting state of the VPN feedback form */ -"vpn.feedback-form.button.submitting" = "Submitting…"; +"vpn.feedback-form.button.submitting" = "Pateikiama…"; /* Title for the browser crash/freeze category of the VPN feedback form */ -"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN causes browser to crash or freeze"; +"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN sukelia naršyklės strigimą arba nustoja veikti"; /* Title for the 'VPN fails to connect' category of the VPN feedback form */ -"vpn.feedback-form.category.fails-to-connect" = "VPN fails to connect"; +"vpn.feedback-form.category.fails-to-connect" = "VPN nepavyksta prisijungti"; /* Title for the 'VPN feature request' category of the VPN feedback form */ -"vpn.feedback-form.category.feature-request" = "VPN feature request"; +"vpn.feedback-form.category.feature-request" = "VPN funkcijos prašymas"; /* Title for the category 'VPN causes issues with other apps or websites' category of the VPN feedback form */ -"vpn.feedback-form.category.issues-with-apps" = "VPN causes issues with other apps or websites"; +"vpn.feedback-form.category.issues-with-apps" = "VPN sukelia problemų su kitomis programėlėmis ar svetainėmis"; /* Title for the local device connectivity category of the VPN feedback form */ -"vpn.feedback-form.category.local-device-connectivity" = "VPN won't let me connect to local device"; +"vpn.feedback-form.category.local-device-connectivity" = "VPN neleidžia prisijungti prie vietinio įrenginio"; /* Title for the 'other VPN feedback' category of the VPN feedback form */ -"vpn.feedback-form.category.other" = "Other VPN feedback"; +"vpn.feedback-form.category.other" = "Kiti atsiliepimai apie VPN"; /* Title for the category selection state of the VPN feedback form */ -"vpn.feedback-form.category.select-category" = "Select a category"; +"vpn.feedback-form.category.select-category" = "PASIRINKITE KATEGORIJĄ"; /* Title for the 'VPN is too slow' category of the VPN feedback form */ -"vpn.feedback-form.category.too-slow" = "VPN connection is too slow"; +"vpn.feedback-form.category.too-slow" = "VPN ryšys yra per lėtas"; /* Title for the 'unable to install' category of the VPN feedback form */ -"vpn.feedback-form.category.unable-to-install" = "Unable to install VPN"; +"vpn.feedback-form.category.unable-to-install" = "Nepavyksta įdiegti VPN"; /* Title for the feedback sent view description of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.description" = "Your feedback will help us improve the\nDuckDuckGo VPN."; +"vpn.feedback-form.sending-confirmation.description" = "Tavo atsiliepimai padės mums tobulinti\n „DuckDuckGo“ VPN."; /* Title for the feedback sending error text of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.error" = "We couldn't send your feedback right now, please try again."; +"vpn.feedback-form.sending-confirmation.error" = "Šiuo metu negalime išsiųsti tavo atsiliepimo, bandyk pabandyti dar kartą."; /* Title for the feedback sent view title of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.title" = "Thank you!"; +"vpn.feedback-form.sending-confirmation.title" = "Ačiū!"; /* Toast message when the VPN feedback form is submitted successfully */ -"vpn.feedback-form.submitted.message" = "Thank You! Feedback submitted."; +"vpn.feedback-form.submitted.message" = "Dėkojame! Atsiliepimas pateiktas."; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-1" = "Please describe what's happening, what you expected to happen, and the steps that led to the issue:"; +"vpn.feedback-form.text-1" = "Aprašyk, kas vyksta, ko tikėjotės, kad įvyks, ir veiksmus, dėl kurių kilo problema:"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-2" = "In addition to the details entered into this form, your app issue report will contain:"; +"vpn.feedback-form.text-2" = "Be šioje formoje įvestos informacijos, tavo programėlės problemų ataskaitoje bus pateikta:"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-3" = "• Whether specific DuckDuckGo features are enabled"; +"vpn.feedback-form.text-3" = "• Ar įjungtos tam tikros „DuckDuckGo“ funkcijos"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-4" = "• Aggregate DuckDuckGo app diagnostics"; +"vpn.feedback-form.text-4" = "• Apibendrinta „DuckDuckGo“ programėlės diagnostika"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-5" = "By tapping \"Submit\" I agree that DuckDuckGo may use the information in this report for purposes of improving the app's features."; +"vpn.feedback-form.text-5" = "Bakstelėdamas „Pateikti“ sutinku, kad „DuckDuckGo“ gali naudoti šioje ataskaitoje pateiktą informaciją programėlės funkcijų gerinimo tikslais."; /* Title for each screen of the VPN feedback form */ -"vpn.feedback-form.title" = "Help Improve the DuckDuckGo VPN"; +"vpn.feedback-form.title" = "Padėk tobulinti „DuckDuckGo“ VPN"; /* VPN settings screen cell text for adding the VPN widget to the home screen */ "vpn.settings.add.widget" = "Pridėti VPN valdiklį pagrindiniame ekrane"; /* Disclaimer for the DNS Server section on the DNS Server screen */ -"vpn.settings.dns.section-disclaimer" = "Using a custom DNS server can impact browsing speeds and expose your activity to third parties if the server isn't secure or reliable."; +"vpn.settings.dns.section-disclaimer" = "Pasirinktinio DNS serverio naudojimas gali turėti įtakos naršymo greičiui ir atskleisti jūsų veiklą trečiosioms šalims, jei serveris nėra saugus ar patikimas."; /* Header text for the DNS section on the VPN Settings screen */ "vpn.settings.dns.section-header" = "DNS"; /* Title for the Apply button on the DNS Server setting screen */ -"vpn.settings.dns.server.apply.button.title" = "Apply"; +"vpn.settings.dns.server.apply.button.title" = "Taikyti"; /* Default value for the DNS Server row on the VPN Settings screen */ -"vpn.settings.dns.server.default.value" = "DuckDuckGo"; +"vpn.settings.dns.server.default.value" = "„DuckDuckGo“"; /* Title for the IPv4 Address setting */ -"vpn.settings.dns.server.ipv4.title" = "IPv4 Address"; +"vpn.settings.dns.server.ipv4.title" = "IPv4 adresas"; /* Custom option for the DNS Server setting */ -"vpn.settings.dns.server.option.custom" = "Custom"; +"vpn.settings.dns.server.option.custom" = "Pasirinkti kitą"; /* Recommended option for the DNS Server setting */ -"vpn.settings.dns.server.option.default" = "DuckDuckGo (Recommended)"; +"vpn.settings.dns.server.option.default" = "„DuckDuckGo“ (rekomenduojama)"; /* Title for the DNS Server setting screen */ -"vpn.settings.dns.server.screen.title" = "DNS Server"; +"vpn.settings.dns.server.screen.title" = "DNS serveris"; /* Title for the DNS Server row on the VPN Settings screen */ -"vpn.settings.dns.server.title" = "DNS Server"; +"vpn.settings.dns.server.title" = "DNS serveris"; /* Title for the button to enable push notifications in system settings */ "waitlist.allow-notifications" = "Leisti pranešimus"; diff --git a/DuckDuckGo/lt.lproj/OmniBar.strings b/DuckDuckGo/lt.lproj/OmniBar.strings index 835cf69747..020f2564b1 100644 --- a/DuckDuckGo/lt.lproj/OmniBar.strings +++ b/DuckDuckGo/lt.lproj/OmniBar.strings @@ -7,6 +7,9 @@ /* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "Dt8-We-5tV"; */ "Dt8-We-5tV.accessibilityLabel" = "Valyti tekstą"; +/* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "gb5-wL-4OW"; */ +"gb5-wL-4OW.accessibilityLabel" = "Valyti tekstą"; + /* Class = "UIButton"; accessibilityLabel = "Share"; ObjectID = "hor-Jd-3kw"; */ "hor-Jd-3kw.accessibilityLabel" = "Bendrinti"; diff --git a/DuckDuckGo/lt.lproj/Settings.strings b/DuckDuckGo/lt.lproj/Settings.strings index 4a069ef60a..1b928b38a7 100644 --- a/DuckDuckGo/lt.lproj/Settings.strings +++ b/DuckDuckGo/lt.lproj/Settings.strings @@ -10,9 +10,6 @@ /* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "5YD-Od-uDU"; */ "5YD-Od-uDU.text" = "Automatiškai valyti duomenis"; -/* Class = "UIButton"; normalTitle = "Settings"; ObjectID = "8YG-7Q-Czd"; */ -"8YG-7Q-Czd.normalTitle" = "Nustatymai"; - /* Class = "UILabel"; text = "App Exit, Inactive for 1 Hour"; ObjectID = "9EC-lx-77p"; */ "9EC-lx-77p.text" = "Išeinant iš programos, po 1 val. neveikimo"; @@ -76,9 +73,6 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Neapsaugotos svetainės"; -/* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ -"ssa-zd-L3T.title" = "Teksto dydis"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Rodyti klaviatūrą"; diff --git a/DuckDuckGo/lv.lproj/DaxOnboarding.strings b/DuckDuckGo/lv.lproj/DaxOnboarding.strings index 9e4b527d61..2a1fef6091 100644 --- a/DuckDuckGo/lv.lproj/DaxOnboarding.strings +++ b/DuckDuckGo/lv.lproj/DaxOnboarding.strings @@ -1,12 +1,6 @@ /* Class = "UIButton"; normalTitle = "Hide"; ObjectID = "hgy-75-OvJ"; */ "hgy-75-OvJ.normalTitle" = "Paslēpt"; -/* Class = "UILabel"; text = "Welcome to\nDuckDuckGo!"; ObjectID = "Lvy-9G-3gr"; */ -"Lvy-9G-3gr.text" = "Laipni lūdzam\nDuckDuckGo!"; - -/* Class = "UIButton"; normalTitle = "Let’s Do It!"; ObjectID = "oAv-Zt-uIt"; */ -"oAv-Zt-uIt.normalTitle" = "Aiziet!"; - /* Class = "UIButton"; normalTitle = "Button"; ObjectID = "zJt-F9-L48"; */ "zJt-F9-L48.normalTitle" = "Poga"; diff --git a/DuckDuckGo/lv.lproj/Localizable.strings b/DuckDuckGo/lv.lproj/Localizable.strings index 8d5a6c5dfb..636cfb7053 100644 --- a/DuckDuckGo/lv.lproj/Localizable.strings +++ b/DuckDuckGo/lv.lproj/Localizable.strings @@ -341,7 +341,7 @@ "autofill.keep-enabled.alert.disable" = "Atslēgt"; /* A link that takes the user to the DuckDuckGo help pages explaining password managers */ -"autofill.learn.more.link.title" = "Learn More"; +"autofill.learn.more.link.title" = "Uzzināt vairāk"; /* Button displayed after saving/updating an autofill login that takes the user to the saved login */ "autofill.login-save-action-button.toast" = "Skatīt"; @@ -977,9 +977,6 @@ /* ad = advertisment */ "dax.onboarding.home.subsequent" = "Izdevās!\n\nAtceries: katru reizi, kad pārlūkosi kopā ar mani, kaitinošās reklāmas zaudēs savu spēku! 👍"; -/* No comment provided by engineer. */ -"dax.onboarding.message" = "Internets var būt diezgan “mežonīgs”.\n\nBet neuztraucies! Meklēt un pārlūkot privātā režīmā ir vieglāk, nekā tu domā."; - /* No comment provided by engineer. */ "Debug" = "Atkļūdošana"; @@ -1080,7 +1077,7 @@ "duckPlayer.never.label" = "Nekad"; /* Body text for the modal feature explanation */ -"duckplayer.presentation.modal.body" = "Duck Player ļauj skatīties YouTube bez mērķētām reklāmām, izmantojot DuckDuckGo režīmu, kas līdzinās kino skatīšanās pieredzei, un skatītais saturs neietekmē tev sniegtos ieteikumus."; +"duckplayer.presentation.modal.body" = "Duck Player ļauj skatīties YouTube bez mērķētām reklāmām DuckDuckGo vidē, un skatītais saturs neietekmē tev sniegtos ieteikumus."; /* Button that will dismiss the modal */ "duckplayer.presentation.modal.dismiss-button" = "Sapratu!"; @@ -1088,17 +1085,23 @@ /* Two line title (separated by \n) for the feature explanation */ "duckplayer.presentation.modal.title" = "Vai tevi nomāc YouTube reklāmas? Izmēģini Duck Player!"; +/* Two line title (separated by \n) for the feature explanation */ +"duckplayer.presentation.modal.title.youtube" = "Vai tevi nomāc YouTube reklāmas? Ne ar Duck Player!"; + /* Footer label in the settings screen for Duck Player */ "duckplayer.settings.footer" = "DuckDuckGo nodrošina visus privātuma aizsardzības līdzekļus, kas nepieciešami, lai aizsargātu sevi, pārlūkojot tīmekli."; /* Text explaining what Duck Player is in the settings screen. */ -"duckplayer.settings.info-text" = "Duck Player nodrošina netraucētu skatīšanās pieredzi bez personalizētām reklāmām un neļauj skatīšanās darbībām ietekmēt tavus YouTube ieteikumus."; +"duckplayer.settings.info-text" = "Duck Player ļauj skatīties YouTube bez mērķētām reklāmām DuckDuckGo vidē, un skatītais saturs neietekmē tev sniegtos ieteikumus."; /* Button that takes the user to learn more about Duck Player. */ "duckplayer.settings.learn-more" = "Uzzināt vairāk"; +/* Settings screen cell text for DuckPlayer settings to open in new tab */ +"duckplayer.settings.open-new-tab-label" = "Atveriet Duck Player jaunā cilnē"; + /* Settings screen cell text for DuckPlayer settings */ -"duckplayer.settings.open-videos-in" = "Atvērt video ar Duck Player"; +"duckplayer.settings.open-videos-in" = "Atvērt YouTube video ar Duck Player"; /* Settings screen cell text for DuckPlayer settings */ "duckplayer.settings.title" = "Duck Player"; @@ -1169,9 +1172,6 @@ /* Title for prompt to sign up for email protection */ "email.signup-prompt.title" = "Paslēp savu e-pastu un\nbloķē izsekotājus"; -/* No comment provided by engineer. */ -"empty!" = "tukšs!"; - /* Empty list state placholder */ "empty.bookmarks" = "Vēl nav pievienota neviena grāmatzīme"; @@ -1322,6 +1322,39 @@ /* Confirmation button */ "feedback.form.submit" = "Iesniegt"; +/* Title of the feedback form */ +"feedback.form.title" = "Palīdzi uzlabot Privacy Pro"; + +/* Category for Identity Theft Restoration feedback */ +"feedback.general.category.itr" = "Identity Theft Restoration"; + +/* Category for Personal Info Removal feedback */ +"feedback.general.category.pir" = "Personīgās informācijas noņemšana"; + +/* Category for subscription and payments feedback */ +"feedback.general.category.ppro" = "Abonements un maksājumi"; + +/* Prompt to select a category for general feedback */ +"feedback.general.category.select" = "ATLASĪT KATEGORIJU"; + +/* Category for VPN feedback */ +"feedback.general.category.vpn" = "VPN"; + +/* Category for access code issues */ +"feedback.itr.category.access-code" = "Problēma ar piekļuves kodu"; + +/* Category for issues contacting an advisor */ +"feedback.itr.category.cant-contact-advisor" = "Nevar sazināties ar konsultantu"; + +/* Prompt to select a category for Identity Theft Restoration feedback */ +"feedback.itr.category.select" = "ATLASĪT KATEGORIJU"; + +/* Category for other Identity Theft Restoration issues */ +"feedback.itr.category.something-else" = "Kaut kas cits"; + +/* Category for unhelpful advisor calls */ +"feedback.itr.category.unhelpful" = "Zvans konsultantam nepalīdzēja"; + /* No comment provided by engineer. */ "feedback.negative.form.genericPlaceholder" = "Lūdzu, norādi pēc iespējas precīzāk"; @@ -1361,6 +1394,24 @@ /* No comment provided by engineer. */ "feedback.performance.slowLoading" = "Tīmekļa lapas vai meklēšanas rezultāti tiek lēni ielādēti"; +/* Category for when scan finds incorrect records */ +"feedback.pir.category.not-me" = "Skenēšanas laikā tika atrasti ieraksti, kas nav mani"; + +/* Category for when scan doesn't find info on a specific site */ +"feedback.pir.category.nothing-on-site" = "Skenēšana neatrada manu informāciju konkrētā vietnē"; + +/* Category for other Personal Info Removal issues */ +"feedback.pir.category.other" = "Kaut kas cits"; + +/* Category for when the removal process is stuck */ +"feedback.pir.category.removal-stuck" = "Noņemšanas process ir iestrēdzis"; + +/* Category for when the scan is stuck */ +"feedback.pir.category.scan-stuck" = "Ierakstu skenēšana ir iestrēgusi"; + +/* Prompt to select a category for Personal Info Removal feedback */ +"feedback.pir.category.select" = "ATLASĪT KATEGORIJU"; + /* Header above input field */ "feedback.positive.form.header" = "Informācijas kopīgošana"; @@ -1379,6 +1430,15 @@ /* Button encouraging uses to share details aboout their feedback */ "feedback.positive.submit" = "Kopīgot informāciju"; +/* Category for other Privacy Pro issues */ +"feedback.ppro.category.other" = "Kaut kas cits"; + +/* Category for one-time password issues */ +"feedback.ppro.category.otp" = "Problēma ar vienreizējo paroli"; + +/* Prompt to select a category for Privacy Pro feedback */ +"feedback.ppro.category.select" = "ATLASĪT KATEGORIJU"; + /* No comment provided by engineer. */ "feedback.start.footer" = "Tavas anonīmās atsauksmes mums ir svarīgas."; @@ -1529,12 +1589,6 @@ /* No comment provided by engineer. */ "keyCommandShowAllTabs" = "Rādīt visas cilnes"; -/* Please preserve newline character */ -"launchscreenWelcomeMessage" = "Laipni lūdzam\nDuckDuckGo!"; - -/* No comment provided by engineer. */ -"LOREM IPSUM" = "LOREM IPSUM"; - /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "DuckDuckGo Mac datoram sniedz nepieciešamo ātrumu, ierastās pārlūkošanas funkcijas un savā klasē labākās privātuma pamatfunkcijas."; @@ -1586,62 +1640,83 @@ /* Edit button */ "navigation.title.edit" = "Rediģēt"; -/* String indicating NetP is connected when viewed from the settings screen */ -"netP.cell.connected" = "Connected"; - -/* String indicating NetP is disconnected when viewed from the settings screen */ -"netP.cell.disconnected" = "Not connected"; - -/* Title for the DuckDuckGo VPN feature in settings */ -"netP.settings.title" = "VPN"; +/* String indicating NetP is snoozing when viewed from the settings screen */ +"netP.cell.snoozing" = "Iemidzināts"; /* Title for the DuckDuckGo VPN feature */ "netP.title" = "DuckDuckGo VPN"; +/* Button title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.action" = "Pievienot logrīku"; + +/* Message for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.message" = "Ieslēdz un izslēdz VPN tieši no sākuma ekrāna."; + +/* Title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.title" = "Pievienot VPN logrīku"; + /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN atvienots, jo ir beidzies abonements. Abonē Privacy Pro, lai atkārtoti izveidotu savienojumu ar DuckDuckGo VPN."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Tīkla aizsardzībai neizdevās izveidot savienojumu. Lūdzu, mēģini vēlreiz vēlāk."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "Neizdevās izveidot savienojumu ar DuckDuckGo VPN. Lūdzu, mēģini vēlreiz vēlāk."; /* Title for the VPN FAQ screen. */ "network.protection.faq.title" = "Bieži uzdotie jautājumi par DuckDuckGo VPN "; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Tīkla aizsardzība tika pārtraukta. Tagad tiek mēģināts atkārtoti izveidot savienojumu..."; +/* Message for tooltip about geoswitching */ +"network.protection.geoswitching.tip.message" = "Tu vari pielāgot savu VPN atrašanās vietu, izveidojot savienojumu ar jebkuru no mūsu serveriem visā pasaulē."; + +/* Title for tooltip about geoswitching */ +"network.protection.geoswitching.tip.title" = "Mainīt atrašanās vietu"; + +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "DuckDuckGo VPN savienojums ir pārtraukts. Mēģina atjaunot savienojumu..."; /* Message for the network protection invite dialog */ -"network.protection.invite.dialog.message" = "Enter your invite code to get started."; +"network.protection.invite.dialog.message" = "Ievadi savu ielūguma kodu, lai sāktu."; /* Title for the network protection invite screen */ "network.protection.invite.dialog.title" = "Tu esi uzaicināts izmēģināt DuckDuckGo VPN"; /* Prompt for the network protection invite code text field */ -"network.protection.invite.field.prompt" = "Invite Code"; +"network.protection.invite.field.prompt" = "Uzaicinājuma kods"; /* Message for the network protection invite success view */ -"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; +"network.protection.invite.success.message" = "Paslēp savu atrašanās vietu no vietnēm un savas tiešsaistes darbības no interneta pakalpojumu sniedzējiem un citiem tavā tīklā."; /* Title for the network protection invite success view */ -"network.protection.invite.success.title" = "Success! You’re in."; +"network.protection.invite.success.title" = "Izdevās! Tu esi pievienots."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; /* Title text for an iOS quick action that opens VPN settings */ "network.protection.quick-action.open-vpn" = "Atvērt VPN"; +/* Button title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.action" = "Uzzināt vairāk"; + +/* Message for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.message" = "Vari izmantot vietnes vai lietotnes, kas bloķē VPN trafiku, uz laiku pārtraucot VPN savienojumu."; + +/* Title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.title" = "Izvairīties no VPN konfliktiem"; + +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN ir atslēgts uz %@"; + /* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ "network.protection.status.connected.format" = "Savienots · %@"; /* The label for the NetP VPN when connecting */ -"network.protection.status.connecting" = "Connecting..."; +"network.protection.status.connecting" = "Savieno..."; /* The label for the NetP VPN when disconnected */ -"network.protection.status.disconnected" = "Not connected"; +"network.protection.status.disconnected" = "Nav savienots"; /* The label for the NetP VPN when disconnecting */ -"network.protection.status.disconnecting" = "Disconnecting..."; +"network.protection.status.disconnecting" = "Atvieno..."; /* Message label text for the status view when VPN is disconnected */ "network.protection.status.header.message.off" = "Izveido savienojumu, lai aizsargātu visu savu ierīču\nInterneta trafiku."; @@ -1655,26 +1730,41 @@ /* Header title label text for the status view when VPN is connected */ "network.protection.status.header.title.on" = "DuckDuckGo VPN ir ieslēgts"; +/* Header title label text for the status view when VPN is snoozing */ +"network.protection.status.header.title.snoozed" = "DuckDuckGo VPN ir atlikts"; + /* The status view 'Share Feedback' button which is shown inline on the status view after the temporary free use footer text */ -"network.protection.status.menu.share.feedback" = "Share Feedback"; +"network.protection.status.menu.share.feedback" = "Kopīgot atsauksmi"; + +/* The label for the NetP VPN when paused */ +"network.protection.status.paused" = "Pārtraukts"; + +/* The label for when NetP VPN is snoozing plus the length of time remaining formatted as '0:00' */ +"network.protection.status.snoozing.format" = "Atlikt, %@ paliek"; + +/* Snooze button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.snooze" = "Iemidzināt uz 20 minūtēm"; + +/* Wake Up button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.wake-up" = "Pamodināt"; /* Connection details label shown in NetworkProtection's status view. */ -"network.protection.status.view.connection.details" = "Connection Details"; +"network.protection.status.view.connection.details" = "Savienojuma informācija"; /* Custom DNS label shown in NetworkProtection's status view. */ -"network.protection.status.view.custom.dns" = "DNS Server"; +"network.protection.status.view.custom.dns" = "DNS serveris"; /* Generic connection failed error message shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.message" = "Please try again later."; +"network.protection.status.view.error.connection.failed.message" = "Lūdzu, mēģini vēlreiz vēlāk."; /* Generic connection failed error title shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; +"network.protection.status.view.error.connection.failed.title" = "Neizdevās izveidot savienojumu."; /* IP Address label shown in NetworkProtection's status view. */ -"network.protection.status.view.ip.address" = "IP Address"; +"network.protection.status.view.ip.address" = "IP adrese"; /* Location label shown in NetworkProtection's status view. */ -"network.protection.status.view.location" = "Location"; +"network.protection.status.view.location" = "Atrašanās vieta"; /* Label shown on the title of the settings section in NetworkProtection's status view. */ "network.protection.status.view.settings.section.title" = "Pārvaldīt"; @@ -1682,12 +1772,15 @@ /* Title label text for the status view when netP is disconnected */ "network.protection.status.view.title" = "VPN"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Tīkla aizsardzība ir ieslēgta. Tava atrašanās vieta un darbības tiešsaistē ir aizsargātas."; +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "DuckDuckGo VPN ir ieslēgts. Tava atrašanās vieta un darbības tiešsaistē ir aizsargātas."; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Ierīces datplūsma tiek maršrutēta caur: %@."; +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "VPN snauda ir beigusies. Ierīces datplūsma tiek maršrutēta caur: %@."; + /* Title for the button to link to the iOS app settings and enable notifications app-wide. */ "network.protection.turn.on.notifications.button.title" = "Ieslēgt paziņojumus"; @@ -1698,13 +1791,13 @@ "network.protection.vpn.about" = "Par"; /* Section header for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.section.header" = "Notifications"; +"network.protection.vpn.alerts.section.header" = "Paziņojumi"; /* List section footer for the toggle for VPN alerts. */ "network.protection.vpn.alerts.toggle.section.footer" = "Saņem paziņojumu, ja savienojums pārtrūkst vai mainās VPN statuss."; /* Title for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.toggle.title" = "VPN Notifications"; +"network.protection.vpn.alerts.toggle.title" = "VPN paziņojumi"; /* Title for the data volume section in the VPN status screen */ "network.protection.vpn.data-volume" = "Datu apjoms"; @@ -1713,13 +1806,13 @@ "network.protection.vpn.exclude.local.networks.setting.footer" = "Ļauj vietējai datplūsmai apiet VPN un izveidot savienojumu ar ierīcēm tavā vietējā tīklā, piemēram, printeri."; /* Header text for the Exclude Local Networks setting item. */ -"network.protection.vpn.exclude.local.networks.setting.header" = "General"; +"network.protection.vpn.exclude.local.networks.setting.header" = "Vispārīgi"; /* Title for the Exclude Local Networks setting item. */ "network.protection.vpn.exclude.local.networks.setting.title" = "Neietvert vietējos tīklus"; /* Title for the VPN Location screen's All Countries section. */ -"network.protection.vpn.location.all.countries.section.title" = "All Countries"; +"network.protection.vpn.location.all.countries.section.title" = "Visas valstis"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.connected" = "Savienojuma atrašanās vieta"; @@ -1734,7 +1827,7 @@ "network.protection.vpn.location.recommended.section.footer" = "Automātiski izveido savienojumu ar tuvāko serveri, ko varam atrast."; /* Title for the VPN Location screen's Recommended section. */ -"network.protection.vpn.location.recommended.section.title" = "Recommended"; +"network.protection.vpn.location.recommended.section.title" = "Ieteicams"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.selected" = "Izvēlētā atrašanās vieta"; @@ -1752,13 +1845,13 @@ "network.protection.vpn.preferred.location.nearest" = "Tuvākā atrašanās vieta"; /* Title for the Preferred Location VPN Settings item. */ -"network.protection.vpn.preferred.location.title" = "Preferred Location"; +"network.protection.vpn.preferred.location.title" = "Vēlamā atrašanās vieta"; /* Footer text for the DNS server setting item. */ -"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo routes DNS queries through our DNS servers so your internet provider can't see what websites you visit."; +"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo maršrutē DNS vaicājumus caur mūsu DNS serveriem, lai tavs interneta pakalpojumu sniedzējs neredzētu, kādas vietnes tu apmeklē."; /* Title for the FAQ row in the VPN status screen. */ -"network.protection.vpn.settings.faq" = "FAQs and Support"; +"network.protection.vpn.settings.faq" = "Bieži uzdotie jautājumi un atbalsts"; /* Title for the feedback row in the VPN status screen. */ "network.protection.vpn.settings.share-feedback" = "Sniedziet atsauksmi par VPN"; @@ -1940,6 +2033,66 @@ /* Deactivate button */ "pm.deactivate" = "Deaktivizēt"; +/* Label for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.label" = "E-pasta adrese (neobligāti)"; + +/* Placeholder for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.placeholder" = "vards@email.com"; + +/* Placeholder for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.placeholder" = "Lūdzu, sniedz atsauksmes…"; + +/* Title for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.title" = "Vispārīgas atsauksmes"; + +/* Title for the ITR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-itr-problem.title" = "Identity Theft Restoration"; + +/* Title for the PIR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-pir-problem.title" = "Personal Information Removal"; + +/* Title for the Subscriptions and Payments category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-ppro-problem.title" = "Abonementi un maksājumi"; + +/* Placeholder for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.placeholder" = "Pastāsti, kas notiek…"; + +/* Title for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.title" = "Ziņot par problēmu"; + +/* Title for the VPN category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-vpn-problem.title" = "VPN"; + +/* Placeholder for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.placeholder" = "Kādu funkciju tu vēlētos redzēt?"; + +/* Title for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.title" = "Funkcionalitātes pieprasījums"; + +/* Title for the category selection section in the Privacy Pro feedback form */ +"ppro.feedback-form.select-category.title" = "ATLASĪT KATEGORIJU"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-1" = "Vai atradi problēmu, kas nav aplūkota mūsu [palīdzības centrā](duck://)? Mēs noteikti gribam par to uzzināt.Norādi savu e-pasta adresi, ja vēlies, lai mēs ar tevi sazināmies par šo problēmu (iespējams, nevarēsim reaģēt uz visām problēmām):"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-2" = "Papildus iepriekš ievadītajai informācijai mēs kopā ar tavām atsauksmēm nosūtām anonimizētu informāciju:"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-3" = "• vai dažas pārlūkprogrammas funkcijas ir aktīvas"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-4" = "• apkopotus lietotņu diagnostikas datus (piemēram, kļūdu kodus)"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-5" = "Pieskaroties pie \"Iesniegt\", tu piekrīti, ka DuckDuckGo var izmantot iesniegto informāciju, lai uzlabotu lietotni."; + +/* Title for the text box in the Privacy Pro feedback form */ +"ppro.feedback-form.text-box.title" = "Atsauksmes"; + +/* Title for the Privacy Pro feedback form */ +"ppro.feedback-form.title" = "Sūtīt atsauksmi"; + /* Button title for sync bookmarks limits exceeded warning to go to manage bookmarks */ "prefrences.sync.bookmarks-limit-exceeded-action" = "Pārvaldīt grāmatzīmes"; @@ -2029,6 +2182,18 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Izlase"; +/* Name of the option the user can chose to give general browser feedback */ +"send.browser.feedback.general-feedback" = "Vispārīgas atsauksmes"; + +/* Name of the option the user can chose to give browser feedback about a problem they enountered */ +"send.browser.feedback.report-problem" = "Ziņot par problēmu"; + +/* Name of the option the user can chose to give browser feedback about a feature they would like */ +"send.browser.feedback.request-feature" = "Pieprasīt funkciju"; + +/* Title of the picker where the user can chose the category of the feedback they want ot send. */ +"send.browser.feedback.select-category" = "ATLASĪT KATEGORIJU"; + /* Settings cell for About DDG */ "settings.about.ddg" = "Par DuckDuckGo"; @@ -2084,6 +2249,9 @@ /* Section footer Autolock description */ "settings.autolock.description" = "Ja ir iespējots Touch ID, Face ID vai sistēmas piekļuves kods, atverot lietotni, tev tā būs jāatbloķē."; +/* Settings cell for Browser Feedback */ +"settings.browser.feedback" = "Atsauksmes par pārlūkprogrammu"; + /* Settings screen cell text for Automatically Clearing Data */ "settings.clear.data" = "Automātiski notīrīt datus"; @@ -2179,10 +2347,10 @@ "settings.on" = "Ieslēgts"; /* Product name for the subscription bundle */ -"settings.ppro" = "Privacy Pro"; +"settings.ppro" = "Privacy Pro"; /* Title for Link in the Footer of Privacy Pro section */ -"settings.ppro.footer" = "Privacy Policy and Terms of Service"; +"settings.ppro.footer" = "Privātuma politika un Pakalpojumu sniegšanas noteikumi"; /* Settings screen cell for long press previews */ "settings.previews" = "Ilgas nospiešanas rezultātu priekšskatījumi"; @@ -2208,23 +2376,20 @@ /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Iestati adreses joslas pozīciju"; -/* Subscription activation pending description */ -"settings.subscription.activation.pending.description" = "This is taking longer than usual, please check back later."; +/* Privacy pro description subtitle in settings when the is activating */ +"settings.subscription.activating" = "Notiek aktivizēšana"; -/* Subscription activation pending title */ -"settings.subscription.activation.pending.title" = "Tavs abonements tiek aktivizēts"; +/* Subscription activation pending description */ +"settings.subscription.activation.pending.description" = "Tas aizņem ilgāku laiku nekā parasti. Lūdzu, atgriezies vēlāk."; /* Data Broker protection cell subtitle for privacy pro */ -"settings.subscription.DBP.subtitle" = "Remove your info from sites that sell it"; +"settings.subscription.DBP.subtitle" = "Noņem savu informāciju no vietnēm, kas to pārdod"; /* Data Broker protection cell title for privacy pro */ "settings.subscription.DBP.title" = "Personal Information Removal"; -/* Privacy pro description subtext */ -"settings.subscription.description" = "Vēl pilnīgāks privātums ar trīs jauniem aizsardzības līdzekļiem:"; - /* I have a Subscription button text for privacy pro */ -"settings.subscription.existing.subscription" = "I Have a Subscription"; +"settings.subscription.existing.subscription" = "Man ir abonements"; /* Subscription expired description */ "settings.subscription.expired.comment" = "Abonē no jauna, lai turpinātu izmantot Privacy Pro"; @@ -2232,23 +2397,26 @@ /* Subscription expired tittle message */ "settings.subscription.expired.title" = "Tavs Privacy Pro abonements ir beidzies"; -/* Privacy pro features list */ -"settings.subscription.features" = " • VPN\n • Personal Information Removal\n • Identity Theft Restoration"; - /* Identity theft restoration cell subtitle for privacy pro */ -"settings.subscription.ITR.subtitle" = "If your identity is stolen, we'll help restore it"; +"settings.subscription.ITR.subtitle" = "Ja tava identitāte tiks nozagta, mēs palīdzēsim to atjaunot"; /* Identity theft restoration cell title for privacy pro */ "settings.subscription.ITR.title" = "Identity Theft Restoration"; /* Get Privacy Pro button text for privacy pro */ -"settings.subscription.learn.more" = "Get Privacy Pro"; +"settings.subscription.learn.more" = "Iegūsti Privacy Pro"; /* Subscription Settings button text for privacy pro */ -"settings.subscription.manage" = "Subscription Settings"; +"settings.subscription.manage" = "Abonementa iestatījumi"; + +/* Privacy Pro description subtitle in settings */ +"settings.subscription.row.description" = "Ietver mūsu VPN un Identity Theft Restoration."; -/* Call to action title for Privacy Pro */ -"settings.subscription.subscribe" = "Subscribe to Privacy Pro"; +/* Call to action title for Privacy Pro settings */ +"settings.subscription.subscribe" = "Aizsargā savu savienojumu un identitāti ar Privacy Pro"; + +/* Privacy pro description subtitle in settings */ +"settings.subscription.us.description" = "Ietver mūsu VPN, Personal Information Removal un Identity Theft Restoration."; /* VPN cell title for privacy pro */ "settings.subscription.VPN.title" = "VPN"; @@ -2317,67 +2485,64 @@ "siteFeedback.urlPlaceholder" = "Kura vietne ir bojāta?"; /* Subscription Activation Info */ -"subscription.activate..header.description" = "Access your Privacy Pro subscription on this device via Apple ID or an email address."; +"subscription.activate..header.description" = "Piekļūsti savam Privacy Pro abonementam šajā ierīcē, izmantojot Apple ID vai e-pasta adresi."; /* Button for adding email address to subscription */ -"subscription.activate.add.email.button" = "Add Email"; +"subscription.activate.add.email.button" = "Pievienot e-pasta adresi"; /* Apple ID option for activation */ "subscription.activate.appleid" = "Apple ID"; /* Button text for restoring purchase via Apple ID */ -"subscription.activate.appleid.button" = "Restore Purchase"; +"subscription.activate.appleid.button" = "Atjaunot pirkumu"; /* Description for Apple ID activation */ -"subscription.activate.appleid.description" = "Restore your purchase to activate your subscription on this device."; +"subscription.activate.appleid.description" = "Atjauno savu pirkumu, lai aktivizētu abonementu šajā ierīcē."; /* Subscription Activation Info */ -"subscription.activate.description" = "Your subscription is automatically available in DuckDuckGo on any device signed in to your Apple ID."; +"subscription.activate.description" = "Tavs abonements ir automātiski pieejams DuckDuckGo jebkurā ierīcē, kurā esi pierakstījies, izmantojot savu Apple ID."; /* Button for editing email address added to subscription */ -"subscription.activate.edit.email.button" = "Edit Email"; +"subscription.activate.edit.email.button" = "Rediģēt e-pasta adresi"; /* View Title for editing your email account */ -"subscription.activate.edit.email.title" = "Edit Email"; +"subscription.activate.edit.email.title" = "Rediģēt e-pasta adresi"; /* Email option for activation */ -"subscription.activate.email" = "Email"; +"subscription.activate.email" = "E-pasts"; /* Restore button title for Email */ -"subscription.activate.email.button" = "Enter Email"; +"subscription.activate.email.button" = "Ievadi e-pasta adresi"; /* Description for Email activation */ -"subscription.activate.email.description" = "Use your email to activate your subscription on this device."; +"subscription.activate.email.description" = "Izmanto savu e-pasta adresi, lai aktivizētu abonementu šajā ierīcē."; /* Activate subscription title */ "subscription.activate.email.title" = "Aktivizēt abonementu"; /* Button title for cancelling email deletion */ -"subscription.activate.manage.email.cancel" = "Cancel"; +"subscription.activate.manage.email.cancel" = "Atcelt"; /* Button title for confirming email deletion */ -"subscription.activate.manage.email.OK" = "OK"; +"subscription.activate.manage.email.OK" = "Labi"; /* Restore button title for AppleID */ -"subscription.activate.restore.apple" = "Restore Purchase"; +"subscription.activate.restore.apple" = "Atjaunot pirkumu"; /* Subscription Activation Title */ -"subscription.activate.title" = "Activate your subscription on this device"; +"subscription.activate.title" = "Aktivizē savu abonementu šajā ierīcē"; /* Resend activation instructions button */ -"subscription.add.device.resend.instructions" = "Resend Instructions"; +"subscription.add.device.resend.instructions" = "Atkārtoti nosūtīt instrukcijas"; /* Add email to an existing subscription */ -"subscription.add.email" = "Add an email address to activate your subscription on your other devices. We’ll only use this address to verify your subscription."; +"subscription.add.email" = "Pievieno e-pasta adresi, lai aktivizētu abonementu citās savās ierīcēs. Šo adresi izmantosim tikai ar mērķi pārbaudīt tavu abonementu."; /* View title for adding email to subscription */ -"subscription.add.email.title" = "Add Email"; - -/* Title for Alert messages */ -"subscription.alert.title" = "subscription.alert.title"; +"subscription.add.email.title" = "Pievienot e-pasta adresi"; /* Subscription availability message on Apple devices */ -"subscription.available.apple" = "Privacy Pro is available on any device signed in to the same Apple ID."; +"subscription.available.apple" = "Privacy Pro ir pieejams jebkurā ierīcē, kurā esi pierakstījies, izmantojot to pašu Apple ID."; /* Text for the manage billing page */ "subscription.billing.google.text" = "Tavs abonements tika iegādāts Google Play veikalā. Lai atjaunotu abonementu, lūdzu, atver Google Play veikala abonēšanas iestatījumus ierīcē, kas ir pierakstījusies tajā pašā Google kontā, kurā sākotnēji iegādājies abonementu."; @@ -2385,89 +2550,86 @@ /* Title for the manage billing page */ "subscription.billing.google.title" = "Abonēšanas plāni"; -/* Subscription annual billing period type */ -"subscription.billing.period.annual" = "annual"; - -/* Subscription monthly billing period type */ -"subscription.billing.period.monthly" = "monthly"; - /* Subscription Removal confirmation message */ -"subscription.cancel.message" = "Your subscription has been removed from this device."; +"subscription.cancel.message" = "Tavs abonements ir noņemts no šīs ierīces."; /* Change plan or cancel title */ -"subscription.change.plan" = "Update Plan or Cancel"; +"subscription.change.plan" = "Atjaunināt plānu vai atcelt"; /* Navigation Button for closing subscription view */ -"subscription.close" = "Close"; +"subscription.close" = "Aizvērt"; /* Title for Confirm messages */ -"subscription.confirm.title" = "Are you sure?"; +"subscription.confirm.title" = "Vai esi pārliecināts?"; /* Header for section for activating subscription on other devices */ -"subscription.devices.header" = "Activate on Other Devices"; +"subscription.devices.header" = "Aktivizēt citās ierīcēs"; /* Footer for section for activating subscription on other devices when email was not yet added */ -"subscription.devices.no.email.footer" = "Add an optional email to your subscription or use your Apple ID to access Privacy Pro on other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.no.email.footer" = "Pēc izvēles pievieno abonementam e-pasta adresi, lai piekļūtu Privacy Pro citās ierīcēs. **[Uzzināt vairāk](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Footer for section for activating subscription on other devices when email is added */ -"subscription.devices.with.email.footer" = "Use this email to activate your subscription in Settings > Privacy Pro in the DuckDuckGo app on your other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.with.email.footer" = "Izmanto šo e-pasta adresi, lai aktivizētu savu abonementu sadaļā Iestatījumi > Privacy Pro lietotnē DuckDuckGo citās ierīcēs. **[Uzzināt vairāk](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Alert content for not found subscription */ -"subscription.email.inactive.alert.message" = "The subscription associated with this email is no longer active."; +"subscription.email.inactive.alert.message" = "Ar šo e-pasta adresi saistītais abonements vairs nav aktīvs."; /* Alert title for not found subscription */ -"subscription.email.inactive.alert.title" = "Subscription Not Found"; +"subscription.email.inactive.alert.title" = "Abonements nav atrasts"; /* Alert content for not found subscription */ -"subscription.expired.alert.message" = "The subscription associated with this Apple ID is no longer active."; +"subscription.expired.alert.message" = "Ar šo Apple ID saistītais abonements vairs nav aktīvs."; /* FAQ Button */ -"subscription.faq" = "FAQs and Support"; +"subscription.faq" = "Bieži uzdotie jautājumi un atbalsts"; /* FAQ Description */ -"subscription.faq.description" = "Get answers to frequently asked questions or contact Privacy Pro support from our help pages."; +"subscription.faq.description" = "Saņem atbildes uz bieži uzdotiem jautājumiem vai sazinies ar Privacy Pro atbalsta dienestu mūsu palīdzības lapās."; + +/* Send Feedback Button */ +"subscription.feedback" = "Sūtīt atsauksmi"; /* Cancel action for the existing subscription dialog */ -"subscription.found.cancel" = "Cancel"; +"subscription.found.cancel" = "Atcelt"; /* Restore action for the existing subscription dialog */ -"subscription.found.restore" = "Restore"; +"subscription.found.restore" = "Atjaunot"; /* Message for the existing subscription dialog */ -"subscription.found.text" = "We found a subscription associated with this Apple ID."; +"subscription.found.text" = "Mēs atradām abonementu, kas ir saistīts ar šo Apple ID."; /* Title for the existing subscription dialog */ -"subscription.found.title" = "Subscription Found"; +"subscription.found.title" = "Atrasts abonements"; /* Help and support Section header */ -"subscription.help" = "Help and support"; +"subscription.help" = "Palīdzība un atbalsts"; /* Manage Plan header */ -"subscription.manage.plan" = "Manage Plan"; +"subscription.manage.plan" = "Plāna pārvaldība"; /* Header for the subscription section */ -"subscription.manage.title" = "Subscription"; +"subscription.manage.title" = "Abonements"; /* Alert content for not found subscription */ -"subscription.notFound.alert.message" = "There is no subscription associated with this Apple ID."; +"subscription.notFound.alert.message" = "Ar šo Apple ID nav saistīts neviens abonements."; /* Alert title for not found subscription */ -"subscription.notFound.alert.title" = "Subscription Not Found"; +"subscription.notFound.alert.title" = "Abonements nav atrasts"; /* View plans button text */ -"subscription.notFound.view.plans" = "View Plans"; +"subscription.notFound.view.plans" = "Skatīt plānus"; /* Hero Text for Personal information removal */ -"subscription.pir.hero" = "Activate Privacy Pro on desktop to set up Personal Information Removal"; +"subscription.pir.hero" = "Aktivizē Privacy Pro datorā, lai iestatītu Personal Information Removal"; /* Description on how to use Personal information removal in desktop. The first placeholder references a location in the Desktop application. Privacy Pro>, and the second, the menu entry. i.e. */ -"subscription.pir.heroText" = "In the DuckDuckGo browser for desktop, go to %1$@ and click %2$@ to get started."; +"subscription.pir.heroText" = "DuckDuckGo pārlūkprogrammā galddatoram dodies uz %1$@ un noklikšķini uz %2$@, lai sāktu."; /* Settings references a menu in the Desktop app, Privacy Pro, references our product name */ -"subscription.pir.heroTextLocation" = "Settings > Privacy Pro"; +"subscription.pir.heroTextLocation" = "Iestatījumi > Privacy Pro"; /* Menu item for enabling Personal Information Removal on Desktop */ -"subscription.pir.heroTextMenyEntry" = "I have a subscription"; +"subscription.pir.heroTextMenyEntry" = "Man ir abonements"; /* Text for the 'macOS' button */ "subscription.pir.macos" = "Mac"; @@ -2476,28 +2638,28 @@ "subscription.pir.windows" = "Windows"; /* Progress view title when completing the purchase */ -"subscription.progress.view.completing.purchase" = "Completing purchase..."; +"subscription.progress.view.completing.purchase" = "Pabeidz pirkumu..."; /* Progress view title when starting the purchase */ -"subscription.progress.view.purchasing.subscription" = "Purchase in progress..."; +"subscription.progress.view.purchasing.subscription" = "Pirkuma apstrāde..."; /* Progress view title when restoring past subscription purchase */ -"subscription.progress.view.restoring.subscription" = "Restoring subscription..."; +"subscription.progress.view.restoring.subscription" = "Atjauno abonementu..."; /* Remove from this device button */ -"subscription.remove.from.device.button" = "Remove From This Device"; +"subscription.remove.from.device.button" = "Noņemt no šīs ierīces"; /* Remove from device confirmation dialog text */ -"subscription.remove.from.device.text" = "You will no longer be able to access your Privacy Pro subscription on this device. This will not cancel your subscription, and it will remain active on your other devices."; +"subscription.remove.from.device.text" = "Šajā ierīcē vairs nevarēsi piekļūt savam Privacy Pro abonementam. Tas neatcels tavu abonementu, un tas paliks aktīvs citās tavās ierīcēs."; /* Remove from device confirmation dialog title */ -"subscription.remove.from.device.title" = "Remove from this device?"; +"subscription.remove.from.device.title" = "Vai noņemt no šīs ierīces?"; /* Remove subscription button text */ -"subscription.remove.subscription" = "Remove Subscription"; +"subscription.remove.subscription" = "Noņemt abonementu"; /* Remove subscription cancel button text */ -"subscription.remove.subscription.cancel" = "Cancel"; +"subscription.remove.subscription.cancel" = "Atcelt"; /* Button text for general error message */ "subscription.restore.backend.error.button" = "Atpakaļ uz iestatījumiem"; @@ -2509,31 +2671,46 @@ "subscription.restore.backend.error.title" = "Kaut kas notika nepareizi"; /* Alert for general error message */ -"subscription.restore.general.error.message" = "The App Store was unable to process your purchase. Please try again later."; +"subscription.restore.general.error.message" = "App Store nevarēja apstrādāt tavu pirkumu. Lūdzu, mēģini vēlreiz vēlāk."; /* Alert for general error title */ -"subscription.restore.general.error.title" = "Something Went Wrong"; +"subscription.restore.general.error.title" = "Kaut kas notika nepareizi"; /* Alert button text for restored purchase alert */ -"subscription.restore.success.alert.button" = "OK"; +"subscription.restore.success.alert.button" = "Labi"; /* Alert message for restored purchase */ -"subscription.restore.success.alert.message" = "Your purchases have been restored."; +"subscription.restore.success.alert.message" = "Tavi pirkumi ir atjaunoti."; /* Alert title for restored purchase */ -"subscription.restore.success.alert.title" = "You’re all set."; +"subscription.restore.success.alert.title" = "Viss ir gatavs."; + +/* Subtitle in header when subscribed */ +"subscription.subscribed" = "Abonēts"; /* Subscription Expired Data. This reads as 'Your subscription expired on (date)' */ "subscription.subscription.expired.caption" = "Tava abonementa termiņš beidzās %@"; -/* Subscription expiration info. This reads as 'Your (monthly or annual) subscription expires on (date)' */ -"subscription.subscription.expiring.caption" = "Your %1$@ subscription expires on %2$@."; +/* Monthly subscription expiration info where parameter is expiration date. This reads as 'Your monthly subscription expires on (date)' */ +"subscription.subscription.expiring.monthly.caption" = "Tavs mēneša abonements beidzas %@."; + +/* Unknown period subscription expiration info where parameter is expiration date. This reads as 'Your subscription expires on (date)' */ +"subscription.subscription.expiring.unknown.caption" = "Tavs abonements beidzas %@."; + +/* Annual subscription expiration info where parameter is expiration date. This reads as 'Your annual subscription expires on (date)' */ +"subscription.subscription.expiring.yearly.caption" = "Tavs gada abonements beidzas %@."; + +/* Monthly subscription renewal info where parameter is renewal date. This reads as 'Your monthly subscription renews on (date)' */ +"subscription.subscription.renewing.monthly.caption" = "Tavs mēneša abonements atjaunojas %@."; + +/* Unknown period subscription renewal info where parameter is renewal date. This reads as 'Your subscription renews on (date)' */ +"subscription.subscription.renewing.unknown.caption" = "Tavs abonements atjaunojas %@."; -/* Subscription renewal info. This reads as 'Your (monthly or annual) subscription renews on (date)' */ -"subscription.subscription.renewing.caption" = "Your %1$@ subscription renews on %2$@."; +/* Annual subscription renewal info where parameter is renewal date. This reads as 'Your annual subscription renews on (date)' */ +"subscription.subscription.renewing.yearly.caption" = "Tavs gada abonements atjaunojas %@."; /* Navigation bar Title for subscriptions */ -"subscription.title" = "Privacy Pro"; +"subscription.title" = "Privacy Pro"; /* Message confirming that recovery code was copied to clipboard */ "sync.code.copied" = "Atgūšanas kods ir nokopēts starpliktuvē"; @@ -2677,118 +2854,118 @@ "voiceSearch.footer.note.old" = "Audio tiek apstrādāts ierīcē. Tas netiek glabāts, un tam nevar piekļūt neviens, pat ne DuckDuckGo."; /* Cancel action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.cancel" = "Dismiss"; +"vpn.access-revoked.alert.action.cancel" = "Nerādīt"; /* Primary action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.subscribe" = "Subscribe"; +"vpn.access-revoked.alert.action.subscribe" = "Abonēt"; /* Alert message for the alert when the Privacy Pro subscription expiress */ -"vpn.access-revoked.alert.message" = "Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"vpn.access-revoked.alert.message" = "Abonē Privacy Pro, lai atkārtoti izveidotu savienojumu ar DuckDuckGo VPN."; /* Alert title for the alert when the Privacy Pro subscription expires */ -"vpn.access-revoked.alert.title" = "VPN disconnected due to expired subscription"; +"vpn.access-revoked.alert.title" = "VPN atvienots, jo ir beidzies abonements"; /* Title for the VPN widget onboarding screen */ "vpn.addWidget.settings.title" = "Atrodi un izvēlies DuckDuckGo. Pēc tam velc uz VPN un izvēlies Pievienot logrīku."; /* Title for the Cancel button of the VPN feedback form */ -"vpn.feedback-form.button.cancel" = "Cancel"; +"vpn.feedback-form.button.cancel" = "Atcelt"; /* Title for the Done button of the VPN feedback form */ -"vpn.feedback-form.button.done" = "Done"; +"vpn.feedback-form.button.done" = "Gatavs"; /* Title for the Submit button of the VPN feedback form */ -"vpn.feedback-form.button.submit" = "Submit"; +"vpn.feedback-form.button.submit" = "Iesniegt"; /* Title for the Submitting state of the VPN feedback form */ -"vpn.feedback-form.button.submitting" = "Submitting…"; +"vpn.feedback-form.button.submitting" = "Iesniedz…"; /* Title for the browser crash/freeze category of the VPN feedback form */ -"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN causes browser to crash or freeze"; +"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN izraisa pārlūkprogrammas avāriju vai apstāšanos"; /* Title for the 'VPN fails to connect' category of the VPN feedback form */ -"vpn.feedback-form.category.fails-to-connect" = "VPN fails to connect"; +"vpn.feedback-form.category.fails-to-connect" = "Neizdodas izveidot VPN savienojumu"; /* Title for the 'VPN feature request' category of the VPN feedback form */ -"vpn.feedback-form.category.feature-request" = "VPN feature request"; +"vpn.feedback-form.category.feature-request" = "VPN funkcijas pieprasījums"; /* Title for the category 'VPN causes issues with other apps or websites' category of the VPN feedback form */ -"vpn.feedback-form.category.issues-with-apps" = "VPN causes issues with other apps or websites"; +"vpn.feedback-form.category.issues-with-apps" = "VPN rada problēmas ar citām lietotnēm vai vietnēm"; /* Title for the local device connectivity category of the VPN feedback form */ -"vpn.feedback-form.category.local-device-connectivity" = "VPN won't let me connect to local device"; +"vpn.feedback-form.category.local-device-connectivity" = "VPN neļauj izveidot savienojumu ar vietēju ierīci"; /* Title for the 'other VPN feedback' category of the VPN feedback form */ -"vpn.feedback-form.category.other" = "Other VPN feedback"; +"vpn.feedback-form.category.other" = "Citas atsauksmes par VPN"; /* Title for the category selection state of the VPN feedback form */ -"vpn.feedback-form.category.select-category" = "Select a category"; +"vpn.feedback-form.category.select-category" = "ATLASĪT KATEGORIJU"; /* Title for the 'VPN is too slow' category of the VPN feedback form */ -"vpn.feedback-form.category.too-slow" = "VPN connection is too slow"; +"vpn.feedback-form.category.too-slow" = "VPN savienojums ir pārāk lēns"; /* Title for the 'unable to install' category of the VPN feedback form */ -"vpn.feedback-form.category.unable-to-install" = "Unable to install VPN"; +"vpn.feedback-form.category.unable-to-install" = "Nevar instalēt VPN"; /* Title for the feedback sent view description of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.description" = "Your feedback will help us improve the\nDuckDuckGo VPN."; +"vpn.feedback-form.sending-confirmation.description" = "Tavas atsauksmes mums palīdzēs uzlabot\n DuckDuckGo VPN."; /* Title for the feedback sending error text of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.error" = "We couldn't send your feedback right now, please try again."; +"vpn.feedback-form.sending-confirmation.error" = "Pašlaik nevarējām nosūtīt tavu atsauksmi. Lūdzu, mēģini vēlreiz."; /* Title for the feedback sent view title of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.title" = "Thank you!"; +"vpn.feedback-form.sending-confirmation.title" = "Paldies!"; /* Toast message when the VPN feedback form is submitted successfully */ -"vpn.feedback-form.submitted.message" = "Thank You! Feedback submitted."; +"vpn.feedback-form.submitted.message" = "Paldies! Tava atsauksme ir iesniegta."; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-1" = "Please describe what's happening, what you expected to happen, and the steps that led to the issue:"; +"vpn.feedback-form.text-1" = "Lūdzu, apraksti, kas notiek, ko tu sagaidīji, un darbības, kas izraisīja problēmu:"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-2" = "In addition to the details entered into this form, your app issue report will contain:"; +"vpn.feedback-form.text-2" = "Papildus šajā veidlapā ievadītajai informācijai ziņojums par lietotnes problēmu ietvers tālāk norādīto informāciju."; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-3" = "• Whether specific DuckDuckGo features are enabled"; +"vpn.feedback-form.text-3" = "• Vai ir iespējotas konkrētas DuckDuckGo funkcijas."; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-4" = "• Aggregate DuckDuckGo app diagnostics"; +"vpn.feedback-form.text-4" = "• Apkopoti DuckDuckGo lietotnes diagnostikas dati."; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-5" = "By tapping \"Submit\" I agree that DuckDuckGo may use the information in this report for purposes of improving the app's features."; +"vpn.feedback-form.text-5" = "Pieskaroties pie “Iesniegt”, es piekrītu, ka DuckDuckGo drīkst izmantot šajā ziņojumā sniegto informāciju, lai uzlabotu lietotnes funkcijas."; /* Title for each screen of the VPN feedback form */ -"vpn.feedback-form.title" = "Help Improve the DuckDuckGo VPN"; +"vpn.feedback-form.title" = "Palīdzi uzlabot DuckDuckGo VPN"; /* VPN settings screen cell text for adding the VPN widget to the home screen */ "vpn.settings.add.widget" = "Pievienot VPN logrīku sākuma ekrānam"; /* Disclaimer for the DNS Server section on the DNS Server screen */ -"vpn.settings.dns.section-disclaimer" = "Using a custom DNS server can impact browsing speeds and expose your activity to third parties if the server isn't secure or reliable."; +"vpn.settings.dns.section-disclaimer" = "Ja serveris nav drošs vai uzticams, pielāgota DNS servera izmantošana var ietekmēt pārlūkošanas ātrumu un atklāt tavas darbības trešajām pusēm."; /* Header text for the DNS section on the VPN Settings screen */ "vpn.settings.dns.section-header" = "DNS"; /* Title for the Apply button on the DNS Server setting screen */ -"vpn.settings.dns.server.apply.button.title" = "Apply"; +"vpn.settings.dns.server.apply.button.title" = "Lietot"; /* Default value for the DNS Server row on the VPN Settings screen */ "vpn.settings.dns.server.default.value" = "DuckDuckGo"; /* Title for the IPv4 Address setting */ -"vpn.settings.dns.server.ipv4.title" = "IPv4 Address"; +"vpn.settings.dns.server.ipv4.title" = "IPv4 adrese"; /* Custom option for the DNS Server setting */ -"vpn.settings.dns.server.option.custom" = "Custom"; +"vpn.settings.dns.server.option.custom" = "Paraža"; /* Recommended option for the DNS Server setting */ -"vpn.settings.dns.server.option.default" = "DuckDuckGo (Recommended)"; +"vpn.settings.dns.server.option.default" = "DuckDuckGo (ieteicams)"; /* Title for the DNS Server setting screen */ -"vpn.settings.dns.server.screen.title" = "DNS Server"; +"vpn.settings.dns.server.screen.title" = "DNS serveris"; /* Title for the DNS Server row on the VPN Settings screen */ -"vpn.settings.dns.server.title" = "DNS Server"; +"vpn.settings.dns.server.title" = "DNS serveris"; /* Title for the button to enable push notifications in system settings */ "waitlist.allow-notifications" = "Atļaut paziņojumus"; diff --git a/DuckDuckGo/lv.lproj/OmniBar.strings b/DuckDuckGo/lv.lproj/OmniBar.strings index 989aa53324..0849bf4059 100644 --- a/DuckDuckGo/lv.lproj/OmniBar.strings +++ b/DuckDuckGo/lv.lproj/OmniBar.strings @@ -7,6 +7,9 @@ /* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "Dt8-We-5tV"; */ "Dt8-We-5tV.accessibilityLabel" = "Notīrīt tekstu"; +/* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "gb5-wL-4OW"; */ +"gb5-wL-4OW.accessibilityLabel" = "Notīrīt tekstu"; + /* Class = "UIButton"; accessibilityLabel = "Share"; ObjectID = "hor-Jd-3kw"; */ "hor-Jd-3kw.accessibilityLabel" = "Kopīgot"; diff --git a/DuckDuckGo/lv.lproj/Settings.strings b/DuckDuckGo/lv.lproj/Settings.strings index 985402f0ea..12f92a7981 100644 --- a/DuckDuckGo/lv.lproj/Settings.strings +++ b/DuckDuckGo/lv.lproj/Settings.strings @@ -10,9 +10,6 @@ /* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "5YD-Od-uDU"; */ "5YD-Od-uDU.text" = "Automātiski notīrīt datus"; -/* Class = "UIButton"; normalTitle = "Settings"; ObjectID = "8YG-7Q-Czd"; */ -"8YG-7Q-Czd.normalTitle" = "Iestatījumi"; - /* Class = "UILabel"; text = "App Exit, Inactive for 1 Hour"; ObjectID = "9EC-lx-77p"; */ "9EC-lx-77p.text" = "Iziešana no lietotnes, neaktīva 1 stundu"; @@ -76,9 +73,6 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Neaizsargātas vietnes"; -/* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ -"ssa-zd-L3T.title" = "Burtu izmērs"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Rādīt tastatūru"; diff --git a/DuckDuckGo/nb.lproj/DaxOnboarding.strings b/DuckDuckGo/nb.lproj/DaxOnboarding.strings index 281d63a9ec..bb5a2a38c9 100644 --- a/DuckDuckGo/nb.lproj/DaxOnboarding.strings +++ b/DuckDuckGo/nb.lproj/DaxOnboarding.strings @@ -1,12 +1,6 @@ /* Class = "UIButton"; normalTitle = "Hide"; ObjectID = "hgy-75-OvJ"; */ "hgy-75-OvJ.normalTitle" = "Skjul"; -/* Class = "UILabel"; text = "Welcome to\nDuckDuckGo!"; ObjectID = "Lvy-9G-3gr"; */ -"Lvy-9G-3gr.text" = "Velkommen til\nDuckDuckGo!"; - -/* Class = "UIButton"; normalTitle = "Let’s Do It!"; ObjectID = "oAv-Zt-uIt"; */ -"oAv-Zt-uIt.normalTitle" = "Sett i gang!"; - /* Class = "UIButton"; normalTitle = "Button"; ObjectID = "zJt-F9-L48"; */ "zJt-F9-L48.normalTitle" = "Knapp"; diff --git a/DuckDuckGo/nb.lproj/Localizable.strings b/DuckDuckGo/nb.lproj/Localizable.strings index 8a1e7d1bd6..ccd9ca0330 100644 --- a/DuckDuckGo/nb.lproj/Localizable.strings +++ b/DuckDuckGo/nb.lproj/Localizable.strings @@ -341,7 +341,7 @@ "autofill.keep-enabled.alert.disable" = "Slå av"; /* A link that takes the user to the DuckDuckGo help pages explaining password managers */ -"autofill.learn.more.link.title" = "Learn More"; +"autofill.learn.more.link.title" = "Finn ut mer"; /* Button displayed after saving/updating an autofill login that takes the user to the saved login */ "autofill.login-save-action-button.toast" = "Vis"; @@ -977,9 +977,6 @@ /* ad = advertisment */ "dax.onboarding.home.subsequent" = "Dette går bra!\n\nHusk: Hver gang du surfer med meg, klippes vingene av en ekkel annonse. 👍"; -/* No comment provided by engineer. */ -"dax.onboarding.message" = "Internettet kan være ganske skummelt.\n\nMen ikke vær redd! Det er enklere enn du tror å søke og surfe privat."; - /* No comment provided by engineer. */ "Debug" = "Feilsøke"; @@ -1080,7 +1077,7 @@ "duckPlayer.never.label" = "Aldri"; /* Body text for the modal feature explanation */ -"duckplayer.presentation.modal.body" = "Med Duck Player kan du se på YouTube uten målrettede annonser i en kinolignende opplevelse i DuckDuckGo. Det du ser på, påvirker ikke anbefalingene du får."; +"duckplayer.presentation.modal.body" = "Med Duck Player kan du se på YouTube uten målrettede annonser i DuckDuckGo. Det du ser på, påvirker ikke anbefalingene du får."; /* Button that will dismiss the modal */ "duckplayer.presentation.modal.dismiss-button" = "Skjønner!"; @@ -1088,17 +1085,23 @@ /* Two line title (separated by \n) for the feature explanation */ "duckplayer.presentation.modal.title" = "Drukner du i annonser på YouTube? Prøv Duck Player!"; +/* Two line title (separated by \n) for the feature explanation */ +"duckplayer.presentation.modal.title.youtube" = "Drukner du i annonser på YouTube? Ikke med Duck Player!"; + /* Footer label in the settings screen for Duck Player */ "duckplayer.settings.footer" = "DuckDuckGo gir deg alt du trenger for å beskytte personvernet ditt, når du surfer på nettet."; /* Text explaining what Duck Player is in the settings screen. */ -"duckplayer.settings.info-text" = "Duck Player tilbyr en ren seeropplevelse uten tilpassede annonser og forhindrer at seeraktiviteten din påvirker YouTube-anbefalingene dine."; +"duckplayer.settings.info-text" = "Med Duck Player kan du se på YouTube uten målrettede annonser i DuckDuckGo. Det du ser på, påvirker ikke anbefalingene du får."; /* Button that takes the user to learn more about Duck Player. */ "duckplayer.settings.learn-more" = "Finn ut mer"; +/* Settings screen cell text for DuckPlayer settings to open in new tab */ +"duckplayer.settings.open-new-tab-label" = "Åpne Duck Player i en ny fane"; + /* Settings screen cell text for DuckPlayer settings */ -"duckplayer.settings.open-videos-in" = "Åpne videoer i Duck Player"; +"duckplayer.settings.open-videos-in" = "Åpne YouTube-videoer i Duck Player"; /* Settings screen cell text for DuckPlayer settings */ "duckplayer.settings.title" = "Duck Player"; @@ -1169,9 +1172,6 @@ /* Title for prompt to sign up for email protection */ "email.signup-prompt.title" = "Skjul e-postadressen din og\nblokker sporere"; -/* No comment provided by engineer. */ -"empty!" = "empty!"; - /* Empty list state placholder */ "empty.bookmarks" = "Ingen bokmerker lagt til ennå"; @@ -1322,6 +1322,39 @@ /* Confirmation button */ "feedback.form.submit" = "Send"; +/* Title of the feedback form */ +"feedback.form.title" = "Hjelp oss med å forbedre Privacy Pro"; + +/* Category for Identity Theft Restoration feedback */ +"feedback.general.category.itr" = "Identity Theft Restoration"; + +/* Category for Personal Info Removal feedback */ +"feedback.general.category.pir" = "Fjerning av personopplysninger"; + +/* Category for subscription and payments feedback */ +"feedback.general.category.ppro" = "Abonnement og betalinger"; + +/* Prompt to select a category for general feedback */ +"feedback.general.category.select" = "VELG EN KATEGORI"; + +/* Category for VPN feedback */ +"feedback.general.category.vpn" = "VPN"; + +/* Category for access code issues */ +"feedback.itr.category.access-code" = "Problem med tilgangskode"; + +/* Category for issues contacting an advisor */ +"feedback.itr.category.cant-contact-advisor" = "Kan ikke kontakte rådgiver"; + +/* Prompt to select a category for Identity Theft Restoration feedback */ +"feedback.itr.category.select" = "VELG EN KATEGORI"; + +/* Category for other Identity Theft Restoration issues */ +"feedback.itr.category.something-else" = "Noe annet"; + +/* Category for unhelpful advisor calls */ +"feedback.itr.category.unhelpful" = "Det hjalp ikke å ringe til rådgiveren"; + /* No comment provided by engineer. */ "feedback.negative.form.genericPlaceholder" = "Vær så spesifikk som mulig"; @@ -1361,6 +1394,24 @@ /* No comment provided by engineer. */ "feedback.performance.slowLoading" = "Nettsider og søkeresultater lastes inn sakte"; +/* Category for when scan finds incorrect records */ +"feedback.pir.category.not-me" = "Skanningen fant oppføringer som ikke er meg"; + +/* Category for when scan doesn't find info on a specific site */ +"feedback.pir.category.nothing-on-site" = "Skanningen fant ikke opplysningene mine på et bestemt nettsted"; + +/* Category for other Personal Info Removal issues */ +"feedback.pir.category.other" = "Noe annet"; + +/* Category for when the removal process is stuck */ +"feedback.pir.category.removal-stuck" = "Fjerningsprosessen er stoppet opp"; + +/* Category for when the scan is stuck */ +"feedback.pir.category.scan-stuck" = "Skanningen etter oppføringer er stoppet opp"; + +/* Prompt to select a category for Personal Info Removal feedback */ +"feedback.pir.category.select" = "VELG EN KATEGORI"; + /* Header above input field */ "feedback.positive.form.header" = "Del detaljer"; @@ -1379,6 +1430,15 @@ /* Button encouraging uses to share details aboout their feedback */ "feedback.positive.submit" = "Del detaljer"; +/* Category for other Privacy Pro issues */ +"feedback.ppro.category.other" = "Noe annet"; + +/* Category for one-time password issues */ +"feedback.ppro.category.otp" = "Problem med engangspassord"; + +/* Prompt to select a category for Privacy Pro feedback */ +"feedback.ppro.category.select" = "VELG EN KATEGORI"; + /* No comment provided by engineer. */ "feedback.start.footer" = "Den anonyme tilbakemeldingen din er viktig for oss."; @@ -1529,12 +1589,6 @@ /* No comment provided by engineer. */ "keyCommandShowAllTabs" = "Vis alle faner"; -/* Please preserve newline character */ -"launchscreenWelcomeMessage" = "Velkommen til\nDuckDuckGo!"; - -/* No comment provided by engineer. */ -"LOREM IPSUM" = "LOREM IPSUM"; - /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "DuckDuckGo for Mac har hastigheten du trenger, nettleserfunksjonene du forventer, og er stappfull av våre førsteklasses personvernfunksjoner."; @@ -1586,62 +1640,83 @@ /* Edit button */ "navigation.title.edit" = "Rediger"; -/* String indicating NetP is connected when viewed from the settings screen */ -"netP.cell.connected" = "Connected"; - -/* String indicating NetP is disconnected when viewed from the settings screen */ -"netP.cell.disconnected" = "Not connected"; - -/* Title for the DuckDuckGo VPN feature in settings */ -"netP.settings.title" = "VPN"; +/* String indicating NetP is snoozing when viewed from the settings screen */ +"netP.cell.snoozing" = "Slumrer"; /* Title for the DuckDuckGo VPN feature */ "netP.title" = "DuckDuckGo VPN"; +/* Button title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.action" = "Legg til widget"; + +/* Message for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.message" = "Slå VPN av og på direkte på startskjermen."; + +/* Title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.title" = "Legg til VPN-widget"; + /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN ble frakoblet på grunn av et utløpt abonnement. Abonner på Privacy Pro for å koble til DuckDuckGo-VPN igjen."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Nettverksbeskyttelse kunne ikke koble til. Prøv igjen senere."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "DuckDuckGo VPN mislyktes i å koble til. Prøv igjen senere."; /* Title for the VPN FAQ screen. */ "network.protection.faq.title" = "Vanlige spørsmål om DuckDuckGo VPN"; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Nettverksbeskyttelse ble avbrutt. Prøver å koble til igjen nå..."; +/* Message for tooltip about geoswitching */ +"network.protection.geoswitching.tip.message" = "Du kan tilpasse VPN-plasseringen din ved å koble til en hvilken som helst av våre servere over hele verden."; + +/* Title for tooltip about geoswitching */ +"network.protection.geoswitching.tip.title" = "Endre plassering"; + +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "DuckDuckGo VPN ble avbrutt. Prøver å koble til igjen nå..."; /* Message for the network protection invite dialog */ -"network.protection.invite.dialog.message" = "Enter your invite code to get started."; +"network.protection.invite.dialog.message" = "Skriv inn invitasjonskoden din for å komme i gang."; /* Title for the network protection invite screen */ "network.protection.invite.dialog.title" = "Du er invitert til å prøve DuckDuckGo VPN"; /* Prompt for the network protection invite code text field */ -"network.protection.invite.field.prompt" = "Invite Code"; +"network.protection.invite.field.prompt" = "Invitasjonskode"; /* Message for the network protection invite success view */ -"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; +"network.protection.invite.success.message" = "Skjul plasseringen din for nettsteder og skjul nettaktiviteten din for internettleverandører og andre i nettverket ditt."; /* Title for the network protection invite success view */ -"network.protection.invite.success.title" = "Success! You’re in."; +"network.protection.invite.success.title" = "Vellykket! Du er inne."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; /* Title text for an iOS quick action that opens VPN settings */ "network.protection.quick-action.open-vpn" = "Åpne VPN"; +/* Button title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.action" = "Finn ut mer"; + +/* Message for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.message" = "Du kan bruke nettsteder eller apper som blokkerer VPN-trafikk, ved å slumre VPN-tilkoblingen."; + +/* Title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.title" = "Unngå VPN-konflikter"; + +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN satt på slumring i %@"; + /* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ "network.protection.status.connected.format" = "Tilkoblet · %@"; /* The label for the NetP VPN when connecting */ -"network.protection.status.connecting" = "Connecting..."; +"network.protection.status.connecting" = "Kobler til..."; /* The label for the NetP VPN when disconnected */ -"network.protection.status.disconnected" = "Not connected"; +"network.protection.status.disconnected" = "Ikke tilkoblet"; /* The label for the NetP VPN when disconnecting */ -"network.protection.status.disconnecting" = "Disconnecting..."; +"network.protection.status.disconnecting" = "Kobler fra..."; /* Message label text for the status view when VPN is disconnected */ "network.protection.status.header.message.off" = "Koble til for å sikre all internettrafikk\npå enheten din."; @@ -1655,26 +1730,41 @@ /* Header title label text for the status view when VPN is connected */ "network.protection.status.header.title.on" = "DuckDuckGo VPN er på"; +/* Header title label text for the status view when VPN is snoozing */ +"network.protection.status.header.title.snoozed" = "DuckDuckGo VPN er satt på slumring"; + /* The status view 'Share Feedback' button which is shown inline on the status view after the temporary free use footer text */ -"network.protection.status.menu.share.feedback" = "Share Feedback"; +"network.protection.status.menu.share.feedback" = "Del tilbakemelding"; + +/* The label for the NetP VPN when paused */ +"network.protection.status.paused" = "Satt på pause"; + +/* The label for when NetP VPN is snoozing plus the length of time remaining formatted as '0:00' */ +"network.protection.status.snoozing.format" = "Slumrer, %@ gjenstår"; + +/* Snooze button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.snooze" = "Slumre i 20 minutter"; + +/* Wake Up button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.wake-up" = "Våkne"; /* Connection details label shown in NetworkProtection's status view. */ -"network.protection.status.view.connection.details" = "Connection Details"; +"network.protection.status.view.connection.details" = "Tilkoblingsdetaljer"; /* Custom DNS label shown in NetworkProtection's status view. */ -"network.protection.status.view.custom.dns" = "DNS Server"; +"network.protection.status.view.custom.dns" = "DNS-server"; /* Generic connection failed error message shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.message" = "Please try again later."; +"network.protection.status.view.error.connection.failed.message" = "Prøv igjen senere."; /* Generic connection failed error title shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; +"network.protection.status.view.error.connection.failed.title" = "Tilkobling mislyktes."; /* IP Address label shown in NetworkProtection's status view. */ -"network.protection.status.view.ip.address" = "IP Address"; +"network.protection.status.view.ip.address" = "IP-adresse"; /* Location label shown in NetworkProtection's status view. */ -"network.protection.status.view.location" = "Location"; +"network.protection.status.view.location" = "Posisjon"; /* Label shown on the title of the settings section in NetworkProtection's status view. */ "network.protection.status.view.settings.section.title" = "Administrere"; @@ -1682,12 +1772,15 @@ /* Title label text for the status view when netP is disconnected */ "network.protection.status.view.title" = "VPN"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Nettverksbeskyttelse er på. Plasseringen og nettaktiviteten din er beskyttet."; +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "DuckDuckGo VPN er på. Plasseringen og nettaktiviteten din er beskyttet."; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Omdirigerer enhetstrafikk gjennom %@."; +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "VPN-slumringen er avsluttet. Ruter enhetstrafikk gjennom %@."; + /* Title for the button to link to the iOS app settings and enable notifications app-wide. */ "network.protection.turn.on.notifications.button.title" = "Slå på varslinger"; @@ -1698,13 +1791,13 @@ "network.protection.vpn.about" = "Om"; /* Section header for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.section.header" = "Notifications"; +"network.protection.vpn.alerts.section.header" = "Varsler"; /* List section footer for the toggle for VPN alerts. */ "network.protection.vpn.alerts.toggle.section.footer" = "Få varsel hvis tilkoblingen blir brutt eller VPN-statusen endret."; /* Title for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.toggle.title" = "VPN Notifications"; +"network.protection.vpn.alerts.toggle.title" = "VPN-varsler"; /* Title for the data volume section in the VPN status screen */ "network.protection.vpn.data-volume" = "Datavolum"; @@ -1713,13 +1806,13 @@ "network.protection.vpn.exclude.local.networks.setting.footer" = "La lokal trafikk omgå VPN-et og koble til enheter på det lokale nettverket, for eksempel en skriver."; /* Header text for the Exclude Local Networks setting item. */ -"network.protection.vpn.exclude.local.networks.setting.header" = "General"; +"network.protection.vpn.exclude.local.networks.setting.header" = "Generelt"; /* Title for the Exclude Local Networks setting item. */ "network.protection.vpn.exclude.local.networks.setting.title" = "Ekskluder lokale nettverk"; /* Title for the VPN Location screen's All Countries section. */ -"network.protection.vpn.location.all.countries.section.title" = "All Countries"; +"network.protection.vpn.location.all.countries.section.title" = "Alle land"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.connected" = "Tilkoblet sted"; @@ -1734,7 +1827,7 @@ "network.protection.vpn.location.recommended.section.footer" = "Koble automatisk til nærmeste server vi kan finne."; /* Title for the VPN Location screen's Recommended section. */ -"network.protection.vpn.location.recommended.section.title" = "Recommended"; +"network.protection.vpn.location.recommended.section.title" = "Anbefalt"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.selected" = "Valgt sted"; @@ -1752,13 +1845,13 @@ "network.protection.vpn.preferred.location.nearest" = "Nærmeste sted"; /* Title for the Preferred Location VPN Settings item. */ -"network.protection.vpn.preferred.location.title" = "Preferred Location"; +"network.protection.vpn.preferred.location.title" = "Foretrukket plassering"; /* Footer text for the DNS server setting item. */ -"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo routes DNS queries through our DNS servers so your internet provider can't see what websites you visit."; +"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo ruter DNS-spørringer gjennom DNS-serverne våre, slik at internettleverandøren din ikke kan se hvilke nettsteder du er på."; /* Title for the FAQ row in the VPN status screen. */ -"network.protection.vpn.settings.faq" = "FAQs and Support"; +"network.protection.vpn.settings.faq" = "Vanlige spørsmål og kundestøtte"; /* Title for the feedback row in the VPN status screen. */ "network.protection.vpn.settings.share-feedback" = "Del tilbakemelding om VPN"; @@ -1940,6 +2033,66 @@ /* Deactivate button */ "pm.deactivate" = "Deaktiver"; +/* Label for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.label" = "E-postadresse (valgfritt)"; + +/* Placeholder for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.placeholder" = "navn@e-post.com"; + +/* Placeholder for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.placeholder" = "Gi oss gjerne en tilbakemelding …"; + +/* Title for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.title" = "Generell tilbakemelding"; + +/* Title for the ITR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-itr-problem.title" = "Identity Theft Restoration"; + +/* Title for the PIR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-pir-problem.title" = "Personal Information Removal"; + +/* Title for the Subscriptions and Payments category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-ppro-problem.title" = "Abonnementer og betalinger"; + +/* Placeholder for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.placeholder" = "Fortell oss hva som skjer …"; + +/* Title for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.title" = "Rapporter et problem"; + +/* Title for the VPN category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-vpn-problem.title" = "VPN"; + +/* Placeholder for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.placeholder" = "Hvilken funksjon hadde du ønsket deg?"; + +/* Title for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.title" = "Forespørsel om funksjon"; + +/* Title for the category selection section in the Privacy Pro feedback form */ +"ppro.feedback-form.select-category.title" = "VELG EN KATEGORI"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-1" = "Har du funnet et problem som ikke er dekket i [hjelpesenteret](duck://)? Da vil vi svært gjerne høre om det.\n\nOppgi en e-postadresse hvis du vil at vi skal kontakte deg om dette problemet (det er ikke sikkert vi kan svare på alle henvendelser):"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-2" = "I tillegg til detaljene ovenfor sender vi noe anonymisert informasjon sammen med tilbakemeldingen din:"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-3" = "• Hvorvidt enkelte nettleserfunksjoner er aktive"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-4" = "• Samlet appdiagnostikk (f.eks. feilkoder)"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-5" = "Ved å trykke på «Send inn» godtar du at DuckDuckGo kan bruke den innsendte informasjonen til å forbedre appen."; + +/* Title for the text box in the Privacy Pro feedback form */ +"ppro.feedback-form.text-box.title" = "Tilbakemelding"; + +/* Title for the Privacy Pro feedback form */ +"ppro.feedback-form.title" = "Send tilbakemelding"; + /* Button title for sync bookmarks limits exceeded warning to go to manage bookmarks */ "prefrences.sync.bookmarks-limit-exceeded-action" = "Administrer bokmerker"; @@ -2029,6 +2182,18 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Favoritter"; +/* Name of the option the user can chose to give general browser feedback */ +"send.browser.feedback.general-feedback" = "Generell tilbakemelding"; + +/* Name of the option the user can chose to give browser feedback about a problem they enountered */ +"send.browser.feedback.report-problem" = "Rapporter et problem"; + +/* Name of the option the user can chose to give browser feedback about a feature they would like */ +"send.browser.feedback.request-feature" = "Be om en funksjon"; + +/* Title of the picker where the user can chose the category of the feedback they want ot send. */ +"send.browser.feedback.select-category" = "VELG EN KATEGORI"; + /* Settings cell for About DDG */ "settings.about.ddg" = "Om DuckDuckGo"; @@ -2084,6 +2249,9 @@ /* Section footer Autolock description */ "settings.autolock.description" = "Hvis Touch ID, Face ID eller et systempassord er aktivert, blir du bedt om å låse opp appen når du åpner den."; +/* Settings cell for Browser Feedback */ +"settings.browser.feedback" = "Nettlesertilbakemelding"; + /* Settings screen cell text for Automatically Clearing Data */ "settings.clear.data" = "Slett data automatisk"; @@ -2182,7 +2350,7 @@ "settings.ppro" = "Privacy Pro"; /* Title for Link in the Footer of Privacy Pro section */ -"settings.ppro.footer" = "Privacy Policy and Terms of Service"; +"settings.ppro.footer" = "Personvernerklæring og vilkår for bruk"; /* Settings screen cell for long press previews */ "settings.previews" = "Forhåndsvisning ved å trykke og holde"; @@ -2208,23 +2376,20 @@ /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Angi plassering av adressefeltet"; -/* Subscription activation pending description */ -"settings.subscription.activation.pending.description" = "This is taking longer than usual, please check back later."; +/* Privacy pro description subtitle in settings when the is activating */ +"settings.subscription.activating" = "Aktiverer"; -/* Subscription activation pending title */ -"settings.subscription.activation.pending.title" = "Abonnementet ditt blir aktivert"; +/* Subscription activation pending description */ +"settings.subscription.activation.pending.description" = "Dette tar lengre tid enn vanlig. Prøv igjen senere."; /* Data Broker protection cell subtitle for privacy pro */ -"settings.subscription.DBP.subtitle" = "Remove your info from sites that sell it"; +"settings.subscription.DBP.subtitle" = "Fjern informasjonen din fra nettsteder som selger den"; /* Data Broker protection cell title for privacy pro */ "settings.subscription.DBP.title" = "Personal Information Removal"; -/* Privacy pro description subtext */ -"settings.subscription.description" = "Mer sømløst personvern med tre nye beskyttelsesfunksjoner:"; - /* I have a Subscription button text for privacy pro */ -"settings.subscription.existing.subscription" = "I Have a Subscription"; +"settings.subscription.existing.subscription" = "Jeg har et abonnement"; /* Subscription expired description */ "settings.subscription.expired.comment" = "Abonner igjen for å fortsette å bruke Privacy Pro"; @@ -2232,23 +2397,26 @@ /* Subscription expired tittle message */ "settings.subscription.expired.title" = "Abonnementet ditt på Privacy Pro har utløpt"; -/* Privacy pro features list */ -"settings.subscription.features" = " • VPN\n • Personal Information Removal\n • Identity Theft Restoration"; - /* Identity theft restoration cell subtitle for privacy pro */ -"settings.subscription.ITR.subtitle" = "If your identity is stolen, we'll help restore it"; +"settings.subscription.ITR.subtitle" = "Hvis identiteten din blir stjålet, hjelper vi deg med å få den tilbake"; /* Identity theft restoration cell title for privacy pro */ "settings.subscription.ITR.title" = "Identity Theft Restoration"; /* Get Privacy Pro button text for privacy pro */ -"settings.subscription.learn.more" = "Get Privacy Pro"; +"settings.subscription.learn.more" = "Skaff deg Privacy Pro"; /* Subscription Settings button text for privacy pro */ -"settings.subscription.manage" = "Subscription Settings"; +"settings.subscription.manage" = "Abonnementsinnstillinger"; + +/* Privacy Pro description subtitle in settings */ +"settings.subscription.row.description" = "Inkluderer vår VPN og Identity Theft Restoration."; -/* Call to action title for Privacy Pro */ -"settings.subscription.subscribe" = "Subscribe to Privacy Pro"; +/* Call to action title for Privacy Pro settings */ +"settings.subscription.subscribe" = "Beskytt tilkoblingen og identiteten din med Privacy Pro"; + +/* Privacy pro description subtitle in settings */ +"settings.subscription.us.description" = "Inkluderer vår VPN, Personal Information Removal og Identity Theft Restoration."; /* VPN cell title for privacy pro */ "settings.subscription.VPN.title" = "VPN"; @@ -2317,67 +2485,64 @@ "siteFeedback.urlPlaceholder" = "Hvilket nettsted fungerer ikke?"; /* Subscription Activation Info */ -"subscription.activate..header.description" = "Access your Privacy Pro subscription on this device via Apple ID or an email address."; +"subscription.activate..header.description" = "Få tilgang til Privacy Pro-abonnementet ditt på denne enheten via Apple ID eller en e-postadresse."; /* Button for adding email address to subscription */ -"subscription.activate.add.email.button" = "Add Email"; +"subscription.activate.add.email.button" = "Legg til e-postadresse"; /* Apple ID option for activation */ "subscription.activate.appleid" = "Apple ID"; /* Button text for restoring purchase via Apple ID */ -"subscription.activate.appleid.button" = "Restore Purchase"; +"subscription.activate.appleid.button" = "Gjenopprett kjøp"; /* Description for Apple ID activation */ -"subscription.activate.appleid.description" = "Restore your purchase to activate your subscription on this device."; +"subscription.activate.appleid.description" = "Gjenopprett kjøpet ditt for å aktivere abonnementet ditt på denne enheten."; /* Subscription Activation Info */ -"subscription.activate.description" = "Your subscription is automatically available in DuckDuckGo on any device signed in to your Apple ID."; +"subscription.activate.description" = "Abonnementet ditt er automatisk tilgjengelig i DuckDuckGo på alle enheter som er logget på Apple ID-en din."; /* Button for editing email address added to subscription */ -"subscription.activate.edit.email.button" = "Edit Email"; +"subscription.activate.edit.email.button" = "Rediger e-postadresse"; /* View Title for editing your email account */ -"subscription.activate.edit.email.title" = "Edit Email"; +"subscription.activate.edit.email.title" = "Rediger e-postadresse"; /* Email option for activation */ -"subscription.activate.email" = "Email"; +"subscription.activate.email" = "E-post"; /* Restore button title for Email */ -"subscription.activate.email.button" = "Enter Email"; +"subscription.activate.email.button" = "Skriv inn e-postadresse"; /* Description for Email activation */ -"subscription.activate.email.description" = "Use your email to activate your subscription on this device."; +"subscription.activate.email.description" = "Bruk e-postadressen din til å aktivere abonnementet ditt på denne enheten."; /* Activate subscription title */ "subscription.activate.email.title" = "Aktiver abonnement"; /* Button title for cancelling email deletion */ -"subscription.activate.manage.email.cancel" = "Cancel"; +"subscription.activate.manage.email.cancel" = "Avbryt"; /* Button title for confirming email deletion */ "subscription.activate.manage.email.OK" = "OK"; /* Restore button title for AppleID */ -"subscription.activate.restore.apple" = "Restore Purchase"; +"subscription.activate.restore.apple" = "Gjenopprett kjøp"; /* Subscription Activation Title */ -"subscription.activate.title" = "Activate your subscription on this device"; +"subscription.activate.title" = "Aktiver abonnementet ditt på denne enheten"; /* Resend activation instructions button */ -"subscription.add.device.resend.instructions" = "Resend Instructions"; +"subscription.add.device.resend.instructions" = "Send instruksjonene på nytt"; /* Add email to an existing subscription */ -"subscription.add.email" = "Add an email address to activate your subscription on your other devices. We’ll only use this address to verify your subscription."; +"subscription.add.email" = "Legg til en e-postadresse for å aktivere abonnementet på andre enheter du eier. Vi bruker denne adressen bare til å verifisere abonnementet."; /* View title for adding email to subscription */ -"subscription.add.email.title" = "Add Email"; - -/* Title for Alert messages */ -"subscription.alert.title" = "subscription.alert.title"; +"subscription.add.email.title" = "Legg til e-postadresse"; /* Subscription availability message on Apple devices */ -"subscription.available.apple" = "Privacy Pro is available on any device signed in to the same Apple ID."; +"subscription.available.apple" = "Privacy Pro er tilgjengelig på alle enheter som er logget inn på samme Apple ID."; /* Text for the manage billing page */ "subscription.billing.google.text" = "Abonnementet ditt ble kjøpt gjennom Google Play Store. Hvis du vil fornye abonnementet, må du åpne abonnementsinnstillingene for Google Play Store på en enhet som er logget på den samme Google-kontoen som du brukte til å kjøpe abonnementet."; @@ -2385,89 +2550,86 @@ /* Title for the manage billing page */ "subscription.billing.google.title" = "Abonnementer"; -/* Subscription annual billing period type */ -"subscription.billing.period.annual" = "annual"; - -/* Subscription monthly billing period type */ -"subscription.billing.period.monthly" = "monthly"; - /* Subscription Removal confirmation message */ -"subscription.cancel.message" = "Your subscription has been removed from this device."; +"subscription.cancel.message" = "Abonnementet ditt er fjernet fra denne enheten."; /* Change plan or cancel title */ -"subscription.change.plan" = "Update Plan or Cancel"; +"subscription.change.plan" = "Oppdater eller avslutt abonnementet"; /* Navigation Button for closing subscription view */ -"subscription.close" = "Close"; +"subscription.close" = "Lukk"; /* Title for Confirm messages */ -"subscription.confirm.title" = "Are you sure?"; +"subscription.confirm.title" = "Er du sikker?"; /* Header for section for activating subscription on other devices */ -"subscription.devices.header" = "Activate on Other Devices"; +"subscription.devices.header" = "Aktiver på andre enheter"; /* Footer for section for activating subscription on other devices when email was not yet added */ -"subscription.devices.no.email.footer" = "Add an optional email to your subscription or use your Apple ID to access Privacy Pro on other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.no.email.footer" = "Legg til en valgfri e-postadresse i abonnementet ditt for å få tilgang til Privacy Pro på andre enheter. **[Finn ut mer](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Footer for section for activating subscription on other devices when email is added */ -"subscription.devices.with.email.footer" = "Use this email to activate your subscription in Settings > Privacy Pro in the DuckDuckGo app on your other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.with.email.footer" = "Bruk denne e-postadressen til å aktivere abonnementet ditt i Innstillinger > Privacy Pro i DuckDuckGo-appen på de andre enhetene. **[Finn ut mer](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Alert content for not found subscription */ -"subscription.email.inactive.alert.message" = "The subscription associated with this email is no longer active."; +"subscription.email.inactive.alert.message" = "Abonnementet tilknyttet denne e-postadressen er ikke lenger aktiv."; /* Alert title for not found subscription */ -"subscription.email.inactive.alert.title" = "Subscription Not Found"; +"subscription.email.inactive.alert.title" = "Abonnementet ble ikke funnet"; /* Alert content for not found subscription */ -"subscription.expired.alert.message" = "The subscription associated with this Apple ID is no longer active."; +"subscription.expired.alert.message" = "Abonnementet tilknyttet denne Apple ID-en er ikke lenger aktiv."; /* FAQ Button */ -"subscription.faq" = "FAQs and Support"; +"subscription.faq" = "Vanlige spørsmål og kundestøtte"; /* FAQ Description */ -"subscription.faq.description" = "Get answers to frequently asked questions or contact Privacy Pro support from our help pages."; +"subscription.faq.description" = "Få svar på ofte stilte spørsmål eller kontakt Privacy Pro-støtte fra hjelpesidene våre."; + +/* Send Feedback Button */ +"subscription.feedback" = "Send tilbakemelding"; /* Cancel action for the existing subscription dialog */ -"subscription.found.cancel" = "Cancel"; +"subscription.found.cancel" = "Avbryt"; /* Restore action for the existing subscription dialog */ -"subscription.found.restore" = "Restore"; +"subscription.found.restore" = "Gjenopprett"; /* Message for the existing subscription dialog */ -"subscription.found.text" = "We found a subscription associated with this Apple ID."; +"subscription.found.text" = "Vi fant et abonnement tilknyttet denne Apple ID-en."; /* Title for the existing subscription dialog */ -"subscription.found.title" = "Subscription Found"; +"subscription.found.title" = "Abonnementet er funnet"; /* Help and support Section header */ -"subscription.help" = "Help and support"; +"subscription.help" = "Hjelp og kundestøtte"; /* Manage Plan header */ -"subscription.manage.plan" = "Manage Plan"; +"subscription.manage.plan" = "Administrer abonnement"; /* Header for the subscription section */ -"subscription.manage.title" = "Subscription"; +"subscription.manage.title" = "Abonnement"; /* Alert content for not found subscription */ -"subscription.notFound.alert.message" = "There is no subscription associated with this Apple ID."; +"subscription.notFound.alert.message" = "Det finnes ikke noe abonnement tilknyttet denne Apple ID-en."; /* Alert title for not found subscription */ -"subscription.notFound.alert.title" = "Subscription Not Found"; +"subscription.notFound.alert.title" = "Abonnementet ble ikke funnet"; /* View plans button text */ -"subscription.notFound.view.plans" = "View Plans"; +"subscription.notFound.view.plans" = "Se abonnementer"; /* Hero Text for Personal information removal */ -"subscription.pir.hero" = "Activate Privacy Pro on desktop to set up Personal Information Removal"; +"subscription.pir.hero" = "Aktiver Privacy Pro på en datamaskin for å konfigurere Personal Information Removal"; /* Description on how to use Personal information removal in desktop. The first placeholder references a location in the Desktop application. Privacy Pro>, and the second, the menu entry. i.e. */ -"subscription.pir.heroText" = "In the DuckDuckGo browser for desktop, go to %1$@ and click %2$@ to get started."; +"subscription.pir.heroText" = "I DuckDuckGo-nettleseren for datamaskin går du til %1$@ og klikker på %2$@ for å komme i gang."; /* Settings references a menu in the Desktop app, Privacy Pro, references our product name */ -"subscription.pir.heroTextLocation" = "Settings > Privacy Pro"; +"subscription.pir.heroTextLocation" = "Innstillinger > Privacy Pro"; /* Menu item for enabling Personal Information Removal on Desktop */ -"subscription.pir.heroTextMenyEntry" = "I have a subscription"; +"subscription.pir.heroTextMenyEntry" = "Jeg har et abonnement"; /* Text for the 'macOS' button */ "subscription.pir.macos" = "Mac"; @@ -2476,28 +2638,28 @@ "subscription.pir.windows" = "Windows"; /* Progress view title when completing the purchase */ -"subscription.progress.view.completing.purchase" = "Completing purchase..."; +"subscription.progress.view.completing.purchase" = "Fullfører kjøpet..."; /* Progress view title when starting the purchase */ -"subscription.progress.view.purchasing.subscription" = "Purchase in progress..."; +"subscription.progress.view.purchasing.subscription" = "Kjøpet behandles..."; /* Progress view title when restoring past subscription purchase */ -"subscription.progress.view.restoring.subscription" = "Restoring subscription..."; +"subscription.progress.view.restoring.subscription" = "Gjenoppretter abonnementet..."; /* Remove from this device button */ -"subscription.remove.from.device.button" = "Remove From This Device"; +"subscription.remove.from.device.button" = "Fjern fra denne enheten"; /* Remove from device confirmation dialog text */ -"subscription.remove.from.device.text" = "You will no longer be able to access your Privacy Pro subscription on this device. This will not cancel your subscription, and it will remain active on your other devices."; +"subscription.remove.from.device.text" = "Du får ikke lenger tilgang til Privacy Pro-abonnementet ditt på denne enheten. Dette avslutter ikke abonnementet, det er fortsatt aktivt på de andre enhetene."; /* Remove from device confirmation dialog title */ -"subscription.remove.from.device.title" = "Remove from this device?"; +"subscription.remove.from.device.title" = "Vil du fjerne det fra denne enheten?"; /* Remove subscription button text */ -"subscription.remove.subscription" = "Remove Subscription"; +"subscription.remove.subscription" = "Fjern abonnement"; /* Remove subscription cancel button text */ -"subscription.remove.subscription.cancel" = "Cancel"; +"subscription.remove.subscription.cancel" = "Avbryt"; /* Button text for general error message */ "subscription.restore.backend.error.button" = "Tilbake til Innstillinger"; @@ -2509,28 +2671,43 @@ "subscription.restore.backend.error.title" = "Noe gikk galt"; /* Alert for general error message */ -"subscription.restore.general.error.message" = "The App Store was unable to process your purchase. Please try again later."; +"subscription.restore.general.error.message" = "App Store kunne ikke behandle kjøpet ditt. Prøv igjen senere."; /* Alert for general error title */ -"subscription.restore.general.error.title" = "Something Went Wrong"; +"subscription.restore.general.error.title" = "Noe gikk galt"; /* Alert button text for restored purchase alert */ "subscription.restore.success.alert.button" = "OK"; /* Alert message for restored purchase */ -"subscription.restore.success.alert.message" = "Your purchases have been restored."; +"subscription.restore.success.alert.message" = "Kjøpene dine er gjenopprettet."; /* Alert title for restored purchase */ -"subscription.restore.success.alert.title" = "You’re all set."; +"subscription.restore.success.alert.title" = "Alt i orden."; + +/* Subtitle in header when subscribed */ +"subscription.subscribed" = "Abonnerer"; /* Subscription Expired Data. This reads as 'Your subscription expired on (date)' */ "subscription.subscription.expired.caption" = "Abonnementet ditt utløp den %@"; -/* Subscription expiration info. This reads as 'Your (monthly or annual) subscription expires on (date)' */ -"subscription.subscription.expiring.caption" = "Your %1$@ subscription expires on %2$@."; +/* Monthly subscription expiration info where parameter is expiration date. This reads as 'Your monthly subscription expires on (date)' */ +"subscription.subscription.expiring.monthly.caption" = "Månedsabonnementet ditt utløper %@."; + +/* Unknown period subscription expiration info where parameter is expiration date. This reads as 'Your subscription expires on (date)' */ +"subscription.subscription.expiring.unknown.caption" = "Abonnementet ditt utløper %@."; + +/* Annual subscription expiration info where parameter is expiration date. This reads as 'Your annual subscription expires on (date)' */ +"subscription.subscription.expiring.yearly.caption" = "Årsabonnementet ditt utløper %@."; + +/* Monthly subscription renewal info where parameter is renewal date. This reads as 'Your monthly subscription renews on (date)' */ +"subscription.subscription.renewing.monthly.caption" = "Månedsabonnementet ditt fornyes %@."; + +/* Unknown period subscription renewal info where parameter is renewal date. This reads as 'Your subscription renews on (date)' */ +"subscription.subscription.renewing.unknown.caption" = "Abonnementet ditt fornyes %@."; -/* Subscription renewal info. This reads as 'Your (monthly or annual) subscription renews on (date)' */ -"subscription.subscription.renewing.caption" = "Your %1$@ subscription renews on %2$@."; +/* Annual subscription renewal info where parameter is renewal date. This reads as 'Your annual subscription renews on (date)' */ +"subscription.subscription.renewing.yearly.caption" = "Årsabonnementet ditt fornyes %@."; /* Navigation bar Title for subscriptions */ "subscription.title" = "Privacy Pro"; @@ -2677,118 +2854,118 @@ "voiceSearch.footer.note.old" = "Lyd behandles på enheten. Den lagres ikke og deles ikke med noen, heller ikke med DuckDuckGo."; /* Cancel action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.cancel" = "Dismiss"; +"vpn.access-revoked.alert.action.cancel" = "Avvis"; /* Primary action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.subscribe" = "Subscribe"; +"vpn.access-revoked.alert.action.subscribe" = "Abonner"; /* Alert message for the alert when the Privacy Pro subscription expiress */ -"vpn.access-revoked.alert.message" = "Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"vpn.access-revoked.alert.message" = "Abonner på Privacy Pro for å koble til DuckDuckGo-VPN igjen."; /* Alert title for the alert when the Privacy Pro subscription expires */ -"vpn.access-revoked.alert.title" = "VPN disconnected due to expired subscription"; +"vpn.access-revoked.alert.title" = "VPN ble frakoblet på grunn av et utløpt abonnement"; /* Title for the VPN widget onboarding screen */ "vpn.addWidget.settings.title" = "Finn og velg DuckDuckGo. Sveip deretter til VPN og velg Legg til widget."; /* Title for the Cancel button of the VPN feedback form */ -"vpn.feedback-form.button.cancel" = "Cancel"; +"vpn.feedback-form.button.cancel" = "Avbryt"; /* Title for the Done button of the VPN feedback form */ -"vpn.feedback-form.button.done" = "Done"; +"vpn.feedback-form.button.done" = "Ferdig"; /* Title for the Submit button of the VPN feedback form */ -"vpn.feedback-form.button.submit" = "Submit"; +"vpn.feedback-form.button.submit" = "Send"; /* Title for the Submitting state of the VPN feedback form */ -"vpn.feedback-form.button.submitting" = "Submitting…"; +"vpn.feedback-form.button.submitting" = "Sender inn…"; /* Title for the browser crash/freeze category of the VPN feedback form */ -"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN causes browser to crash or freeze"; +"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN forårsaker at nettleseren krasjer eller fryser"; /* Title for the 'VPN fails to connect' category of the VPN feedback form */ -"vpn.feedback-form.category.fails-to-connect" = "VPN fails to connect"; +"vpn.feedback-form.category.fails-to-connect" = "VPN kobler ikke til"; /* Title for the 'VPN feature request' category of the VPN feedback form */ -"vpn.feedback-form.category.feature-request" = "VPN feature request"; +"vpn.feedback-form.category.feature-request" = "Forespørsel om VPN-funksjon"; /* Title for the category 'VPN causes issues with other apps or websites' category of the VPN feedback form */ -"vpn.feedback-form.category.issues-with-apps" = "VPN causes issues with other apps or websites"; +"vpn.feedback-form.category.issues-with-apps" = "VPN forårsaker problemer med andre apper eller nettsteder"; /* Title for the local device connectivity category of the VPN feedback form */ -"vpn.feedback-form.category.local-device-connectivity" = "VPN won't let me connect to local device"; +"vpn.feedback-form.category.local-device-connectivity" = "VPN hindrer meg i å koble til en lokal enhet"; /* Title for the 'other VPN feedback' category of the VPN feedback form */ -"vpn.feedback-form.category.other" = "Other VPN feedback"; +"vpn.feedback-form.category.other" = "Annen tilbakemelding om VPN"; /* Title for the category selection state of the VPN feedback form */ -"vpn.feedback-form.category.select-category" = "Select a category"; +"vpn.feedback-form.category.select-category" = "VELG EN KATEGORI"; /* Title for the 'VPN is too slow' category of the VPN feedback form */ -"vpn.feedback-form.category.too-slow" = "VPN connection is too slow"; +"vpn.feedback-form.category.too-slow" = "VPN-tilkoblingen er for treg"; /* Title for the 'unable to install' category of the VPN feedback form */ -"vpn.feedback-form.category.unable-to-install" = "Unable to install VPN"; +"vpn.feedback-form.category.unable-to-install" = "Kan ikke installere VPN"; /* Title for the feedback sent view description of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.description" = "Your feedback will help us improve the\nDuckDuckGo VPN."; +"vpn.feedback-form.sending-confirmation.description" = "Tilbakemeldingen din vil hjelpe oss å forbedre\nDuckDuckGo VPN."; /* Title for the feedback sending error text of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.error" = "We couldn't send your feedback right now, please try again."; +"vpn.feedback-form.sending-confirmation.error" = "Vi kunne ikke sende tilbakemeldingen din akkurat nå. Prøv igjen."; /* Title for the feedback sent view title of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.title" = "Thank you!"; +"vpn.feedback-form.sending-confirmation.title" = "Takk!"; /* Toast message when the VPN feedback form is submitted successfully */ -"vpn.feedback-form.submitted.message" = "Thank You! Feedback submitted."; +"vpn.feedback-form.submitted.message" = "Takk! Tilbakemeldingen er sendt."; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-1" = "Please describe what's happening, what you expected to happen, and the steps that led to the issue:"; +"vpn.feedback-form.text-1" = "Beskriv hva som skjer, hva du forventet skulle skje, og trinnene som førte til problemet:"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-2" = "In addition to the details entered into this form, your app issue report will contain:"; +"vpn.feedback-form.text-2" = "I tillegg til detaljene du skrev i dette skjemaet, inneholder rapporten om app-problemet:"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-3" = "• Whether specific DuckDuckGo features are enabled"; +"vpn.feedback-form.text-3" = "• Hvorvidt spesifikke DuckDuckGo-funksjoner er aktivert"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-4" = "• Aggregate DuckDuckGo app diagnostics"; +"vpn.feedback-form.text-4" = "• Samlet diagnostikk av DuckDuckGo-appen"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-5" = "By tapping \"Submit\" I agree that DuckDuckGo may use the information in this report for purposes of improving the app's features."; +"vpn.feedback-form.text-5" = "Ved å trykke på «Send inn» godtar jeg at DuckDuckGo kan bruke informasjonen i denne rapporten til å forbedre appens funksjoner."; /* Title for each screen of the VPN feedback form */ -"vpn.feedback-form.title" = "Help Improve the DuckDuckGo VPN"; +"vpn.feedback-form.title" = "Bidra til å forbedre DuckDuckGo VPN"; /* VPN settings screen cell text for adding the VPN widget to the home screen */ "vpn.settings.add.widget" = "Legg til VPN-widgeten på startskjermen"; /* Disclaimer for the DNS Server section on the DNS Server screen */ -"vpn.settings.dns.section-disclaimer" = "Using a custom DNS server can impact browsing speeds and expose your activity to third parties if the server isn't secure or reliable."; +"vpn.settings.dns.section-disclaimer" = "Bruk av en tilpasset DNS-server kan påvirke surfehastigheter og eksponere aktiviteten din til tredjeparter hvis serveren ikke er sikker eller pålitelig."; /* Header text for the DNS section on the VPN Settings screen */ "vpn.settings.dns.section-header" = "DNS"; /* Title for the Apply button on the DNS Server setting screen */ -"vpn.settings.dns.server.apply.button.title" = "Apply"; +"vpn.settings.dns.server.apply.button.title" = "Bruk"; /* Default value for the DNS Server row on the VPN Settings screen */ "vpn.settings.dns.server.default.value" = "DuckDuckGo"; /* Title for the IPv4 Address setting */ -"vpn.settings.dns.server.ipv4.title" = "IPv4 Address"; +"vpn.settings.dns.server.ipv4.title" = "IPv4-adresse"; /* Custom option for the DNS Server setting */ -"vpn.settings.dns.server.option.custom" = "Custom"; +"vpn.settings.dns.server.option.custom" = "Tilpasset"; /* Recommended option for the DNS Server setting */ -"vpn.settings.dns.server.option.default" = "DuckDuckGo (Recommended)"; +"vpn.settings.dns.server.option.default" = "DuckDuckGo (anbefalt)"; /* Title for the DNS Server setting screen */ -"vpn.settings.dns.server.screen.title" = "DNS Server"; +"vpn.settings.dns.server.screen.title" = "DNS-server"; /* Title for the DNS Server row on the VPN Settings screen */ -"vpn.settings.dns.server.title" = "DNS Server"; +"vpn.settings.dns.server.title" = "DNS-server"; /* Title for the button to enable push notifications in system settings */ "waitlist.allow-notifications" = "Tillat varsler"; diff --git a/DuckDuckGo/nb.lproj/OmniBar.strings b/DuckDuckGo/nb.lproj/OmniBar.strings index 58740d5a20..d0133cb02a 100644 --- a/DuckDuckGo/nb.lproj/OmniBar.strings +++ b/DuckDuckGo/nb.lproj/OmniBar.strings @@ -7,6 +7,9 @@ /* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "Dt8-We-5tV"; */ "Dt8-We-5tV.accessibilityLabel" = "Tøm tekst"; +/* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "gb5-wL-4OW"; */ +"gb5-wL-4OW.accessibilityLabel" = "Tøm tekst"; + /* Class = "UIButton"; accessibilityLabel = "Share"; ObjectID = "hor-Jd-3kw"; */ "hor-Jd-3kw.accessibilityLabel" = "Del"; diff --git a/DuckDuckGo/nb.lproj/Settings.strings b/DuckDuckGo/nb.lproj/Settings.strings index 95cb40ddef..a0a582594b 100644 --- a/DuckDuckGo/nb.lproj/Settings.strings +++ b/DuckDuckGo/nb.lproj/Settings.strings @@ -10,9 +10,6 @@ /* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "5YD-Od-uDU"; */ "5YD-Od-uDU.text" = "Slett data automatisk"; -/* Class = "UIButton"; normalTitle = "Settings"; ObjectID = "8YG-7Q-Czd"; */ -"8YG-7Q-Czd.normalTitle" = "Innstillinger"; - /* Class = "UILabel"; text = "App Exit, Inactive for 1 Hour"; ObjectID = "9EC-lx-77p"; */ "9EC-lx-77p.text" = "når appen lukkes eller har vært inaktiv i 1 time"; @@ -76,9 +73,6 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Ubeskyttede nettsteder"; -/* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ -"ssa-zd-L3T.title" = "Tekststørrelse"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Vis tastatur i"; diff --git a/DuckDuckGo/nl.lproj/DaxOnboarding.strings b/DuckDuckGo/nl.lproj/DaxOnboarding.strings index 771c90773b..f0395e53d7 100644 --- a/DuckDuckGo/nl.lproj/DaxOnboarding.strings +++ b/DuckDuckGo/nl.lproj/DaxOnboarding.strings @@ -1,12 +1,6 @@ /* Class = "UIButton"; normalTitle = "Hide"; ObjectID = "hgy-75-OvJ"; */ "hgy-75-OvJ.normalTitle" = "Verbergen"; -/* Class = "UILabel"; text = "Welcome to\nDuckDuckGo!"; ObjectID = "Lvy-9G-3gr"; */ -"Lvy-9G-3gr.text" = "Welkom bij\nDuckDuckGo!"; - -/* Class = "UIButton"; normalTitle = "Let’s Do It!"; ObjectID = "oAv-Zt-uIt"; */ -"oAv-Zt-uIt.normalTitle" = "Laten we het doen!"; - /* Class = "UIButton"; normalTitle = "Button"; ObjectID = "zJt-F9-L48"; */ "zJt-F9-L48.normalTitle" = "Knop"; diff --git a/DuckDuckGo/nl.lproj/Localizable.strings b/DuckDuckGo/nl.lproj/Localizable.strings index c86a8c2907..08efcd53a0 100644 --- a/DuckDuckGo/nl.lproj/Localizable.strings +++ b/DuckDuckGo/nl.lproj/Localizable.strings @@ -341,7 +341,7 @@ "autofill.keep-enabled.alert.disable" = "Uitschakelen"; /* A link that takes the user to the DuckDuckGo help pages explaining password managers */ -"autofill.learn.more.link.title" = "Learn More"; +"autofill.learn.more.link.title" = "Meer informatie"; /* Button displayed after saving/updating an autofill login that takes the user to the saved login */ "autofill.login-save-action-button.toast" = "Weergeven"; @@ -977,9 +977,6 @@ /* ad = advertisment */ "dax.onboarding.home.subsequent" = "Je kunt het!\n\nDenk eraan: elke keer als je met mij browset, verliest een enge advertentie zijn vleugels. 👍"; -/* No comment provided by engineer. */ -"dax.onboarding.message" = "Internet kan best eng zijn.\n\nMaak je geen zorgen! Privé zoeken en browsen is eenvoudiger dan je denkt."; - /* No comment provided by engineer. */ "Debug" = "Fouten opsporen"; @@ -1080,7 +1077,7 @@ "duckPlayer.never.label" = "Nooit"; /* Body text for the modal feature explanation */ -"duckplayer.presentation.modal.body" = "Met Duck Player kun je YouTube bekijken in een soort bioscoopomgeving in DuckDuckGo, zonder gerichte advertenties. Wat je bekijkt heeft geen invloed op je aanbevelingen."; +"duckplayer.presentation.modal.body" = "Met Duck Player kun je YouTube bekijken in DuckDuckGo, zonder gerichte advertenties. Wat je bekijkt heeft geen invloed op je aanbevelingen."; /* Button that will dismiss the modal */ "duckplayer.presentation.modal.dismiss-button" = "Ik snap het!"; @@ -1088,17 +1085,23 @@ /* Two line title (separated by \n) for the feature explanation */ "duckplayer.presentation.modal.title" = "Te veel advertenties op YouTube? Probeer Duck Player!"; +/* Two line title (separated by \n) for the feature explanation */ +"duckplayer.presentation.modal.title.youtube" = "Te veel advertenties op YouTube? Niet met Duck Player!"; + /* Footer label in the settings screen for Duck Player */ "duckplayer.settings.footer" = "DuckDuckGo biedt alle Privacy Essentials die je nodig hebt om jezelf te beschermen terwijl je op internet surft."; /* Text explaining what Duck Player is in the settings screen. */ -"duckplayer.settings.info-text" = "Duck Player biedt puur kijkplezier zonder gepersonaliseerde advertenties en voorkomt dat de dingen die je bekijkt je YouTube-aanbevelingen beïnvloeden."; +"duckplayer.settings.info-text" = "Met Duck Player kun je YouTube bekijken in DuckDuckGo, zonder gerichte advertenties. Wat je bekijkt heeft geen invloed op je aanbevelingen."; /* Button that takes the user to learn more about Duck Player. */ "duckplayer.settings.learn-more" = "Meer informatie"; +/* Settings screen cell text for DuckPlayer settings to open in new tab */ +"duckplayer.settings.open-new-tab-label" = "Open Duck Player in een nieuw tabblad"; + /* Settings screen cell text for DuckPlayer settings */ -"duckplayer.settings.open-videos-in" = "Video's openen in Duck Player"; +"duckplayer.settings.open-videos-in" = "YouTube-video's openen in Duck Player"; /* Settings screen cell text for DuckPlayer settings */ "duckplayer.settings.title" = "Duck Player"; @@ -1169,9 +1172,6 @@ /* Title for prompt to sign up for email protection */ "email.signup-prompt.title" = "Je e-mailadres verbergen en\n trackers blokkeren"; -/* No comment provided by engineer. */ -"empty!" = "leeg!"; - /* Empty list state placholder */ "empty.bookmarks" = "Nog geen bladwijzers toegevoegd"; @@ -1322,6 +1322,39 @@ /* Confirmation button */ "feedback.form.submit" = "Verzenden"; +/* Title of the feedback form */ +"feedback.form.title" = "Help Privacy Pro te verbeteren"; + +/* Category for Identity Theft Restoration feedback */ +"feedback.general.category.itr" = "Identity Theft Restoration"; + +/* Category for Personal Info Removal feedback */ +"feedback.general.category.pir" = "Verwijdering van persoonlijke informatie"; + +/* Category for subscription and payments feedback */ +"feedback.general.category.ppro" = "Abonnementen en betalingen"; + +/* Prompt to select a category for general feedback */ +"feedback.general.category.select" = "SELECTEER EEN CATEGORIE"; + +/* Category for VPN feedback */ +"feedback.general.category.vpn" = "VPN"; + +/* Category for access code issues */ +"feedback.itr.category.access-code" = "Probleem met toegangscode"; + +/* Category for issues contacting an advisor */ +"feedback.itr.category.cant-contact-advisor" = "Kan geen contact opnemen met de adviseur"; + +/* Prompt to select a category for Identity Theft Restoration feedback */ +"feedback.itr.category.select" = "SELECTEER EEN CATEGORIE"; + +/* Category for other Identity Theft Restoration issues */ +"feedback.itr.category.something-else" = "Iets anders"; + +/* Category for unhelpful advisor calls */ +"feedback.itr.category.unhelpful" = "Telefoontje naar adviseur hielp niet"; + /* No comment provided by engineer. */ "feedback.negative.form.genericPlaceholder" = "Wees zo specifiek mogelijk"; @@ -1361,6 +1394,24 @@ /* No comment provided by engineer. */ "feedback.performance.slowLoading" = "Webpagina's of zoekresultaten worden langzaam geladen"; +/* Category for when scan finds incorrect records */ +"feedback.pir.category.not-me" = "De scan vond gegevens die niet van mij zijn"; + +/* Category for when scan doesn't find info on a specific site */ +"feedback.pir.category.nothing-on-site" = "De scan heeft mijn gegevens niet gevonden op een specifieke site"; + +/* Category for other Personal Info Removal issues */ +"feedback.pir.category.other" = "Iets anders"; + +/* Category for when the removal process is stuck */ +"feedback.pir.category.removal-stuck" = "Het verwijderingsproces is vastgelopen"; + +/* Category for when the scan is stuck */ +"feedback.pir.category.scan-stuck" = "De scan naar gegevens is vastgelopen"; + +/* Prompt to select a category for Personal Info Removal feedback */ +"feedback.pir.category.select" = "SELECTEER EEN CATEGORIE"; + /* Header above input field */ "feedback.positive.form.header" = "Details delen"; @@ -1379,6 +1430,15 @@ /* Button encouraging uses to share details aboout their feedback */ "feedback.positive.submit" = "Details delen"; +/* Category for other Privacy Pro issues */ +"feedback.ppro.category.other" = "Iets anders"; + +/* Category for one-time password issues */ +"feedback.ppro.category.otp" = "Probleem met eenmalig wachtwoord"; + +/* Prompt to select a category for Privacy Pro feedback */ +"feedback.ppro.category.select" = "SELECTEER EEN CATEGORIE"; + /* No comment provided by engineer. */ "feedback.start.footer" = "Jouw anonieme feedback is belangrijk voor ons."; @@ -1529,12 +1589,6 @@ /* No comment provided by engineer. */ "keyCommandShowAllTabs" = "Alle tabbladen weergeven"; -/* Please preserve newline character */ -"launchscreenWelcomeMessage" = "Welkom bij\nDuckDuckGo!"; - -/* No comment provided by engineer. */ -"LOREM IPSUM" = "LOREM IPSUM"; - /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "DuckDuckGo voor Mac heeft de snelheid die je nodig hebt en de browsefuncties die je verwacht, en zit boordevol met onze allerbeste privacy essentials."; @@ -1586,62 +1640,83 @@ /* Edit button */ "navigation.title.edit" = "Bewerken"; -/* String indicating NetP is connected when viewed from the settings screen */ -"netP.cell.connected" = "Connected"; - -/* String indicating NetP is disconnected when viewed from the settings screen */ -"netP.cell.disconnected" = "Not connected"; - -/* Title for the DuckDuckGo VPN feature in settings */ -"netP.settings.title" = "VPN"; +/* String indicating NetP is snoozing when viewed from the settings screen */ +"netP.cell.snoozing" = "Sluimeren"; /* Title for the DuckDuckGo VPN feature */ "netP.title" = "DuckDuckGo VPN"; +/* Button title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.action" = "Widget toevoegen"; + +/* Message for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.message" = "Schakel de VPN rechtstreeks vanaf het startscherm in en uit."; + +/* Title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.title" = "VPN-widget toevoegen"; + /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "De VPN-verbinding is verbroken omdat het abonnement is verlopen. Abonneer je op Privacy Pro om opnieuw verbinding te maken met DuckDuckGo VPN."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "De netwerkbeveiliging kan geen verbinding maken. Probeer het later opnieuw."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "DuckDuckGo VPN kon geen verbinding maken. Probeer het later opnieuw."; /* Title for the VPN FAQ screen. */ "network.protection.faq.title" = "DuckDuckGo VPN FAQ"; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "De netwerkbeveiliging is onderbroken. Er wordt geprobeerd opnieuw verbinding te maken ..."; +/* Message for tooltip about geoswitching */ +"network.protection.geoswitching.tip.message" = "Je kunt je VPN-locatie aanpassen door verbinding te maken met een van onze servers wereldwijd."; + +/* Title for tooltip about geoswitching */ +"network.protection.geoswitching.tip.title" = "Wijzig je locatie"; + +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "DuckDuckGo VPN werd onderbroken. Er wordt geprobeerd opnieuw verbinding te maken ..."; /* Message for the network protection invite dialog */ -"network.protection.invite.dialog.message" = "Enter your invite code to get started."; +"network.protection.invite.dialog.message" = "Voer je uitnodigingscode in om te beginnen."; /* Title for the network protection invite screen */ "network.protection.invite.dialog.title" = "Je bent uitgenodigd om DuckDuckGo VPN uit te proberen"; /* Prompt for the network protection invite code text field */ -"network.protection.invite.field.prompt" = "Invite Code"; +"network.protection.invite.field.prompt" = "Uitnodigingscode"; /* Message for the network protection invite success view */ -"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; +"network.protection.invite.success.message" = "Verberg je locatie voor websites en verberg je online activiteiten voor internetproviders en anderen in je netwerk."; /* Title for the network protection invite success view */ -"network.protection.invite.success.title" = "Success! You’re in."; +"network.protection.invite.success.title" = "Gelukt! Je bent binnen."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; /* Title text for an iOS quick action that opens VPN settings */ "network.protection.quick-action.open-vpn" = "VPN openen"; +/* Button title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.action" = "Meer informatie"; + +/* Message for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.message" = "Je kunt sites of apps gebruiken die VPN-verkeer blokkeren door de VPN-verbinding in sluimerstand te zetten."; + +/* Title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.title" = "VPN-conflicten vermijden"; + +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN in sluimerstand voor %@"; + /* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ "network.protection.status.connected.format" = "Verbonden · %@"; /* The label for the NetP VPN when connecting */ -"network.protection.status.connecting" = "Connecting..."; +"network.protection.status.connecting" = "Er wordt verbinding gemaakt ..."; /* The label for the NetP VPN when disconnected */ -"network.protection.status.disconnected" = "Not connected"; +"network.protection.status.disconnected" = "Niet verbonden"; /* The label for the NetP VPN when disconnecting */ -"network.protection.status.disconnecting" = "Disconnecting..."; +"network.protection.status.disconnecting" = "De verbinding wordt verbroken ..."; /* Message label text for the status view when VPN is disconnected */ "network.protection.status.header.message.off" = "Maak verbinding om het internetverkeer van al je\napparaten te beveiligen."; @@ -1655,26 +1730,41 @@ /* Header title label text for the status view when VPN is connected */ "network.protection.status.header.title.on" = "DuckDuckGo VPN is ingeschakeld"; +/* Header title label text for the status view when VPN is snoozing */ +"network.protection.status.header.title.snoozed" = "DuckDuckGo VPN is gepauzeerd"; + /* The status view 'Share Feedback' button which is shown inline on the status view after the temporary free use footer text */ -"network.protection.status.menu.share.feedback" = "Share Feedback"; +"network.protection.status.menu.share.feedback" = "Feedback delen"; + +/* The label for the NetP VPN when paused */ +"network.protection.status.paused" = "Gepauzeerd"; + +/* The label for when NetP VPN is snoozing plus the length of time remaining formatted as '0:00' */ +"network.protection.status.snoozing.format" = "Sluimeren, %@ resterend"; + +/* Snooze button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.snooze" = "20 minuten sluimeren"; + +/* Wake Up button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.wake-up" = "Inschakelen"; /* Connection details label shown in NetworkProtection's status view. */ -"network.protection.status.view.connection.details" = "Connection Details"; +"network.protection.status.view.connection.details" = "Verbindingsdetails"; /* Custom DNS label shown in NetworkProtection's status view. */ -"network.protection.status.view.custom.dns" = "DNS Server"; +"network.protection.status.view.custom.dns" = "DNS-server"; /* Generic connection failed error message shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.message" = "Please try again later."; +"network.protection.status.view.error.connection.failed.message" = "Probeer het later opnieuw."; /* Generic connection failed error title shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; +"network.protection.status.view.error.connection.failed.title" = "Kan geen verbinding maken."; /* IP Address label shown in NetworkProtection's status view. */ -"network.protection.status.view.ip.address" = "IP Address"; +"network.protection.status.view.ip.address" = "IP-adres"; /* Location label shown in NetworkProtection's status view. */ -"network.protection.status.view.location" = "Location"; +"network.protection.status.view.location" = "Locatie"; /* Label shown on the title of the settings section in NetworkProtection's status view. */ "network.protection.status.view.settings.section.title" = "Beheren"; @@ -1682,12 +1772,15 @@ /* Title label text for the status view when netP is disconnected */ "network.protection.status.view.title" = "VPN"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "De netwerkbeveiliging is ingeschakeld. Je locatie en online-activiteiten zijn beschermd."; +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "DuckDuckGo VPN is ingeschakeld. Je locatie en online-activiteiten zijn beschermd."; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Apparaatverkeer routeren via %@."; +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "De VPN-sluimerstand is beëindigd. Apparaatverkeer wordt gerouteerd via %@."; + /* Title for the button to link to the iOS app settings and enable notifications app-wide. */ "network.protection.turn.on.notifications.button.title" = "Meldingen inschakelen"; @@ -1698,13 +1791,13 @@ "network.protection.vpn.about" = "Over"; /* Section header for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.section.header" = "Notifications"; +"network.protection.vpn.alerts.section.header" = "Meldingen"; /* List section footer for the toggle for VPN alerts. */ "network.protection.vpn.alerts.toggle.section.footer" = "Ontvang een melding als je verbinding wegvalt of de VPN-status verandert."; /* Title for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.toggle.title" = "VPN Notifications"; +"network.protection.vpn.alerts.toggle.title" = "VPN-meldingen"; /* Title for the data volume section in the VPN status screen */ "network.protection.vpn.data-volume" = "Datavolume"; @@ -1713,13 +1806,13 @@ "network.protection.vpn.exclude.local.networks.setting.footer" = "Laat lokaal verkeer de VPN omzeilen en maak verbinding met apparaten in je lokale netwerk, zoals een printer."; /* Header text for the Exclude Local Networks setting item. */ -"network.protection.vpn.exclude.local.networks.setting.header" = "General"; +"network.protection.vpn.exclude.local.networks.setting.header" = "Algemeen"; /* Title for the Exclude Local Networks setting item. */ "network.protection.vpn.exclude.local.networks.setting.title" = "Lokale netwerken uitsluiten"; /* Title for the VPN Location screen's All Countries section. */ -"network.protection.vpn.location.all.countries.section.title" = "All Countries"; +"network.protection.vpn.location.all.countries.section.title" = "Alle landen"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.connected" = "Verbonden locatie"; @@ -1734,7 +1827,7 @@ "network.protection.vpn.location.recommended.section.footer" = "Maak automatisch verbinding met de dichtstbijzijnde server die we kunnen vinden."; /* Title for the VPN Location screen's Recommended section. */ -"network.protection.vpn.location.recommended.section.title" = "Recommended"; +"network.protection.vpn.location.recommended.section.title" = "Aanbevolen"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.selected" = "Geselecteerde locatie"; @@ -1752,13 +1845,13 @@ "network.protection.vpn.preferred.location.nearest" = "Dichtstbijzijnde locatie"; /* Title for the Preferred Location VPN Settings item. */ -"network.protection.vpn.preferred.location.title" = "Preferred Location"; +"network.protection.vpn.preferred.location.title" = "Voorkeurslocatie"; /* Footer text for the DNS server setting item. */ -"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo routes DNS queries through our DNS servers so your internet provider can't see what websites you visit."; +"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo routeert DNS-zoekopdrachten via onze DNS-servers zodat je internetprovider niet kan zien welke websites je bezoekt."; /* Title for the FAQ row in the VPN status screen. */ -"network.protection.vpn.settings.faq" = "FAQs and Support"; +"network.protection.vpn.settings.faq" = "Veelgestelde vragen en ondersteuning"; /* Title for the feedback row in the VPN status screen. */ "network.protection.vpn.settings.share-feedback" = "VPN-feedback delen"; @@ -1940,6 +2033,66 @@ /* Deactivate button */ "pm.deactivate" = "Deactiveren"; +/* Label for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.label" = "E-mailadres (optioneel)"; + +/* Placeholder for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.placeholder" = "name@email.com"; + +/* Placeholder for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.placeholder" = "Deel je feedback met ons …"; + +/* Title for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.title" = "Algemene feedback"; + +/* Title for the ITR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-itr-problem.title" = "Identity Theft Restoration"; + +/* Title for the PIR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-pir-problem.title" = "Personal Information Removal"; + +/* Title for the Subscriptions and Payments category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-ppro-problem.title" = "Abonnementen en betalingen"; + +/* Placeholder for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.placeholder" = "Vertel ons wat er aan de hand is …"; + +/* Title for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.title" = "Een probleem melden"; + +/* Title for the VPN category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-vpn-problem.title" = "VPN"; + +/* Placeholder for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.placeholder" = "Welke functie wil je zien?"; + +/* Title for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.title" = "Functieverzoek"; + +/* Title for the category selection section in the Privacy Pro feedback form */ +"ppro.feedback-form.select-category.title" = "SELECTEER EEN CATEGORIE"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-1" = "Heb je een probleem gevonden dat niet in ons [helpcentrum](duck://) wordt besproken? We willen er zeker meer over weten.\n\nGeef een e-mailadres op als je wilt dat we contact met je opnemen over dit probleem (het is mogelijk dat we niet op alle problemen kunnen reageren):"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-2" = "Naast de gegevens die je hierboven invult, sturen we ook geanonimiseerde informatie mee met je feedback:"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-3" = "• Of sommige browserfuncties actief zijn"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-4" = "• Samengevoegde app-diagnostiek (bijv. foutcodes)"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-5" = "Door op 'Verzenden' te tikken, geef je akkoord dat DuckDuckGo de verzonden informatie mag gebruiken om de app te verbeteren."; + +/* Title for the text box in the Privacy Pro feedback form */ +"ppro.feedback-form.text-box.title" = "Feedback"; + +/* Title for the Privacy Pro feedback form */ +"ppro.feedback-form.title" = "Verstuur feedback"; + /* Button title for sync bookmarks limits exceeded warning to go to manage bookmarks */ "prefrences.sync.bookmarks-limit-exceeded-action" = "Bladwijzers beheren"; @@ -2029,6 +2182,18 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Favorieten"; +/* Name of the option the user can chose to give general browser feedback */ +"send.browser.feedback.general-feedback" = "Algemene feedback"; + +/* Name of the option the user can chose to give browser feedback about a problem they enountered */ +"send.browser.feedback.report-problem" = "Een probleem melden"; + +/* Name of the option the user can chose to give browser feedback about a feature they would like */ +"send.browser.feedback.request-feature" = "Een functie aanvragen"; + +/* Title of the picker where the user can chose the category of the feedback they want ot send. */ +"send.browser.feedback.select-category" = "SELECTEER EEN CATEGORIE"; + /* Settings cell for About DDG */ "settings.about.ddg" = "Over DuckDuckGo"; @@ -2084,6 +2249,9 @@ /* Section footer Autolock description */ "settings.autolock.description" = "Als Touch ID, Face ID of een systeemwachtwoord is ingeschakeld, word je gevraagd om de app te ontgrendelen als je deze opent."; +/* Settings cell for Browser Feedback */ +"settings.browser.feedback" = "Browserfeedback"; + /* Settings screen cell text for Automatically Clearing Data */ "settings.clear.data" = "Gegevens automatisch wissen"; @@ -2182,7 +2350,7 @@ "settings.ppro" = "Privacy Pro"; /* Title for Link in the Footer of Privacy Pro section */ -"settings.ppro.footer" = "Privacy Policy and Terms of Service"; +"settings.ppro.footer" = "Privacybeleid en gebruiksvoorwaarden"; /* Settings screen cell for long press previews */ "settings.previews" = "Voorbeeldweergave bij lang indrukken"; @@ -2208,23 +2376,20 @@ /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Positie van adresbalk instellen"; -/* Subscription activation pending description */ -"settings.subscription.activation.pending.description" = "This is taking longer than usual, please check back later."; +/* Privacy pro description subtitle in settings when the is activating */ +"settings.subscription.activating" = "Bezig met activeren"; -/* Subscription activation pending title */ -"settings.subscription.activation.pending.title" = "Je abonnement wordt geactiveerd"; +/* Subscription activation pending description */ +"settings.subscription.activation.pending.description" = "Dit duurt langer dan normaal, kom later nog eens terug."; /* Data Broker protection cell subtitle for privacy pro */ -"settings.subscription.DBP.subtitle" = "Remove your info from sites that sell it"; +"settings.subscription.DBP.subtitle" = "Verwijder je gegevens van sites die ze verkopen"; /* Data Broker protection cell title for privacy pro */ "settings.subscription.DBP.title" = "Personal Information Removal"; -/* Privacy pro description subtext */ -"settings.subscription.description" = "Meer naadloze privacy met drie nieuwe beschermingen:"; - /* I have a Subscription button text for privacy pro */ -"settings.subscription.existing.subscription" = "I Have a Subscription"; +"settings.subscription.existing.subscription" = "Ik heb een abonnement"; /* Subscription expired description */ "settings.subscription.expired.comment" = "Abonneer je opnieuw om Privacy Pro te blijven gebruiken"; @@ -2232,23 +2397,26 @@ /* Subscription expired tittle message */ "settings.subscription.expired.title" = "Je Privacy Pro-abonnement is verlopen"; -/* Privacy pro features list */ -"settings.subscription.features" = " • VPN\n • Personal Information Removal\n • Identity Theft Restoration"; - /* Identity theft restoration cell subtitle for privacy pro */ -"settings.subscription.ITR.subtitle" = "If your identity is stolen, we'll help restore it"; +"settings.subscription.ITR.subtitle" = "Als je identiteit wordt gestolen, helpen we je deze te herstellen"; /* Identity theft restoration cell title for privacy pro */ "settings.subscription.ITR.title" = "Identity Theft Restoration"; /* Get Privacy Pro button text for privacy pro */ -"settings.subscription.learn.more" = "Get Privacy Pro"; +"settings.subscription.learn.more" = "Privacy Pro kopen"; /* Subscription Settings button text for privacy pro */ -"settings.subscription.manage" = "Subscription Settings"; +"settings.subscription.manage" = "Abonnementinstellingen"; + +/* Privacy Pro description subtitle in settings */ +"settings.subscription.row.description" = "Inclusief onze VPN en Identity Theft Restoration."; -/* Call to action title for Privacy Pro */ -"settings.subscription.subscribe" = "Subscribe to Privacy Pro"; +/* Call to action title for Privacy Pro settings */ +"settings.subscription.subscribe" = "Bescherm je verbinding en identiteit met Privacy Pro"; + +/* Privacy pro description subtitle in settings */ +"settings.subscription.us.description" = "Inclusief onze VPN, Personal Information Removal en Identity Theft Restoration."; /* VPN cell title for privacy pro */ "settings.subscription.VPN.title" = "VPN"; @@ -2317,67 +2485,64 @@ "siteFeedback.urlPlaceholder" = "Welke website is defect?"; /* Subscription Activation Info */ -"subscription.activate..header.description" = "Access your Privacy Pro subscription on this device via Apple ID or an email address."; +"subscription.activate..header.description" = "Krijg toegang tot je Privacy Pro-abonnement op dit apparaat via je Apple-ID of e-mailadres."; /* Button for adding email address to subscription */ -"subscription.activate.add.email.button" = "Add Email"; +"subscription.activate.add.email.button" = "E-mailadres toevoegen"; /* Apple ID option for activation */ -"subscription.activate.appleid" = "Apple ID"; +"subscription.activate.appleid" = "Apple-ID"; /* Button text for restoring purchase via Apple ID */ -"subscription.activate.appleid.button" = "Restore Purchase"; +"subscription.activate.appleid.button" = "Aankoop herstellen"; /* Description for Apple ID activation */ -"subscription.activate.appleid.description" = "Restore your purchase to activate your subscription on this device."; +"subscription.activate.appleid.description" = "Herstel je aankoop om je abonnement op dit apparaat te activeren."; /* Subscription Activation Info */ -"subscription.activate.description" = "Your subscription is automatically available in DuckDuckGo on any device signed in to your Apple ID."; +"subscription.activate.description" = "Je abonnement is automatisch beschikbaar in DuckDuckGo op elk apparaat dat is aangemeld bij je Apple-ID."; /* Button for editing email address added to subscription */ -"subscription.activate.edit.email.button" = "Edit Email"; +"subscription.activate.edit.email.button" = "E-mailadres bewerken"; /* View Title for editing your email account */ -"subscription.activate.edit.email.title" = "Edit Email"; +"subscription.activate.edit.email.title" = "E-mailadres bewerken"; /* Email option for activation */ -"subscription.activate.email" = "Email"; +"subscription.activate.email" = "E-mail"; /* Restore button title for Email */ -"subscription.activate.email.button" = "Enter Email"; +"subscription.activate.email.button" = "E-mailadres invoeren"; /* Description for Email activation */ -"subscription.activate.email.description" = "Use your email to activate your subscription on this device."; +"subscription.activate.email.description" = "Gebruik je e-mailadres om je abonnement op dit apparaat te activeren."; /* Activate subscription title */ "subscription.activate.email.title" = "Abonnement activeren"; /* Button title for cancelling email deletion */ -"subscription.activate.manage.email.cancel" = "Cancel"; +"subscription.activate.manage.email.cancel" = "Annuleren"; /* Button title for confirming email deletion */ "subscription.activate.manage.email.OK" = "OK"; /* Restore button title for AppleID */ -"subscription.activate.restore.apple" = "Restore Purchase"; +"subscription.activate.restore.apple" = "Aankoop herstellen"; /* Subscription Activation Title */ -"subscription.activate.title" = "Activate your subscription on this device"; +"subscription.activate.title" = "Activeer je abonnement op dit apparaat"; /* Resend activation instructions button */ -"subscription.add.device.resend.instructions" = "Resend Instructions"; +"subscription.add.device.resend.instructions" = "Instructies opnieuw verzenden"; /* Add email to an existing subscription */ -"subscription.add.email" = "Add an email address to activate your subscription on your other devices. We’ll only use this address to verify your subscription."; +"subscription.add.email" = "Voeg een e-mailadres toe om je abonnement op je andere apparaten te activeren. We zullen dit adres alleen gebruiken om je abonnement te verifiëren."; /* View title for adding email to subscription */ -"subscription.add.email.title" = "Add Email"; - -/* Title for Alert messages */ -"subscription.alert.title" = "subscription.alert.title"; +"subscription.add.email.title" = "E-mailadres toevoegen"; /* Subscription availability message on Apple devices */ -"subscription.available.apple" = "Privacy Pro is available on any device signed in to the same Apple ID."; +"subscription.available.apple" = "Privacy Pro is beschikbaar op elk apparaat dat is aangemeld bij dezelfde Apple-ID."; /* Text for the manage billing page */ "subscription.billing.google.text" = "Je abonnement is aangeschaft via de Google Play Store. Om je abonnement te verlengen, open je de abonnementsinstellingen van de Google Play Store op een apparaat dat is aangemeld bij hetzelfde Google-account waarmee je het abonnement hebt gekocht."; @@ -2385,89 +2550,86 @@ /* Title for the manage billing page */ "subscription.billing.google.title" = "Abonnementsformules"; -/* Subscription annual billing period type */ -"subscription.billing.period.annual" = "annual"; - -/* Subscription monthly billing period type */ -"subscription.billing.period.monthly" = "monthly"; - /* Subscription Removal confirmation message */ -"subscription.cancel.message" = "Your subscription has been removed from this device."; +"subscription.cancel.message" = "Je abonnement is van dit apparaat verwijderd."; /* Change plan or cancel title */ -"subscription.change.plan" = "Update Plan or Cancel"; +"subscription.change.plan" = "Plan bijwerken of annuleren"; /* Navigation Button for closing subscription view */ -"subscription.close" = "Close"; +"subscription.close" = "Sluiten"; /* Title for Confirm messages */ -"subscription.confirm.title" = "Are you sure?"; +"subscription.confirm.title" = "Weet je het zeker?"; /* Header for section for activating subscription on other devices */ -"subscription.devices.header" = "Activate on Other Devices"; +"subscription.devices.header" = "Activeren op andere apparaten"; /* Footer for section for activating subscription on other devices when email was not yet added */ -"subscription.devices.no.email.footer" = "Add an optional email to your subscription or use your Apple ID to access Privacy Pro on other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.no.email.footer" = "Voeg een optioneel e-mailadres toe aan je abonnement om toegang te krijgen tot Privacy Pro op andere apparaten. **[Meer informatie](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Footer for section for activating subscription on other devices when email is added */ -"subscription.devices.with.email.footer" = "Use this email to activate your subscription in Settings > Privacy Pro in the DuckDuckGo app on your other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.with.email.footer" = "Gebruik deze e-mail om je abonnement te activeren in 'Instellingen' > 'Privacy Pro' in de DuckDuckGo-app op je andere apparaten. **[Meer informatie](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Alert content for not found subscription */ -"subscription.email.inactive.alert.message" = "The subscription associated with this email is no longer active."; +"subscription.email.inactive.alert.message" = "Je abonnement dat aan dit e-mailadres is gekoppeld, is niet langer actief."; /* Alert title for not found subscription */ -"subscription.email.inactive.alert.title" = "Subscription Not Found"; +"subscription.email.inactive.alert.title" = "Abonnement niet gevonden"; /* Alert content for not found subscription */ -"subscription.expired.alert.message" = "The subscription associated with this Apple ID is no longer active."; +"subscription.expired.alert.message" = "Je abonnement dat aan deze Apple-ID is gekoppeld, is niet langer actief."; /* FAQ Button */ -"subscription.faq" = "FAQs and Support"; +"subscription.faq" = "Veelgestelde vragen en ondersteuning"; /* FAQ Description */ -"subscription.faq.description" = "Get answers to frequently asked questions or contact Privacy Pro support from our help pages."; +"subscription.faq.description" = "Krijg antwoorden op veelgestelde vragen of neem contact op met de klantenservice van Privacy Pro via onze helppagina's."; + +/* Send Feedback Button */ +"subscription.feedback" = "Verstuur feedback"; /* Cancel action for the existing subscription dialog */ -"subscription.found.cancel" = "Cancel"; +"subscription.found.cancel" = "Annuleren"; /* Restore action for the existing subscription dialog */ -"subscription.found.restore" = "Restore"; +"subscription.found.restore" = "Herstellen"; /* Message for the existing subscription dialog */ -"subscription.found.text" = "We found a subscription associated with this Apple ID."; +"subscription.found.text" = "We hebben een abonnement gevonden dat aan deze Apple-ID is gekoppeld."; /* Title for the existing subscription dialog */ -"subscription.found.title" = "Subscription Found"; +"subscription.found.title" = "Abonnement gevonden"; /* Help and support Section header */ -"subscription.help" = "Help and support"; +"subscription.help" = "Hulp en ondersteuning"; /* Manage Plan header */ -"subscription.manage.plan" = "Manage Plan"; +"subscription.manage.plan" = "Abonnement beheren"; /* Header for the subscription section */ -"subscription.manage.title" = "Subscription"; +"subscription.manage.title" = "Abonnement"; /* Alert content for not found subscription */ -"subscription.notFound.alert.message" = "There is no subscription associated with this Apple ID."; +"subscription.notFound.alert.message" = "Er is geen abonnement gekoppeld aan deze Apple-ID."; /* Alert title for not found subscription */ -"subscription.notFound.alert.title" = "Subscription Not Found"; +"subscription.notFound.alert.title" = "Abonnement niet gevonden"; /* View plans button text */ -"subscription.notFound.view.plans" = "View Plans"; +"subscription.notFound.view.plans" = "Abonnementen bekijken"; /* Hero Text for Personal information removal */ -"subscription.pir.hero" = "Activate Privacy Pro on desktop to set up Personal Information Removal"; +"subscription.pir.hero" = "Activeer Privacy Pro op je desktop om Personal Information Removal in te stellen."; /* Description on how to use Personal information removal in desktop. The first placeholder references a location in the Desktop application. Privacy Pro>, and the second, the menu entry. i.e. */ -"subscription.pir.heroText" = "In the DuckDuckGo browser for desktop, go to %1$@ and click %2$@ to get started."; +"subscription.pir.heroText" = "Ga in de DuckDuckGo-browser voor desktop naar '%1$@' en klik op '%2$@' om te beginnen."; /* Settings references a menu in the Desktop app, Privacy Pro, references our product name */ -"subscription.pir.heroTextLocation" = "Settings > Privacy Pro"; +"subscription.pir.heroTextLocation" = "'Instellingen' > 'Privacy Pro'"; /* Menu item for enabling Personal Information Removal on Desktop */ -"subscription.pir.heroTextMenyEntry" = "I have a subscription"; +"subscription.pir.heroTextMenyEntry" = "Ik heb een abonnement"; /* Text for the 'macOS' button */ "subscription.pir.macos" = "Mac"; @@ -2476,28 +2638,28 @@ "subscription.pir.windows" = "Windows"; /* Progress view title when completing the purchase */ -"subscription.progress.view.completing.purchase" = "Completing purchase..."; +"subscription.progress.view.completing.purchase" = "Je aankoop wordt afgerond ..."; /* Progress view title when starting the purchase */ -"subscription.progress.view.purchasing.subscription" = "Purchase in progress..."; +"subscription.progress.view.purchasing.subscription" = "Je aankoop wordt gedaan ..."; /* Progress view title when restoring past subscription purchase */ -"subscription.progress.view.restoring.subscription" = "Restoring subscription..."; +"subscription.progress.view.restoring.subscription" = "Je abonnement wordt hersteld ..."; /* Remove from this device button */ -"subscription.remove.from.device.button" = "Remove From This Device"; +"subscription.remove.from.device.button" = "Verwijderen van dit apparaat"; /* Remove from device confirmation dialog text */ -"subscription.remove.from.device.text" = "You will no longer be able to access your Privacy Pro subscription on this device. This will not cancel your subscription, and it will remain active on your other devices."; +"subscription.remove.from.device.text" = "Je hebt niet langer toegang tot je Privacy Pro-abonnement op dit apparaat. Je abonnement wordt niet opgezegd en het blijft actief op je andere apparaten."; /* Remove from device confirmation dialog title */ -"subscription.remove.from.device.title" = "Remove from this device?"; +"subscription.remove.from.device.title" = "Verwijderen van dit apparaat?"; /* Remove subscription button text */ -"subscription.remove.subscription" = "Remove Subscription"; +"subscription.remove.subscription" = "Abonnement verwijderen"; /* Remove subscription cancel button text */ -"subscription.remove.subscription.cancel" = "Cancel"; +"subscription.remove.subscription.cancel" = "Annuleren"; /* Button text for general error message */ "subscription.restore.backend.error.button" = "Terug naar Instellingen"; @@ -2509,28 +2671,43 @@ "subscription.restore.backend.error.title" = "Er is iets misgegaan"; /* Alert for general error message */ -"subscription.restore.general.error.message" = "The App Store was unable to process your purchase. Please try again later."; +"subscription.restore.general.error.message" = "De App Store kan je aankoop niet verwerken. Probeer het later opnieuw."; /* Alert for general error title */ -"subscription.restore.general.error.title" = "Something Went Wrong"; +"subscription.restore.general.error.title" = "Er is iets misgegaan"; /* Alert button text for restored purchase alert */ "subscription.restore.success.alert.button" = "OK"; /* Alert message for restored purchase */ -"subscription.restore.success.alert.message" = "Your purchases have been restored."; +"subscription.restore.success.alert.message" = "Je aankopen zijn hersteld."; /* Alert title for restored purchase */ -"subscription.restore.success.alert.title" = "You’re all set."; +"subscription.restore.success.alert.title" = "Je bent helemaal klaar."; + +/* Subtitle in header when subscribed */ +"subscription.subscribed" = "Geabonneerd"; /* Subscription Expired Data. This reads as 'Your subscription expired on (date)' */ "subscription.subscription.expired.caption" = "Je abonnement is verlopen op % @"; -/* Subscription expiration info. This reads as 'Your (monthly or annual) subscription expires on (date)' */ -"subscription.subscription.expiring.caption" = "Your %1$@ subscription expires on %2$@."; +/* Monthly subscription expiration info where parameter is expiration date. This reads as 'Your monthly subscription expires on (date)' */ +"subscription.subscription.expiring.monthly.caption" = "Je maandabonnement verloopt op %@."; + +/* Unknown period subscription expiration info where parameter is expiration date. This reads as 'Your subscription expires on (date)' */ +"subscription.subscription.expiring.unknown.caption" = "Je abonnement verloopt op %@."; + +/* Annual subscription expiration info where parameter is expiration date. This reads as 'Your annual subscription expires on (date)' */ +"subscription.subscription.expiring.yearly.caption" = "Je jaarabonnement verloopt op %@."; + +/* Monthly subscription renewal info where parameter is renewal date. This reads as 'Your monthly subscription renews on (date)' */ +"subscription.subscription.renewing.monthly.caption" = "Je maandabonnement wordt verlengd op %@."; + +/* Unknown period subscription renewal info where parameter is renewal date. This reads as 'Your subscription renews on (date)' */ +"subscription.subscription.renewing.unknown.caption" = "Je abonnement wordt verlengd op %@."; -/* Subscription renewal info. This reads as 'Your (monthly or annual) subscription renews on (date)' */ -"subscription.subscription.renewing.caption" = "Your %1$@ subscription renews on %2$@."; +/* Annual subscription renewal info where parameter is renewal date. This reads as 'Your annual subscription renews on (date)' */ +"subscription.subscription.renewing.yearly.caption" = "Je jaarlijkse abonnement wordt verlengd op %@."; /* Navigation bar Title for subscriptions */ "subscription.title" = "Privacy Pro"; @@ -2677,118 +2854,118 @@ "voiceSearch.footer.note.old" = "Audio wordt op het apparaat verwerkt. Het wordt niet opgeslagen of met iemand gedeeld, ook niet met DuckDuckGo."; /* Cancel action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.cancel" = "Dismiss"; +"vpn.access-revoked.alert.action.cancel" = "Negeren"; /* Primary action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.subscribe" = "Subscribe"; +"vpn.access-revoked.alert.action.subscribe" = "Abonneer"; /* Alert message for the alert when the Privacy Pro subscription expiress */ -"vpn.access-revoked.alert.message" = "Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"vpn.access-revoked.alert.message" = "Abonneer je op Privacy Pro om opnieuw verbinding te maken met DuckDuckGo VPN."; /* Alert title for the alert when the Privacy Pro subscription expires */ -"vpn.access-revoked.alert.title" = "VPN disconnected due to expired subscription"; +"vpn.access-revoked.alert.title" = "De verbinding met de VPN is verbroken omdat het abonnement is beëindigd"; /* Title for the VPN widget onboarding screen */ "vpn.addWidget.settings.title" = "Zoek en selecteer DuckDuckGo. Veeg vervolgens naar VPN en selecteer Widget toevoegen."; /* Title for the Cancel button of the VPN feedback form */ -"vpn.feedback-form.button.cancel" = "Cancel"; +"vpn.feedback-form.button.cancel" = "Annuleren"; /* Title for the Done button of the VPN feedback form */ -"vpn.feedback-form.button.done" = "Done"; +"vpn.feedback-form.button.done" = "Klaar"; /* Title for the Submit button of the VPN feedback form */ -"vpn.feedback-form.button.submit" = "Submit"; +"vpn.feedback-form.button.submit" = "Verzenden"; /* Title for the Submitting state of the VPN feedback form */ -"vpn.feedback-form.button.submitting" = "Submitting…"; +"vpn.feedback-form.button.submitting" = "Bezig met verzenden …"; /* Title for the browser crash/freeze category of the VPN feedback form */ -"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN causes browser to crash or freeze"; +"vpn.feedback-form.category.browser-crash-or-freeze" = "De VPN zorgt ervoor dat de browser crasht of vastloopt"; /* Title for the 'VPN fails to connect' category of the VPN feedback form */ -"vpn.feedback-form.category.fails-to-connect" = "VPN fails to connect"; +"vpn.feedback-form.category.fails-to-connect" = "De VPN kan geen verbinding maken"; /* Title for the 'VPN feature request' category of the VPN feedback form */ -"vpn.feedback-form.category.feature-request" = "VPN feature request"; +"vpn.feedback-form.category.feature-request" = "VPN-functieverzoek"; /* Title for the category 'VPN causes issues with other apps or websites' category of the VPN feedback form */ -"vpn.feedback-form.category.issues-with-apps" = "VPN causes issues with other apps or websites"; +"vpn.feedback-form.category.issues-with-apps" = "De VPN veroorzaakt problemen met andere apps of websites"; /* Title for the local device connectivity category of the VPN feedback form */ -"vpn.feedback-form.category.local-device-connectivity" = "VPN won't let me connect to local device"; +"vpn.feedback-form.category.local-device-connectivity" = "De VPN laat me geen verbinding maken met een lokaal apparaat"; /* Title for the 'other VPN feedback' category of the VPN feedback form */ -"vpn.feedback-form.category.other" = "Other VPN feedback"; +"vpn.feedback-form.category.other" = "Andere feedback over het VPN"; /* Title for the category selection state of the VPN feedback form */ -"vpn.feedback-form.category.select-category" = "Select a category"; +"vpn.feedback-form.category.select-category" = "SELECTEER EEN CATEGORIE"; /* Title for the 'VPN is too slow' category of the VPN feedback form */ -"vpn.feedback-form.category.too-slow" = "VPN connection is too slow"; +"vpn.feedback-form.category.too-slow" = "De VPN-verbinding is te traag"; /* Title for the 'unable to install' category of the VPN feedback form */ -"vpn.feedback-form.category.unable-to-install" = "Unable to install VPN"; +"vpn.feedback-form.category.unable-to-install" = "Ik kan de VPN niet installeren"; /* Title for the feedback sent view description of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.description" = "Your feedback will help us improve the\nDuckDuckGo VPN."; +"vpn.feedback-form.sending-confirmation.description" = "Je feedback helpt ons de VPN-functie van DuckDuckGo te verbeteren."; /* Title for the feedback sending error text of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.error" = "We couldn't send your feedback right now, please try again."; +"vpn.feedback-form.sending-confirmation.error" = "We kunnen je feedback op dit moment niet verzenden. Probeer het opnieuw."; /* Title for the feedback sent view title of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.title" = "Thank you!"; +"vpn.feedback-form.sending-confirmation.title" = "Bedankt!"; /* Toast message when the VPN feedback form is submitted successfully */ -"vpn.feedback-form.submitted.message" = "Thank You! Feedback submitted."; +"vpn.feedback-form.submitted.message" = "Dank je wel! Feedback verzonden."; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-1" = "Please describe what's happening, what you expected to happen, and the steps that led to the issue:"; +"vpn.feedback-form.text-1" = "Beschrijf wat er gebeurt, wat je verwachtte dat er zou gebeuren en de stappen die tot het probleem hebben geleid."; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-2" = "In addition to the details entered into this form, your app issue report will contain:"; +"vpn.feedback-form.text-2" = "Naast de gegevens die in dit formulier zijn ingevoerd, bevat je foutrapport:"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-3" = "• Whether specific DuckDuckGo features are enabled"; +"vpn.feedback-form.text-3" = "• Of er specifieke functies in DuckDuckGo zijn ingeschakeld"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-4" = "• Aggregate DuckDuckGo app diagnostics"; +"vpn.feedback-form.text-4" = "• Verzamelde diagnostische gegevens voor de DuckDuckGo-app"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-5" = "By tapping \"Submit\" I agree that DuckDuckGo may use the information in this report for purposes of improving the app's features."; +"vpn.feedback-form.text-5" = "Door op 'Verzenden' te tikken, geef ik DuckDuckGo toestemming om de informatie in dit rapport te gebruiken om de functies van de app te verbeteren."; /* Title for each screen of the VPN feedback form */ -"vpn.feedback-form.title" = "Help Improve the DuckDuckGo VPN"; +"vpn.feedback-form.title" = "Help de VPN-functie van DuckDuckGo te verbeteren"; /* VPN settings screen cell text for adding the VPN widget to the home screen */ "vpn.settings.add.widget" = "VPN-widget toevoegen aan startscherm"; /* Disclaimer for the DNS Server section on the DNS Server screen */ -"vpn.settings.dns.section-disclaimer" = "Using a custom DNS server can impact browsing speeds and expose your activity to third parties if the server isn't secure or reliable."; +"vpn.settings.dns.section-disclaimer" = "Het gebruik van een aangepaste DNS-server kan de surfsnelheid beïnvloeden en je activiteiten blootstellen aan derden als de server niet veilig of betrouwbaar is."; /* Header text for the DNS section on the VPN Settings screen */ "vpn.settings.dns.section-header" = "DNS"; /* Title for the Apply button on the DNS Server setting screen */ -"vpn.settings.dns.server.apply.button.title" = "Apply"; +"vpn.settings.dns.server.apply.button.title" = "Toepassen"; /* Default value for the DNS Server row on the VPN Settings screen */ "vpn.settings.dns.server.default.value" = "DuckDuckGo"; /* Title for the IPv4 Address setting */ -"vpn.settings.dns.server.ipv4.title" = "IPv4 Address"; +"vpn.settings.dns.server.ipv4.title" = "IPv4-adres"; /* Custom option for the DNS Server setting */ -"vpn.settings.dns.server.option.custom" = "Custom"; +"vpn.settings.dns.server.option.custom" = "Pas Aan"; /* Recommended option for the DNS Server setting */ -"vpn.settings.dns.server.option.default" = "DuckDuckGo (Recommended)"; +"vpn.settings.dns.server.option.default" = "DuckDuckGo (aanbevolen)"; /* Title for the DNS Server setting screen */ -"vpn.settings.dns.server.screen.title" = "DNS Server"; +"vpn.settings.dns.server.screen.title" = "DNS-server"; /* Title for the DNS Server row on the VPN Settings screen */ -"vpn.settings.dns.server.title" = "DNS Server"; +"vpn.settings.dns.server.title" = "DNS-server"; /* Title for the button to enable push notifications in system settings */ "waitlist.allow-notifications" = "Meldingen toestaan"; diff --git a/DuckDuckGo/nl.lproj/OmniBar.strings b/DuckDuckGo/nl.lproj/OmniBar.strings index 39042719a1..7c6a8a0d5e 100644 --- a/DuckDuckGo/nl.lproj/OmniBar.strings +++ b/DuckDuckGo/nl.lproj/OmniBar.strings @@ -7,6 +7,9 @@ /* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "Dt8-We-5tV"; */ "Dt8-We-5tV.accessibilityLabel" = "Tekst wissen"; +/* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "gb5-wL-4OW"; */ +"gb5-wL-4OW.accessibilityLabel" = "Tekst wissen"; + /* Class = "UIButton"; accessibilityLabel = "Share"; ObjectID = "hor-Jd-3kw"; */ "hor-Jd-3kw.accessibilityLabel" = "Delen"; diff --git a/DuckDuckGo/nl.lproj/Settings.strings b/DuckDuckGo/nl.lproj/Settings.strings index 33a73b1b46..eef2c9ae2c 100644 --- a/DuckDuckGo/nl.lproj/Settings.strings +++ b/DuckDuckGo/nl.lproj/Settings.strings @@ -10,9 +10,6 @@ /* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "5YD-Od-uDU"; */ "5YD-Od-uDU.text" = "Gegevens automatisch wissen"; -/* Class = "UIButton"; normalTitle = "Settings"; ObjectID = "8YG-7Q-Czd"; */ -"8YG-7Q-Czd.normalTitle" = "Instellingen"; - /* Class = "UILabel"; text = "App Exit, Inactive for 1 Hour"; ObjectID = "9EC-lx-77p"; */ "9EC-lx-77p.text" = "App afsluiten, 1 uur inactief"; @@ -76,9 +73,6 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Onbeschermde sites"; -/* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ -"ssa-zd-L3T.title" = "Lettergrootte"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Toetsenbord weergeven bij"; diff --git a/DuckDuckGo/pl.lproj/DaxOnboarding.strings b/DuckDuckGo/pl.lproj/DaxOnboarding.strings index 64377b41bf..c60839746e 100644 --- a/DuckDuckGo/pl.lproj/DaxOnboarding.strings +++ b/DuckDuckGo/pl.lproj/DaxOnboarding.strings @@ -1,12 +1,6 @@ /* Class = "UIButton"; normalTitle = "Hide"; ObjectID = "hgy-75-OvJ"; */ "hgy-75-OvJ.normalTitle" = "Ukryj"; -/* Class = "UILabel"; text = "Welcome to\nDuckDuckGo!"; ObjectID = "Lvy-9G-3gr"; */ -"Lvy-9G-3gr.text" = "Witamy\nw DuckDuckGo!"; - -/* Class = "UIButton"; normalTitle = "Let’s Do It!"; ObjectID = "oAv-Zt-uIt"; */ -"oAv-Zt-uIt.normalTitle" = "Zróbmy to!"; - /* Class = "UIButton"; normalTitle = "Button"; ObjectID = "zJt-F9-L48"; */ "zJt-F9-L48.normalTitle" = "Przycisk"; diff --git a/DuckDuckGo/pl.lproj/Localizable.strings b/DuckDuckGo/pl.lproj/Localizable.strings index 3ed633cac8..c91457fda0 100644 --- a/DuckDuckGo/pl.lproj/Localizable.strings +++ b/DuckDuckGo/pl.lproj/Localizable.strings @@ -341,7 +341,7 @@ "autofill.keep-enabled.alert.disable" = "Wyłącz"; /* A link that takes the user to the DuckDuckGo help pages explaining password managers */ -"autofill.learn.more.link.title" = "Learn More"; +"autofill.learn.more.link.title" = "Dowiedz się więcej"; /* Button displayed after saving/updating an autofill login that takes the user to the saved login */ "autofill.login-save-action-button.toast" = "Wyświetl"; @@ -977,9 +977,6 @@ /* ad = advertisment */ "dax.onboarding.home.subsequent" = "Udało się!\n\nPamiętaj: za każdym razem, gdy przeglądasz ze mną Internet, jakaś wścibska reklama przestaje działać. 👍"; -/* No comment provided by engineer. */ -"dax.onboarding.message" = "Internet może być trochę nieprzyjemny.\n\nBez obaw! Prywatne wyszukiwanie i przeglądanie jest łatwiejsze niż myślisz."; - /* No comment provided by engineer. */ "Debug" = "Debugowanie"; @@ -1080,7 +1077,7 @@ "duckPlayer.never.label" = "Nigdy"; /* Body text for the modal feature explanation */ -"duckplayer.presentation.modal.body" = "Duck Player umożliwia oglądanie YouTube bez ukierunkowanych reklam w środowisku kinowym DuckDuckGo bez wpływu oglądanych treści na rekomendacje."; +"duckplayer.presentation.modal.body" = "Duck Player umożliwia oglądanie YouTube bez ukierunkowanych reklam w DuckDuckGo i wpływu oglądanych treści na rekomendacje."; /* Button that will dismiss the modal */ "duckplayer.presentation.modal.dismiss-button" = "Rozumiem!"; @@ -1088,17 +1085,23 @@ /* Two line title (separated by \n) for the feature explanation */ "duckplayer.presentation.modal.title" = "Za dużo reklam na YouTube? Wypróbuj Duck Player!"; +/* Two line title (separated by \n) for the feature explanation */ +"duckplayer.presentation.modal.title.youtube" = "Za dużo reklam na YouTube? Nie z odtwarzaczem Duck Player!"; + /* Footer label in the settings screen for Duck Player */ "duckplayer.settings.footer" = "DuckDuckGo zapewnia wszystkie niezbędne funkcje ochrony prywatności użytkownika podczas przeglądania Internetu."; /* Text explaining what Duck Player is in the settings screen. */ -"duckplayer.settings.info-text" = "Duck Player zapewnia czyste środowisko oglądania bez spersonalizowanych reklam i sprawia, że aktywność związana z oglądaniem filmów nie wpływa na rekomendacje YouTube'a."; +"duckplayer.settings.info-text" = "Duck Player umożliwia oglądanie YouTube bez ukierunkowanych reklam w DuckDuckGo i wpływu oglądanych treści na rekomendacje."; /* Button that takes the user to learn more about Duck Player. */ "duckplayer.settings.learn-more" = "Dowiedz się więcej"; +/* Settings screen cell text for DuckPlayer settings to open in new tab */ +"duckplayer.settings.open-new-tab-label" = "Otwieraj Duck Player w nowej karcie"; + /* Settings screen cell text for DuckPlayer settings */ -"duckplayer.settings.open-videos-in" = "Otwieraj filmy w Duck Player"; +"duckplayer.settings.open-videos-in" = "Otwieraj filmy z YouTube w odtwarzaczu Duck Player"; /* Settings screen cell text for DuckPlayer settings */ "duckplayer.settings.title" = "Duck Player"; @@ -1169,9 +1172,6 @@ /* Title for prompt to sign up for email protection */ "email.signup-prompt.title" = "Ukryj swój adres e-mail i\nblokuj skrypty śledzące"; -/* No comment provided by engineer. */ -"empty!" = "empty!"; - /* Empty list state placholder */ "empty.bookmarks" = "Nie dodano jeszcze żadnych zakładek"; @@ -1322,6 +1322,39 @@ /* Confirmation button */ "feedback.form.submit" = "Prześlij"; +/* Title of the feedback form */ +"feedback.form.title" = "Pomóż ulepszyć Privacy Pro"; + +/* Category for Identity Theft Restoration feedback */ +"feedback.general.category.itr" = "Identity Theft Restoration"; + +/* Category for Personal Info Removal feedback */ +"feedback.general.category.pir" = "Usuwanie danych osobowych"; + +/* Category for subscription and payments feedback */ +"feedback.general.category.ppro" = "Subskrypcja i płatności"; + +/* Prompt to select a category for general feedback */ +"feedback.general.category.select" = "WYBIERZ KATEGORIĘ"; + +/* Category for VPN feedback */ +"feedback.general.category.vpn" = "VPN"; + +/* Category for access code issues */ +"feedback.itr.category.access-code" = "Problem z kodem dostępu"; + +/* Category for issues contacting an advisor */ +"feedback.itr.category.cant-contact-advisor" = "Nie można skontaktować się z doradcą"; + +/* Prompt to select a category for Identity Theft Restoration feedback */ +"feedback.itr.category.select" = "WYBIERZ KATEGORIĘ"; + +/* Category for other Identity Theft Restoration issues */ +"feedback.itr.category.something-else" = "Coś innego"; + +/* Category for unhelpful advisor calls */ +"feedback.itr.category.unhelpful" = "Połączenie z doradcą nie pomogło"; + /* No comment provided by engineer. */ "feedback.negative.form.genericPlaceholder" = "Podaj jak najdokładniejsze informacje"; @@ -1361,6 +1394,24 @@ /* No comment provided by engineer. */ "feedback.performance.slowLoading" = "Strony lub wyniki wyszukiwania wczytują się powoli"; +/* Category for when scan finds incorrect records */ +"feedback.pir.category.not-me" = "Skanowanie pokazuje rekordy, które mnie nie dotyczą"; + +/* Category for when scan doesn't find info on a specific site */ +"feedback.pir.category.nothing-on-site" = "Skanowanie nie odnajduje moich danych na konkretnej stronie"; + +/* Category for other Personal Info Removal issues */ +"feedback.pir.category.other" = "Coś innego"; + +/* Category for when the removal process is stuck */ +"feedback.pir.category.removal-stuck" = "Proces usuwania zablokowany"; + +/* Category for when the scan is stuck */ +"feedback.pir.category.scan-stuck" = "Skanowanie w poszukiwaniu rekordów zablokowane"; + +/* Prompt to select a category for Personal Info Removal feedback */ +"feedback.pir.category.select" = "WYBIERZ KATEGORIĘ"; + /* Header above input field */ "feedback.positive.form.header" = "Przekaż informacje"; @@ -1379,6 +1430,15 @@ /* Button encouraging uses to share details aboout their feedback */ "feedback.positive.submit" = "Przekaż informacje"; +/* Category for other Privacy Pro issues */ +"feedback.ppro.category.other" = "Coś innego"; + +/* Category for one-time password issues */ +"feedback.ppro.category.otp" = "Problem z hasłem jednorazowym"; + +/* Prompt to select a category for Privacy Pro feedback */ +"feedback.ppro.category.select" = "WYBIERZ KATEGORIĘ"; + /* No comment provided by engineer. */ "feedback.start.footer" = "Twoja anonimowa opinia ma dla nas ogromne znaczenie."; @@ -1529,12 +1589,6 @@ /* No comment provided by engineer. */ "keyCommandShowAllTabs" = "Pokaż wszystkie karty"; -/* Please preserve newline character */ -"launchscreenWelcomeMessage" = "Witamy\nw DuckDuckGo!"; - -/* No comment provided by engineer. */ -"LOREM IPSUM" = "LOREM IPSUM"; - /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "Aplikacja DuckDuckGo dla komputerów Mac zapewnia szybkość, której potrzebujesz, i niezbędne funkcje przeglądania. Ponadto jest wyposażona w najlepsze w swojej klasie narzędzia do ochrony prywatności."; @@ -1586,62 +1640,83 @@ /* Edit button */ "navigation.title.edit" = "Edytuj"; -/* String indicating NetP is connected when viewed from the settings screen */ -"netP.cell.connected" = "Connected"; - -/* String indicating NetP is disconnected when viewed from the settings screen */ -"netP.cell.disconnected" = "Not connected"; - -/* Title for the DuckDuckGo VPN feature in settings */ -"netP.settings.title" = "VPN"; +/* String indicating NetP is snoozing when viewed from the settings screen */ +"netP.cell.snoozing" = "Wstrzymano"; /* Title for the DuckDuckGo VPN feature */ "netP.title" = "DuckDuckGo VPN"; +/* Button title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.action" = "Dodaj widżet"; + +/* Message for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.message" = "Włączaj i wyłączaj VPN bezpośrednio z ekranu głównego."; + +/* Title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.title" = "Dodaj widżet VPN"; + /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "Sieć VPN została rozłączona z powodu wygasłej subskrypcji. Subskrybuj Privacy Pro, aby ponownie połączyć się z siecią DuckDuckGo VPN."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Niepowodzenie połączenia usługi ochrony sieci.Spróbuj ponownie później."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "Nie udało się nawiązać połączenia DuckDuckGo VPN. Spróbuj ponownie później."; /* Title for the VPN FAQ screen. */ "network.protection.faq.title" = "DuckDuckGo VPN — często zadawane pytania"; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Usługa ochrony sieci została przerwana. Próba ponownego połączenia..."; +/* Message for tooltip about geoswitching */ +"network.protection.geoswitching.tip.message" = "Możesz dostosować lokalizację sieci VPN, łącząc się z dowolnym z naszych serwerów na całym świecie."; + +/* Title for tooltip about geoswitching */ +"network.protection.geoswitching.tip.title" = "Zmień lokalizację"; + +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "Usługa DuckDuckGo VPN została przerwana. Próba ponownego połączenia..."; /* Message for the network protection invite dialog */ -"network.protection.invite.dialog.message" = "Enter your invite code to get started."; +"network.protection.invite.dialog.message" = "Wprowadź swój kod zaproszenia, aby rozpocząć."; /* Title for the network protection invite screen */ "network.protection.invite.dialog.title" = "Masz zaproszenie do wypróbowania DuckDuckGo VPN"; /* Prompt for the network protection invite code text field */ -"network.protection.invite.field.prompt" = "Invite Code"; +"network.protection.invite.field.prompt" = "Kod zaproszenia"; /* Message for the network protection invite success view */ -"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; +"network.protection.invite.success.message" = "Ukryj lokalizację na stronach internetowych i w Internecie przed dostawcami usługi dostępu do Internetu oraz innymi użytkownikami w sieci."; /* Title for the network protection invite success view */ -"network.protection.invite.success.title" = "Success! You’re in."; +"network.protection.invite.success.title" = "Udało się! Dołączono."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; /* Title text for an iOS quick action that opens VPN settings */ "network.protection.quick-action.open-vpn" = "Otwórz VPN"; +/* Button title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.action" = "Dowiedz się więcej"; + +/* Message for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.message" = "Możesz korzystać z witryn lub aplikacji, które blokują ruch VPN, wstrzymując połączenie VPN."; + +/* Title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.title" = "Unikaj konfliktów VPN"; + +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "Wstrzymano VPN na %@"; + /* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ "network.protection.status.connected.format" = "Połączono · %@"; /* The label for the NetP VPN when connecting */ -"network.protection.status.connecting" = "Connecting..."; +"network.protection.status.connecting" = "Łączenie..."; /* The label for the NetP VPN when disconnected */ -"network.protection.status.disconnected" = "Not connected"; +"network.protection.status.disconnected" = "Nie połączono"; /* The label for the NetP VPN when disconnecting */ -"network.protection.status.disconnecting" = "Disconnecting..."; +"network.protection.status.disconnecting" = "Odłączanie..."; /* Message label text for the status view when VPN is disconnected */ "network.protection.status.header.message.off" = "Nawiąż połączenie, aby zabezpieczyć ruch internetowy urządzenia."; @@ -1655,26 +1730,41 @@ /* Header title label text for the status view when VPN is connected */ "network.protection.status.header.title.on" = "Usługa DuckDuckGo VPN jest włączona"; +/* Header title label text for the status view when VPN is snoozing */ +"network.protection.status.header.title.snoozed" = "Usługa DuckDuckGo VPN jest wstrzymana"; + /* The status view 'Share Feedback' button which is shown inline on the status view after the temporary free use footer text */ -"network.protection.status.menu.share.feedback" = "Share Feedback"; +"network.protection.status.menu.share.feedback" = "Podziel się opinią"; + +/* The label for the NetP VPN when paused */ +"network.protection.status.paused" = "Wstrzymano"; + +/* The label for when NetP VPN is snoozing plus the length of time remaining formatted as '0:00' */ +"network.protection.status.snoozing.format" = "Wstrzymano, pozostało %@"; + +/* Snooze button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.snooze" = "Wstrzymaj na 20 minut"; + +/* Wake Up button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.wake-up" = "Przywróć"; /* Connection details label shown in NetworkProtection's status view. */ -"network.protection.status.view.connection.details" = "Connection Details"; +"network.protection.status.view.connection.details" = "Szczegóły połączenia"; /* Custom DNS label shown in NetworkProtection's status view. */ -"network.protection.status.view.custom.dns" = "DNS Server"; +"network.protection.status.view.custom.dns" = "Serwer DNS"; /* Generic connection failed error message shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.message" = "Please try again later."; +"network.protection.status.view.error.connection.failed.message" = "Spróbuj ponownie później."; /* Generic connection failed error title shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; +"network.protection.status.view.error.connection.failed.title" = "Nie udało się połączyć."; /* IP Address label shown in NetworkProtection's status view. */ -"network.protection.status.view.ip.address" = "IP Address"; +"network.protection.status.view.ip.address" = "Adres IP"; /* Location label shown in NetworkProtection's status view. */ -"network.protection.status.view.location" = "Location"; +"network.protection.status.view.location" = "Lokalizacja"; /* Label shown on the title of the settings section in NetworkProtection's status view. */ "network.protection.status.view.settings.section.title" = "Zarządzaj"; @@ -1682,12 +1772,15 @@ /* Title label text for the status view when netP is disconnected */ "network.protection.status.view.title" = "VPN"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Usługa ochrony sieci jest włączona. Twoja lokalizacja i aktywność w Internecie są chronione."; +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "Usługa DuckDuckGo VPN jest włączona. Twoja lokalizacja i aktywność w Internecie są chronione."; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Przekierowanie ruchu urządzenia przez %@."; +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "Wstrzymanie VPN zostało zakończone. Przekierowanie ruchu urządzenia przez %@."; + /* Title for the button to link to the iOS app settings and enable notifications app-wide. */ "network.protection.turn.on.notifications.button.title" = "Włącz powiadomienia"; @@ -1698,13 +1791,13 @@ "network.protection.vpn.about" = "Informacje"; /* Section header for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.section.header" = "Notifications"; +"network.protection.vpn.alerts.section.header" = "Powiadomienia"; /* List section footer for the toggle for VPN alerts. */ "network.protection.vpn.alerts.toggle.section.footer" = "Otrzymuj powiadomienia o zerwaniu połączenia lub zmianie stanu VPN."; /* Title for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.toggle.title" = "VPN Notifications"; +"network.protection.vpn.alerts.toggle.title" = "Powiadomienia VPN"; /* Title for the data volume section in the VPN status screen */ "network.protection.vpn.data-volume" = "Ilość danych"; @@ -1713,13 +1806,13 @@ "network.protection.vpn.exclude.local.networks.setting.footer" = "Zezwól na pomijanie sieci VPN przez ruch lokalny w celu łączenia z urządzeniami w sieci lokalnej, takimi jak drukarka."; /* Header text for the Exclude Local Networks setting item. */ -"network.protection.vpn.exclude.local.networks.setting.header" = "General"; +"network.protection.vpn.exclude.local.networks.setting.header" = "Ogólne"; /* Title for the Exclude Local Networks setting item. */ "network.protection.vpn.exclude.local.networks.setting.title" = "Wyklucz sieci lokalne"; /* Title for the VPN Location screen's All Countries section. */ -"network.protection.vpn.location.all.countries.section.title" = "All Countries"; +"network.protection.vpn.location.all.countries.section.title" = "Wszystkie kraje"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.connected" = "Połączona lokalizacja"; @@ -1734,7 +1827,7 @@ "network.protection.vpn.location.recommended.section.footer" = "Automatycznie połącz z najbliższym znalezionym serwerem."; /* Title for the VPN Location screen's Recommended section. */ -"network.protection.vpn.location.recommended.section.title" = "Recommended"; +"network.protection.vpn.location.recommended.section.title" = "Zalecane"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.selected" = "Wybrana lokalizacja"; @@ -1752,13 +1845,13 @@ "network.protection.vpn.preferred.location.nearest" = "Najbliższa lokalizacja"; /* Title for the Preferred Location VPN Settings item. */ -"network.protection.vpn.preferred.location.title" = "Preferred Location"; +"network.protection.vpn.preferred.location.title" = "Preferowana lokalizacja"; /* Footer text for the DNS server setting item. */ -"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo routes DNS queries through our DNS servers so your internet provider can't see what websites you visit."; +"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo kieruje zapytania DNS przez nasze serwery DNS, aby Twój dostawca usług internetowych nie mógł zobaczyć, jakie strony odwiedzasz."; /* Title for the FAQ row in the VPN status screen. */ -"network.protection.vpn.settings.faq" = "FAQs and Support"; +"network.protection.vpn.settings.faq" = "Często zadawane pytania i pomoc techniczna"; /* Title for the feedback row in the VPN status screen. */ "network.protection.vpn.settings.share-feedback" = "Podziel się opinią o funkcji VPN"; @@ -1940,6 +2033,66 @@ /* Deactivate button */ "pm.deactivate" = "Dezaktywuj"; +/* Label for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.label" = "Adres e-mail (opcjonalnie)"; + +/* Placeholder for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.placeholder" = "name@email.com"; + +/* Placeholder for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.placeholder" = "Przekaż opinię..."; + +/* Title for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.title" = "Opinia ogólna"; + +/* Title for the ITR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-itr-problem.title" = "Identity Theft Restoration"; + +/* Title for the PIR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-pir-problem.title" = "Personal Information Removal"; + +/* Title for the Subscriptions and Payments category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-ppro-problem.title" = "Subskrypcje i płatności"; + +/* Placeholder for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.placeholder" = "Powiedz nam, co się dzieje…"; + +/* Title for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.title" = "Zgłoś problem"; + +/* Title for the VPN category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-vpn-problem.title" = "VPN"; + +/* Placeholder for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.placeholder" = "Na jakiej funkcji Ci zależy?"; + +/* Title for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.title" = "Prośba o dodanie funkcji"; + +/* Title for the category selection section in the Privacy Pro feedback form */ +"ppro.feedback-form.select-category.title" = "WYBIERZ KATEGORIĘ"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-1" = "Wystąpił problem, który nie został omówiony w naszym [centrum pomocy](duck://)? Zdecydowanie chcemy o tym wiedzieć.\n\nPodaj adres e-mail, jeśli wyrażasz zgodę na kontakt w tej sprawie (możemy nie być w stanie odpowiedzieć na wszystkie zgłoszenia):"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-2" = "Oprócz szczegółów podanych powyżej, wysyłamy zanonimizowane informacje wraz z Twoją opinią:"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-3" = "• Czy określone funkcje przeglądarki są aktywne"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-4" = "• Gromadzenie danych diagnostyki aplikacji (np. kody błędów)"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-5" = "Dotykając opcji „Prześlij”, wyrażasz zgodę na wykorzystanie przez DuckDuckGo przesłanych informacji w celu ulepszenia aplikacji."; + +/* Title for the text box in the Privacy Pro feedback form */ +"ppro.feedback-form.text-box.title" = "Opinie"; + +/* Title for the Privacy Pro feedback form */ +"ppro.feedback-form.title" = "Wyślij opinię"; + /* Button title for sync bookmarks limits exceeded warning to go to manage bookmarks */ "prefrences.sync.bookmarks-limit-exceeded-action" = "Zarządzaj zakładkami"; @@ -2029,6 +2182,18 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Ulubione"; +/* Name of the option the user can chose to give general browser feedback */ +"send.browser.feedback.general-feedback" = "Opinia ogólna"; + +/* Name of the option the user can chose to give browser feedback about a problem they enountered */ +"send.browser.feedback.report-problem" = "Zgłoś problem"; + +/* Name of the option the user can chose to give browser feedback about a feature they would like */ +"send.browser.feedback.request-feature" = "Poproś o funkcję"; + +/* Title of the picker where the user can chose the category of the feedback they want ot send. */ +"send.browser.feedback.select-category" = "WYBIERZ KATEGORIĘ"; + /* Settings cell for About DDG */ "settings.about.ddg" = "O DuckDuckGo"; @@ -2084,6 +2249,9 @@ /* Section footer Autolock description */ "settings.autolock.description" = "Jeśli włączono Touch ID, Face ID lub hasło systemowe, pojawi się prośba o odblokowanie aplikacji podczas jej otwierania."; +/* Settings cell for Browser Feedback */ +"settings.browser.feedback" = "Opinia o przeglądarce"; + /* Settings screen cell text for Automatically Clearing Data */ "settings.clear.data" = "Automatyczne czyszczenie danych"; @@ -2182,7 +2350,7 @@ "settings.ppro" = "Privacy Pro"; /* Title for Link in the Footer of Privacy Pro section */ -"settings.ppro.footer" = "Privacy Policy and Terms of Service"; +"settings.ppro.footer" = "Polityka prywatności i warunki użytkowania"; /* Settings screen cell for long press previews */ "settings.previews" = "Podglądy po dłuższym przyciśnięciu"; @@ -2208,23 +2376,20 @@ /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Ustaw pozycję paska adresu"; -/* Subscription activation pending description */ -"settings.subscription.activation.pending.description" = "This is taking longer than usual, please check back later."; +/* Privacy pro description subtitle in settings when the is activating */ +"settings.subscription.activating" = "Aktywacja"; -/* Subscription activation pending title */ -"settings.subscription.activation.pending.title" = "Twoja subskrypcja jest aktywowana"; +/* Subscription activation pending description */ +"settings.subscription.activation.pending.description" = "Trwa to dłużej niż zwykle, sprawdź ponownie później."; /* Data Broker protection cell subtitle for privacy pro */ -"settings.subscription.DBP.subtitle" = "Remove your info from sites that sell it"; +"settings.subscription.DBP.subtitle" = "Usuń swoje dane z witryn, które je sprzedają"; /* Data Broker protection cell title for privacy pro */ "settings.subscription.DBP.title" = "Personal Information Removal"; -/* Privacy pro description subtext */ -"settings.subscription.description" = "Płynniejsza ochrona prywatności dzięki trzem nowym zabezpieczeniom:"; - /* I have a Subscription button text for privacy pro */ -"settings.subscription.existing.subscription" = "I Have a Subscription"; +"settings.subscription.existing.subscription" = "Mam subskrypcję"; /* Subscription expired description */ "settings.subscription.expired.comment" = "Wznów subskrypcję, aby nadal korzystać z Privacy Pro"; @@ -2232,23 +2397,26 @@ /* Subscription expired tittle message */ "settings.subscription.expired.title" = "Twoja subskrypcja Privacy Pro wygasła"; -/* Privacy pro features list */ -"settings.subscription.features" = " • VPN\n • Personal Information Removal\n • Identity Theft Restoration"; - /* Identity theft restoration cell subtitle for privacy pro */ -"settings.subscription.ITR.subtitle" = "If your identity is stolen, we'll help restore it"; +"settings.subscription.ITR.subtitle" = "W przypadku kradzieży Twojej tożsamości pomożemy Ci ją przywrócić"; /* Identity theft restoration cell title for privacy pro */ "settings.subscription.ITR.title" = "Identity Theft Restoration"; /* Get Privacy Pro button text for privacy pro */ -"settings.subscription.learn.more" = "Get Privacy Pro"; +"settings.subscription.learn.more" = "Uzyskaj Privacy Pro"; /* Subscription Settings button text for privacy pro */ -"settings.subscription.manage" = "Subscription Settings"; +"settings.subscription.manage" = "Ustawienia subskrypcji"; + +/* Privacy Pro description subtitle in settings */ +"settings.subscription.row.description" = "Obejmuje usługi VPN oraz Identity Theft Restoration."; -/* Call to action title for Privacy Pro */ -"settings.subscription.subscribe" = "Subscribe to Privacy Pro"; +/* Call to action title for Privacy Pro settings */ +"settings.subscription.subscribe" = "Chroń połączenie i tożsamość dzięki subskrypcji Privacy Pro"; + +/* Privacy pro description subtitle in settings */ +"settings.subscription.us.description" = "Obejmuje usługi VPN, Personal Information Removal oraz Identity Theft Restoration."; /* VPN cell title for privacy pro */ "settings.subscription.VPN.title" = "VPN"; @@ -2317,67 +2485,64 @@ "siteFeedback.urlPlaceholder" = "Która witryna nie działa poprawnie?"; /* Subscription Activation Info */ -"subscription.activate..header.description" = "Access your Privacy Pro subscription on this device via Apple ID or an email address."; +"subscription.activate..header.description" = "Uzyskaj dostęp do subskrypcji Privacy Pro na tym urządzeniu za pośrednictwem Apple ID lub adresu e-mail."; /* Button for adding email address to subscription */ -"subscription.activate.add.email.button" = "Add Email"; +"subscription.activate.add.email.button" = "Dodaj adres e-mail"; /* Apple ID option for activation */ "subscription.activate.appleid" = "Apple ID"; /* Button text for restoring purchase via Apple ID */ -"subscription.activate.appleid.button" = "Restore Purchase"; +"subscription.activate.appleid.button" = "Przywróć zakup"; /* Description for Apple ID activation */ -"subscription.activate.appleid.description" = "Restore your purchase to activate your subscription on this device."; +"subscription.activate.appleid.description" = "Przywróć zakup, aby aktywować subskrypcję na tym urządzeniu."; /* Subscription Activation Info */ -"subscription.activate.description" = "Your subscription is automatically available in DuckDuckGo on any device signed in to your Apple ID."; +"subscription.activate.description" = "Twoja subskrypcja jest automatycznie dostępna w DuckDuckGo na każdym urządzeniu, na którym zalogujesz się na konto Apple ID."; /* Button for editing email address added to subscription */ -"subscription.activate.edit.email.button" = "Edit Email"; +"subscription.activate.edit.email.button" = "Edytuj adres e-mail"; /* View Title for editing your email account */ -"subscription.activate.edit.email.title" = "Edit Email"; +"subscription.activate.edit.email.title" = "Edytuj adres e-mail"; /* Email option for activation */ -"subscription.activate.email" = "Email"; +"subscription.activate.email" = "E-mail"; /* Restore button title for Email */ -"subscription.activate.email.button" = "Enter Email"; +"subscription.activate.email.button" = "Wpisz adres e-mail"; /* Description for Email activation */ -"subscription.activate.email.description" = "Use your email to activate your subscription on this device."; +"subscription.activate.email.description" = "Użyj adresu e-mail, aby aktywować subskrypcję na tym urządzeniu."; /* Activate subscription title */ "subscription.activate.email.title" = "Aktywuj subskrypcję"; /* Button title for cancelling email deletion */ -"subscription.activate.manage.email.cancel" = "Cancel"; +"subscription.activate.manage.email.cancel" = "Anuluj"; /* Button title for confirming email deletion */ "subscription.activate.manage.email.OK" = "OK"; /* Restore button title for AppleID */ -"subscription.activate.restore.apple" = "Restore Purchase"; +"subscription.activate.restore.apple" = "Przywróć zakup"; /* Subscription Activation Title */ -"subscription.activate.title" = "Activate your subscription on this device"; +"subscription.activate.title" = "Aktywuj subskrypcję na tym urządzeniu"; /* Resend activation instructions button */ -"subscription.add.device.resend.instructions" = "Resend Instructions"; +"subscription.add.device.resend.instructions" = "Wyślij ponownie instrukcje"; /* Add email to an existing subscription */ -"subscription.add.email" = "Add an email address to activate your subscription on your other devices. We’ll only use this address to verify your subscription."; +"subscription.add.email" = "Dodaj adres e-mail, aby aktywować subskrypcję na innych urządzeniach. Użyjemy tego adresu w celu zweryfikowania Twojej subskrypcji."; /* View title for adding email to subscription */ -"subscription.add.email.title" = "Add Email"; - -/* Title for Alert messages */ -"subscription.alert.title" = "subscription.alert.title"; +"subscription.add.email.title" = "Dodaj adres e-mail"; /* Subscription availability message on Apple devices */ -"subscription.available.apple" = "Privacy Pro is available on any device signed in to the same Apple ID."; +"subscription.available.apple" = "Subskrypcja Privacy Pro jest dostępna na każdym urządzeniu zalogowanym na to samo konto Apple ID."; /* Text for the manage billing page */ "subscription.billing.google.text" = "Twoja subskrypcja została zakupiona za pośrednictwem sklepu Google Play. Aby odnowić subskrypcję, otwórz ustawienia subskrypcji w sklepie Google Play na urządzeniu zalogowanym na konto Google wykorzystane do zakupu subskrypcji."; @@ -2385,89 +2550,86 @@ /* Title for the manage billing page */ "subscription.billing.google.title" = "Plany subskrypcji"; -/* Subscription annual billing period type */ -"subscription.billing.period.annual" = "annual"; - -/* Subscription monthly billing period type */ -"subscription.billing.period.monthly" = "monthly"; - /* Subscription Removal confirmation message */ -"subscription.cancel.message" = "Your subscription has been removed from this device."; +"subscription.cancel.message" = "Twoja subskrypcja została usunięta z tego urządzenia."; /* Change plan or cancel title */ -"subscription.change.plan" = "Update Plan or Cancel"; +"subscription.change.plan" = "Aktualizuj lub anuluj plan"; /* Navigation Button for closing subscription view */ -"subscription.close" = "Close"; +"subscription.close" = "Zamknij"; /* Title for Confirm messages */ -"subscription.confirm.title" = "Are you sure?"; +"subscription.confirm.title" = "Na pewno?"; /* Header for section for activating subscription on other devices */ -"subscription.devices.header" = "Activate on Other Devices"; +"subscription.devices.header" = "Aktywuj na innych urządzeniach"; /* Footer for section for activating subscription on other devices when email was not yet added */ -"subscription.devices.no.email.footer" = "Add an optional email to your subscription or use your Apple ID to access Privacy Pro on other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.no.email.footer" = "Dodaj opcjonalny adres e-mail do subskrypcji, aby uzyskać dostęp do Privacy Pro na innych urządzeniach. **[Więcej informacji](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Footer for section for activating subscription on other devices when email is added */ -"subscription.devices.with.email.footer" = "Use this email to activate your subscription in Settings > Privacy Pro in the DuckDuckGo app on your other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.with.email.footer" = "Użyj tego adresu e-mail, aby aktywować subskrypcję poprzez wybór opcji Ustawienia > Privacy Pro w aplikacji DuckDuckGo na innych urządzeniach. **[Więcej informacji](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Alert content for not found subscription */ -"subscription.email.inactive.alert.message" = "The subscription associated with this email is no longer active."; +"subscription.email.inactive.alert.message" = "Subskrypcja powiązana z tym adresem e-mail nie jest już aktywna."; /* Alert title for not found subscription */ -"subscription.email.inactive.alert.title" = "Subscription Not Found"; +"subscription.email.inactive.alert.title" = "Nie znaleziono subskrypcji"; /* Alert content for not found subscription */ -"subscription.expired.alert.message" = "The subscription associated with this Apple ID is no longer active."; +"subscription.expired.alert.message" = "Subskrypcja powiązana z tym kontem Apple ID nie jest już aktywna."; /* FAQ Button */ -"subscription.faq" = "FAQs and Support"; +"subscription.faq" = "Często zadawane pytania i pomoc techniczna"; /* FAQ Description */ -"subscription.faq.description" = "Get answers to frequently asked questions or contact Privacy Pro support from our help pages."; +"subscription.faq.description" = "Uzyskaj odpowiedzi na często zadawane pytania lub skontaktuj się z pomocą techniczną Privacy Pro na naszych stronach pomocy."; + +/* Send Feedback Button */ +"subscription.feedback" = "Wyślij opinię"; /* Cancel action for the existing subscription dialog */ -"subscription.found.cancel" = "Cancel"; +"subscription.found.cancel" = "Anuluj"; /* Restore action for the existing subscription dialog */ -"subscription.found.restore" = "Restore"; +"subscription.found.restore" = "Przywróć"; /* Message for the existing subscription dialog */ -"subscription.found.text" = "We found a subscription associated with this Apple ID."; +"subscription.found.text" = "Znaleźliśmy subskrypcję powiązaną z tym Apple ID."; /* Title for the existing subscription dialog */ -"subscription.found.title" = "Subscription Found"; +"subscription.found.title" = "Znaleziono subskrypcję"; /* Help and support Section header */ -"subscription.help" = "Help and support"; +"subscription.help" = "Pomoc i wsparcie"; /* Manage Plan header */ -"subscription.manage.plan" = "Manage Plan"; +"subscription.manage.plan" = "Zarządzaj planem"; /* Header for the subscription section */ -"subscription.manage.title" = "Subscription"; +"subscription.manage.title" = "Subskrypcja"; /* Alert content for not found subscription */ -"subscription.notFound.alert.message" = "There is no subscription associated with this Apple ID."; +"subscription.notFound.alert.message" = "Nie ma subskrypcji powiązanej z tym Apple ID."; /* Alert title for not found subscription */ -"subscription.notFound.alert.title" = "Subscription Not Found"; +"subscription.notFound.alert.title" = "Nie znaleziono subskrypcji"; /* View plans button text */ -"subscription.notFound.view.plans" = "View Plans"; +"subscription.notFound.view.plans" = "Wyświetl plany"; /* Hero Text for Personal information removal */ -"subscription.pir.hero" = "Activate Privacy Pro on desktop to set up Personal Information Removal"; +"subscription.pir.hero" = "Aktywuj Privacy Pro na komputerze, aby skonfigurować usługę Personal Information Removal"; /* Description on how to use Personal information removal in desktop. The first placeholder references a location in the Desktop application. Privacy Pro>, and the second, the menu entry. i.e. */ -"subscription.pir.heroText" = "In the DuckDuckGo browser for desktop, go to %1$@ and click %2$@ to get started."; +"subscription.pir.heroText" = "W przeglądarce DuckDuckGo na komputerze wybierz %1$@ i kliknij %2$@, aby rozpocząć."; /* Settings references a menu in the Desktop app, Privacy Pro, references our product name */ -"subscription.pir.heroTextLocation" = "Settings > Privacy Pro"; +"subscription.pir.heroTextLocation" = "Ustawienia > Privacy Pro"; /* Menu item for enabling Personal Information Removal on Desktop */ -"subscription.pir.heroTextMenyEntry" = "I have a subscription"; +"subscription.pir.heroTextMenyEntry" = "Mam subskrypcję"; /* Text for the 'macOS' button */ "subscription.pir.macos" = "Mac"; @@ -2476,28 +2638,28 @@ "subscription.pir.windows" = "Windows"; /* Progress view title when completing the purchase */ -"subscription.progress.view.completing.purchase" = "Completing purchase..."; +"subscription.progress.view.completing.purchase" = "Finalizowanie zakupu..."; /* Progress view title when starting the purchase */ -"subscription.progress.view.purchasing.subscription" = "Purchase in progress..."; +"subscription.progress.view.purchasing.subscription" = "Trwa zakup..."; /* Progress view title when restoring past subscription purchase */ -"subscription.progress.view.restoring.subscription" = "Restoring subscription..."; +"subscription.progress.view.restoring.subscription" = "Przywracanie subskrypcji..."; /* Remove from this device button */ -"subscription.remove.from.device.button" = "Remove From This Device"; +"subscription.remove.from.device.button" = "Usuń z tego urządzenia"; /* Remove from device confirmation dialog text */ -"subscription.remove.from.device.text" = "You will no longer be able to access your Privacy Pro subscription on this device. This will not cancel your subscription, and it will remain active on your other devices."; +"subscription.remove.from.device.text" = "Nie będziesz już mieć dostępu do subskrypcji Privacy Pro na tym urządzeniu. Nie spowoduje to anulowania Twojej subskrypcji, która pozostanie aktywna na pozostałych urządzeniach."; /* Remove from device confirmation dialog title */ -"subscription.remove.from.device.title" = "Remove from this device?"; +"subscription.remove.from.device.title" = "Usunąć z tego urządzenia?"; /* Remove subscription button text */ -"subscription.remove.subscription" = "Remove Subscription"; +"subscription.remove.subscription" = "Usuń subskrypcję"; /* Remove subscription cancel button text */ -"subscription.remove.subscription.cancel" = "Cancel"; +"subscription.remove.subscription.cancel" = "Anuluj"; /* Button text for general error message */ "subscription.restore.backend.error.button" = "Powrót do ustawień"; @@ -2509,28 +2671,43 @@ "subscription.restore.backend.error.title" = "Wystąpił błąd"; /* Alert for general error message */ -"subscription.restore.general.error.message" = "The App Store was unable to process your purchase. Please try again later."; +"subscription.restore.general.error.message" = "Nie udało się zrealizować zakupu w App Store. Spróbuj ponownie później."; /* Alert for general error title */ -"subscription.restore.general.error.title" = "Something Went Wrong"; +"subscription.restore.general.error.title" = "Wystąpił błąd"; /* Alert button text for restored purchase alert */ "subscription.restore.success.alert.button" = "OK"; /* Alert message for restored purchase */ -"subscription.restore.success.alert.message" = "Your purchases have been restored."; +"subscription.restore.success.alert.message" = "Zakupy zostały przywrócone."; /* Alert title for restored purchase */ -"subscription.restore.success.alert.title" = "You’re all set."; +"subscription.restore.success.alert.title" = "Wszystko gotowe."; + +/* Subtitle in header when subscribed */ +"subscription.subscribed" = "Subskrypcja"; /* Subscription Expired Data. This reads as 'Your subscription expired on (date)' */ "subscription.subscription.expired.caption" = "Twoja subskrypcja wygasła w dniu %@"; -/* Subscription expiration info. This reads as 'Your (monthly or annual) subscription expires on (date)' */ -"subscription.subscription.expiring.caption" = "Your %1$@ subscription expires on %2$@."; +/* Monthly subscription expiration info where parameter is expiration date. This reads as 'Your monthly subscription expires on (date)' */ +"subscription.subscription.expiring.monthly.caption" = "Twoja miesięczna subskrypcja wygasa w dniu %@."; + +/* Unknown period subscription expiration info where parameter is expiration date. This reads as 'Your subscription expires on (date)' */ +"subscription.subscription.expiring.unknown.caption" = "Twoja subskrypcja wygasa w dniu %@."; + +/* Annual subscription expiration info where parameter is expiration date. This reads as 'Your annual subscription expires on (date)' */ +"subscription.subscription.expiring.yearly.caption" = "Twoja roczna subskrypcja wygasa w dniu %@."; + +/* Monthly subscription renewal info where parameter is renewal date. This reads as 'Your monthly subscription renews on (date)' */ +"subscription.subscription.renewing.monthly.caption" = "Twoja miesięczna subskrypcja odnawia się w dniu %@."; + +/* Unknown period subscription renewal info where parameter is renewal date. This reads as 'Your subscription renews on (date)' */ +"subscription.subscription.renewing.unknown.caption" = "Twoja subskrypcja odnawia się w dniu %@."; -/* Subscription renewal info. This reads as 'Your (monthly or annual) subscription renews on (date)' */ -"subscription.subscription.renewing.caption" = "Your %1$@ subscription renews on %2$@."; +/* Annual subscription renewal info where parameter is renewal date. This reads as 'Your annual subscription renews on (date)' */ +"subscription.subscription.renewing.yearly.caption" = "Twoja roczna subskrypcja odnawia się w dniu %@."; /* Navigation bar Title for subscriptions */ "subscription.title" = "Privacy Pro"; @@ -2677,118 +2854,118 @@ "voiceSearch.footer.note.old" = "Dźwięk jest przetwarzany na urządzeniu. Dane nie są przechowywane ani udostępniane żadnym podmiotom, w tym DuckDuckGo."; /* Cancel action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.cancel" = "Dismiss"; +"vpn.access-revoked.alert.action.cancel" = "Odrzuć"; /* Primary action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.subscribe" = "Subscribe"; +"vpn.access-revoked.alert.action.subscribe" = "Subskrybuj"; /* Alert message for the alert when the Privacy Pro subscription expiress */ -"vpn.access-revoked.alert.message" = "Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"vpn.access-revoked.alert.message" = "Subskrybuj Privacy Pro, aby ponownie połączyć się z siecią DuckDuckGo VPN."; /* Alert title for the alert when the Privacy Pro subscription expires */ -"vpn.access-revoked.alert.title" = "VPN disconnected due to expired subscription"; +"vpn.access-revoked.alert.title" = "Sieć VPN została rozłączona z powodu wygasłej subskrypcji"; /* Title for the VPN widget onboarding screen */ "vpn.addWidget.settings.title" = "Znajdź i zaznacz DuckDuckGo. Następnie przesuń palcem do VPN i wybierz Dodaj widżet."; /* Title for the Cancel button of the VPN feedback form */ -"vpn.feedback-form.button.cancel" = "Cancel"; +"vpn.feedback-form.button.cancel" = "Anuluj"; /* Title for the Done button of the VPN feedback form */ -"vpn.feedback-form.button.done" = "Done"; +"vpn.feedback-form.button.done" = "Gotowe"; /* Title for the Submit button of the VPN feedback form */ -"vpn.feedback-form.button.submit" = "Submit"; +"vpn.feedback-form.button.submit" = "Prześlij"; /* Title for the Submitting state of the VPN feedback form */ -"vpn.feedback-form.button.submitting" = "Submitting…"; +"vpn.feedback-form.button.submitting" = "Przesyłanie..."; /* Title for the browser crash/freeze category of the VPN feedback form */ -"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN causes browser to crash or freeze"; +"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN powoduje awarię lub zawieszanie się przeglądarki"; /* Title for the 'VPN fails to connect' category of the VPN feedback form */ -"vpn.feedback-form.category.fails-to-connect" = "VPN fails to connect"; +"vpn.feedback-form.category.fails-to-connect" = "Nie można połączyć się z siecią VPN"; /* Title for the 'VPN feature request' category of the VPN feedback form */ -"vpn.feedback-form.category.feature-request" = "VPN feature request"; +"vpn.feedback-form.category.feature-request" = "Prośba o dodanie funkcji VPN"; /* Title for the category 'VPN causes issues with other apps or websites' category of the VPN feedback form */ -"vpn.feedback-form.category.issues-with-apps" = "VPN causes issues with other apps or websites"; +"vpn.feedback-form.category.issues-with-apps" = "VPN powoduje problemy z innymi aplikacjami lub witrynami"; /* Title for the local device connectivity category of the VPN feedback form */ -"vpn.feedback-form.category.local-device-connectivity" = "VPN won't let me connect to local device"; +"vpn.feedback-form.category.local-device-connectivity" = "Sieć VPN nie pozwala na połączenie się z urządzeniem lokalnym"; /* Title for the 'other VPN feedback' category of the VPN feedback form */ -"vpn.feedback-form.category.other" = "Other VPN feedback"; +"vpn.feedback-form.category.other" = "Inne opinie o funkcji VPN"; /* Title for the category selection state of the VPN feedback form */ -"vpn.feedback-form.category.select-category" = "Select a category"; +"vpn.feedback-form.category.select-category" = "WYBIERZ KATEGORIĘ"; /* Title for the 'VPN is too slow' category of the VPN feedback form */ -"vpn.feedback-form.category.too-slow" = "VPN connection is too slow"; +"vpn.feedback-form.category.too-slow" = "Połączenie VPN jest za wolne"; /* Title for the 'unable to install' category of the VPN feedback form */ -"vpn.feedback-form.category.unable-to-install" = "Unable to install VPN"; +"vpn.feedback-form.category.unable-to-install" = "Nie można zainstalować VPN"; /* Title for the feedback sent view description of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.description" = "Your feedback will help us improve the\nDuckDuckGo VPN."; +"vpn.feedback-form.sending-confirmation.description" = "Twoja opinia pomoże nam ulepszyć DuckDuckGo VPN."; /* Title for the feedback sending error text of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.error" = "We couldn't send your feedback right now, please try again."; +"vpn.feedback-form.sending-confirmation.error" = "Nie udało się teraz przesłać Twojej opinii, spróbuj ponownie."; /* Title for the feedback sent view title of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.title" = "Thank you!"; +"vpn.feedback-form.sending-confirmation.title" = "Dziękujemy!"; /* Toast message when the VPN feedback form is submitted successfully */ -"vpn.feedback-form.submitted.message" = "Thank You! Feedback submitted."; +"vpn.feedback-form.submitted.message" = "Dziękujemy! Informacje zostały przekazane."; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-1" = "Please describe what's happening, what you expected to happen, and the steps that led to the issue:"; +"vpn.feedback-form.text-1" = "Opisz, co się wydarzyło, czego oczekiwano i jakie działania doprowadziły do wystąpienia problemu:"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-2" = "In addition to the details entered into this form, your app issue report will contain:"; +"vpn.feedback-form.text-2" = "Oprócz szczegółów wprowadzonych w tym formularzu zgłoszenie problemu z aplikacją będzie zawierać:"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-3" = "• Whether specific DuckDuckGo features are enabled"; +"vpn.feedback-form.text-3" = "• Czy określone funkcje DuckDuckGo są włączone"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-4" = "• Aggregate DuckDuckGo app diagnostics"; +"vpn.feedback-form.text-4" = "• Zbiorcze dane diagnostyczne aplikacji DuckDuckGo"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-5" = "By tapping \"Submit\" I agree that DuckDuckGo may use the information in this report for purposes of improving the app's features."; +"vpn.feedback-form.text-5" = "Dotykając opcji „Prześlij”, zgadzam się na wykorzystanie przez DuckDuckGo informacji zawartych w tym zgłoszeniu w celu ulepszenia funkcji aplikacji."; /* Title for each screen of the VPN feedback form */ -"vpn.feedback-form.title" = "Help Improve the DuckDuckGo VPN"; +"vpn.feedback-form.title" = "Pomóż ulepszyć DuckDuckGo VPN"; /* VPN settings screen cell text for adding the VPN widget to the home screen */ "vpn.settings.add.widget" = "Dodaj widżet VPN do ekranu głównego"; /* Disclaimer for the DNS Server section on the DNS Server screen */ -"vpn.settings.dns.section-disclaimer" = "Using a custom DNS server can impact browsing speeds and expose your activity to third parties if the server isn't secure or reliable."; +"vpn.settings.dns.section-disclaimer" = "Korzystanie z niestandardowego serwera DNS może mieć wpływ na prędkość przeglądania stron internetowych oraz narażać aktywność na dostęp nieupoważnionych osób, jeśli serwer nie jest bezpieczny lub sprawdzony."; /* Header text for the DNS section on the VPN Settings screen */ "vpn.settings.dns.section-header" = "DNS"; /* Title for the Apply button on the DNS Server setting screen */ -"vpn.settings.dns.server.apply.button.title" = "Apply"; +"vpn.settings.dns.server.apply.button.title" = "Zastosuj"; /* Default value for the DNS Server row on the VPN Settings screen */ "vpn.settings.dns.server.default.value" = "DuckDuckGo"; /* Title for the IPv4 Address setting */ -"vpn.settings.dns.server.ipv4.title" = "IPv4 Address"; +"vpn.settings.dns.server.ipv4.title" = "Adres IPv4"; /* Custom option for the DNS Server setting */ -"vpn.settings.dns.server.option.custom" = "Custom"; +"vpn.settings.dns.server.option.custom" = "Niestandardowy"; /* Recommended option for the DNS Server setting */ -"vpn.settings.dns.server.option.default" = "DuckDuckGo (Recommended)"; +"vpn.settings.dns.server.option.default" = "DuckDuckGo (zalecane)"; /* Title for the DNS Server setting screen */ -"vpn.settings.dns.server.screen.title" = "DNS Server"; +"vpn.settings.dns.server.screen.title" = "Serwer DNS"; /* Title for the DNS Server row on the VPN Settings screen */ -"vpn.settings.dns.server.title" = "DNS Server"; +"vpn.settings.dns.server.title" = "Serwer DNS"; /* Title for the button to enable push notifications in system settings */ "waitlist.allow-notifications" = "Zezwalaj na powiadomienia"; diff --git a/DuckDuckGo/pl.lproj/OmniBar.strings b/DuckDuckGo/pl.lproj/OmniBar.strings index 932c1b562e..7f4269c4a9 100644 --- a/DuckDuckGo/pl.lproj/OmniBar.strings +++ b/DuckDuckGo/pl.lproj/OmniBar.strings @@ -7,6 +7,9 @@ /* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "Dt8-We-5tV"; */ "Dt8-We-5tV.accessibilityLabel" = "Wyczyść tekst"; +/* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "gb5-wL-4OW"; */ +"gb5-wL-4OW.accessibilityLabel" = "Wyczyść tekst"; + /* Class = "UIButton"; accessibilityLabel = "Share"; ObjectID = "hor-Jd-3kw"; */ "hor-Jd-3kw.accessibilityLabel" = "Udostępnij"; diff --git a/DuckDuckGo/pl.lproj/Settings.strings b/DuckDuckGo/pl.lproj/Settings.strings index b75934c547..ceb6dedc8c 100644 --- a/DuckDuckGo/pl.lproj/Settings.strings +++ b/DuckDuckGo/pl.lproj/Settings.strings @@ -10,9 +10,6 @@ /* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "5YD-Od-uDU"; */ "5YD-Od-uDU.text" = "Automatyczne czyszczenie danych"; -/* Class = "UIButton"; normalTitle = "Settings"; ObjectID = "8YG-7Q-Czd"; */ -"8YG-7Q-Czd.normalTitle" = "Ustawienia"; - /* Class = "UILabel"; text = "App Exit, Inactive for 1 Hour"; ObjectID = "9EC-lx-77p"; */ "9EC-lx-77p.text" = "Zamknięcie aplikacji, nieaktywna przez 1 godzinę"; @@ -76,9 +73,6 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Niezabezpieczone witryny"; -/* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ -"ssa-zd-L3T.title" = "Rozmiar tekstu"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Pokaż klawiaturę"; diff --git a/DuckDuckGo/pt.lproj/DaxOnboarding.strings b/DuckDuckGo/pt.lproj/DaxOnboarding.strings index e672004263..2a19abfbb7 100644 --- a/DuckDuckGo/pt.lproj/DaxOnboarding.strings +++ b/DuckDuckGo/pt.lproj/DaxOnboarding.strings @@ -1,12 +1,6 @@ /* Class = "UIButton"; normalTitle = "Hide"; ObjectID = "hgy-75-OvJ"; */ "hgy-75-OvJ.normalTitle" = "Ocultar"; -/* Class = "UILabel"; text = "Welcome to\nDuckDuckGo!"; ObjectID = "Lvy-9G-3gr"; */ -"Lvy-9G-3gr.text" = "Damos-lhe as boas-vindas ao\nDuckDuckGo!"; - -/* Class = "UIButton"; normalTitle = "Let’s Do It!"; ObjectID = "oAv-Zt-uIt"; */ -"oAv-Zt-uIt.normalTitle" = "Vamos lá!"; - /* Class = "UIButton"; normalTitle = "Button"; ObjectID = "zJt-F9-L48"; */ "zJt-F9-L48.normalTitle" = "Botão"; diff --git a/DuckDuckGo/pt.lproj/Localizable.strings b/DuckDuckGo/pt.lproj/Localizable.strings index f8b8487247..5cf8042b4a 100644 --- a/DuckDuckGo/pt.lproj/Localizable.strings +++ b/DuckDuckGo/pt.lproj/Localizable.strings @@ -19,6 +19,15 @@ /* Button label for OK action */ "action.ok" = "OK"; +/* Title for text zoom menu item. %d%% is replaced with percent, e.g. 56% so do not change that please. */ +"action.text-zoom-menu-item" = "Zoom (%d%%)"; + +/* Text zoom menu item */ +"action.text-zoom-sheet-menu-item" = "Zoom"; + +/* Title for text zoom sheet view. %d%% is replaced with percent, e.g. 56% so do not change that please. */ +"action.text-zoom-sheet-title" = "Zoom do texto (%d%%)"; + /* Add action - button shown in alert */ "action.title.add" = "Adicionar"; @@ -332,7 +341,7 @@ "autofill.keep-enabled.alert.disable" = "Desabilitar (fora)"; /* A link that takes the user to the DuckDuckGo help pages explaining password managers */ -"autofill.learn.more.link.title" = "Learn More"; +"autofill.learn.more.link.title" = "Saiba mais"; /* Button displayed after saving/updating an autofill login that takes the user to the saved login */ "autofill.login-save-action-button.toast" = "Visualizar"; @@ -968,9 +977,6 @@ /* ad = advertisment */ "dax.onboarding.home.subsequent" = "Você consegue!\n\nLembre-se: sempre que navega comigo, um anúncio assustador perde as suas asas. 👍"; -/* No comment provided by engineer. */ -"dax.onboarding.message" = "A Internet pode ser um pouco assustadora.\n\nMas não se preocupe! Pesquisar e navegar em privado é mais fácil do que pensa."; - /* No comment provided by engineer. */ "Debug" = "Depurar"; @@ -1071,7 +1077,7 @@ "duckPlayer.never.label" = "Nunca"; /* Body text for the modal feature explanation */ -"duckplayer.presentation.modal.body" = "O Duck Player permite-te ver o YouTube sem anúncios segmentados como se estivesses no cinema no DuckDuckGo, e o que vês não vai influenciar as tuas recomendações."; +"duckplayer.presentation.modal.body" = "O Duck Player permite-te ver o YouTube sem anúncios segmentados no DuckDuckGo, e o que vês não vai influenciar as tuas recomendações."; /* Button that will dismiss the modal */ "duckplayer.presentation.modal.dismiss-button" = "Entendi!"; @@ -1079,17 +1085,23 @@ /* Two line title (separated by \n) for the feature explanation */ "duckplayer.presentation.modal.title" = "Já não suportas ver anúncios no YouTube? Experimenta o Duck Player!"; +/* Two line title (separated by \n) for the feature explanation */ +"duckplayer.presentation.modal.title.youtube" = "Já não suportas ver anúncios no YouTube? Experimenta o Duck Player!"; + /* Footer label in the settings screen for Duck Player */ "duckplayer.settings.footer" = "O DuckDuckGo fornece todos os Privacy Essentials de que precisas para te protegeres enquanto navegas na Web."; /* Text explaining what Duck Player is in the settings screen. */ -"duckplayer.settings.info-text" = "O Duck Player oferece uma experiência de visualização limpa sem anúncios personalizados e evita que as atividades de visualização influenciem as recomendações do YouTube."; +"duckplayer.settings.info-text" = "O Duck Player permite-te ver o YouTube sem anúncios segmentados no DuckDuckGo, e o que vês não vai influenciar as tuas recomendações."; /* Button that takes the user to learn more about Duck Player. */ "duckplayer.settings.learn-more" = "Saiba mais"; +/* Settings screen cell text for DuckPlayer settings to open in new tab */ +"duckplayer.settings.open-new-tab-label" = "Abrir Duck Player num novo separador"; + /* Settings screen cell text for DuckPlayer settings */ -"duckplayer.settings.open-videos-in" = "Abrir vídeos no Duck Player"; +"duckplayer.settings.open-videos-in" = "Abrir vídeos do YouTube no Duck Player"; /* Settings screen cell text for DuckPlayer settings */ "duckplayer.settings.title" = "Duck Player"; @@ -1160,9 +1172,6 @@ /* Title for prompt to sign up for email protection */ "email.signup-prompt.title" = "Ocultar o teu e-mail e\nbloquear rastreadores"; -/* No comment provided by engineer. */ -"empty!" = "empty!"; - /* Empty list state placholder */ "empty.bookmarks" = "Ainda não foram adicionados marcadores"; @@ -1313,6 +1322,39 @@ /* Confirmation button */ "feedback.form.submit" = "Submeter"; +/* Title of the feedback form */ +"feedback.form.title" = "Ajuda a melhorar o Privacy Pro"; + +/* Category for Identity Theft Restoration feedback */ +"feedback.general.category.itr" = "Identity Theft Restoration"; + +/* Category for Personal Info Removal feedback */ +"feedback.general.category.pir" = "Personal Info Removal"; + +/* Category for subscription and payments feedback */ +"feedback.general.category.ppro" = "Subscrição e Pagamentos"; + +/* Prompt to select a category for general feedback */ +"feedback.general.category.select" = "SELECIONA UMA CATEGORIA"; + +/* Category for VPN feedback */ +"feedback.general.category.vpn" = "VPN"; + +/* Category for access code issues */ +"feedback.itr.category.access-code" = "Problema com o código de acesso"; + +/* Category for issues contacting an advisor */ +"feedback.itr.category.cant-contact-advisor" = "Não é possível contactar o consultor"; + +/* Prompt to select a category for Identity Theft Restoration feedback */ +"feedback.itr.category.select" = "SELECIONA UMA CATEGORIA"; + +/* Category for other Identity Theft Restoration issues */ +"feedback.itr.category.something-else" = "Outra coisa"; + +/* Category for unhelpful advisor calls */ +"feedback.itr.category.unhelpful" = "A chamada para o consultor não foi útil"; + /* No comment provided by engineer. */ "feedback.negative.form.genericPlaceholder" = "Seja o mais específico possível"; @@ -1352,6 +1394,24 @@ /* No comment provided by engineer. */ "feedback.performance.slowLoading" = "Páginas Web ou resultados de pesquisa carregados lentamente"; +/* Category for when scan finds incorrect records */ +"feedback.pir.category.not-me" = "A verificação encontrou registos que não correspondem a mim"; + +/* Category for when scan doesn't find info on a specific site */ +"feedback.pir.category.nothing-on-site" = "A verificação não encontrou a minha informação num site específico"; + +/* Category for other Personal Info Removal issues */ +"feedback.pir.category.other" = "Outra coisa"; + +/* Category for when the removal process is stuck */ +"feedback.pir.category.removal-stuck" = "O processo de remoção está bloqueado"; + +/* Category for when the scan is stuck */ +"feedback.pir.category.scan-stuck" = "A verificação de registos está bloqueada"; + +/* Prompt to select a category for Personal Info Removal feedback */ +"feedback.pir.category.select" = "SELECIONA UMA CATEGORIA"; + /* Header above input field */ "feedback.positive.form.header" = "Partilhar detalhes"; @@ -1370,6 +1430,15 @@ /* Button encouraging uses to share details aboout their feedback */ "feedback.positive.submit" = "Partilhar detalhes"; +/* Category for other Privacy Pro issues */ +"feedback.ppro.category.other" = "Outra coisa"; + +/* Category for one-time password issues */ +"feedback.ppro.category.otp" = "Problema com a palavra-passe de utilização única"; + +/* Prompt to select a category for Privacy Pro feedback */ +"feedback.ppro.category.select" = "SELECIONA UMA CATEGORIA"; + /* No comment provided by engineer. */ "feedback.start.footer" = "Os seus comentários anônimos são importantes para nós."; @@ -1520,12 +1589,6 @@ /* No comment provided by engineer. */ "keyCommandShowAllTabs" = "Mostrar todos os separadores"; -/* Please preserve newline character */ -"launchscreenWelcomeMessage" = "Damos-lhe as boas-vindas ao\nDuckDuckGo!"; - -/* No comment provided by engineer. */ -"LOREM IPSUM" = "LOREM IPSUM"; - /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "O DuckDuckGo para Mac tem a velocidade de que necessita, as funcionalidades de navegação que espera e os nossos Privacy Essentials líderes no setor."; @@ -1577,62 +1640,83 @@ /* Edit button */ "navigation.title.edit" = "Editar"; -/* String indicating NetP is connected when viewed from the settings screen */ -"netP.cell.connected" = "Connected"; - -/* String indicating NetP is disconnected when viewed from the settings screen */ -"netP.cell.disconnected" = "Not connected"; - -/* Title for the DuckDuckGo VPN feature in settings */ -"netP.settings.title" = "VPN"; +/* String indicating NetP is snoozing when viewed from the settings screen */ +"netP.cell.snoozing" = "Em suspensão"; /* Title for the DuckDuckGo VPN feature */ "netP.title" = "VPN do DuckDuckGo"; +/* Button title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.action" = "Adicionar widget"; + +/* Message for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.message" = "Ativa e desativa a VPN diretamente no ecrã inicial."; + +/* Title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.title" = "Adicionar widget de VPN"; + /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN desativada devido a subscrição expirada. Subscreve o Privacy Pro para voltar a ligar a DuckDuckGo VPN."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Falha na ligação da Network Protection. Tenta novamente mais tarde."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "A VPN DuckDuckGo não conseguiu estabelecer ligação. Tenta novamente mais tarde."; /* Title for the VPN FAQ screen. */ "network.protection.faq.title" = "Perguntas frequentes sobre a DuckDuckGo VPN"; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "A Network Protection foi interrompida. A tentar ligar novamente..."; +/* Message for tooltip about geoswitching */ +"network.protection.geoswitching.tip.message" = "Podes personalizar a localização da tua VPN ligando-te a qualquer um dos nossos servidores em todo o mundo."; + +/* Title for tooltip about geoswitching */ +"network.protection.geoswitching.tip.title" = "Alterar a localização"; + +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "A VPN DuckDuckGo foi interrompida. A tentar ligar novamente…"; /* Message for the network protection invite dialog */ -"network.protection.invite.dialog.message" = "Enter your invite code to get started."; +"network.protection.invite.dialog.message" = "Introduz o código de convite para começares."; /* Title for the network protection invite screen */ "network.protection.invite.dialog.title" = "Convidamos-te a experimentar a DuckDuckGo VPN"; /* Prompt for the network protection invite code text field */ -"network.protection.invite.field.prompt" = "Invite Code"; +"network.protection.invite.field.prompt" = "Código de Convite"; /* Message for the network protection invite success view */ -"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; +"network.protection.invite.success.message" = "Oculta a tua localização dos sites e a tua atividade online dos fornecedores de Internet e de outras pessoas na tua rede."; /* Title for the network protection invite success view */ -"network.protection.invite.success.title" = "Success! You’re in."; +"network.protection.invite.success.title" = "Sucesso! Já está."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; /* Title text for an iOS quick action that opens VPN settings */ "network.protection.quick-action.open-vpn" = "Abrir VPN"; +/* Button title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.action" = "Saiba mais"; + +/* Message for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.message" = "Podes utilizar sites ou aplicações que bloqueiam o tráfego da VPN suspendendo a ligação VPN."; + +/* Title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.title" = "Evitar conflitos de VPN"; + +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN suspensa por %@"; + /* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ "network.protection.status.connected.format" = "Ligada · %@"; /* The label for the NetP VPN when connecting */ -"network.protection.status.connecting" = "Connecting..."; +"network.protection.status.connecting" = "A ligar…"; /* The label for the NetP VPN when disconnected */ -"network.protection.status.disconnected" = "Not connected"; +"network.protection.status.disconnected" = "Não ligada"; /* The label for the NetP VPN when disconnecting */ -"network.protection.status.disconnecting" = "Disconnecting..."; +"network.protection.status.disconnecting" = "A desligar…"; /* Message label text for the status view when VPN is disconnected */ "network.protection.status.header.message.off" = "Liga a VPN para protegeres todo o tráfego de Internet\ndo teu dispositivo."; @@ -1646,26 +1730,41 @@ /* Header title label text for the status view when VPN is connected */ "network.protection.status.header.title.on" = "A DuckDuckGo VPN está Ativada"; +/* Header title label text for the status view when VPN is snoozing */ +"network.protection.status.header.title.snoozed" = "A DuckDuckGo VPN está suspensa"; + /* The status view 'Share Feedback' button which is shown inline on the status view after the temporary free use footer text */ -"network.protection.status.menu.share.feedback" = "Share Feedback"; +"network.protection.status.menu.share.feedback" = "Partilhar comentários"; + +/* The label for the NetP VPN when paused */ +"network.protection.status.paused" = "Em pausa"; + +/* The label for when NetP VPN is snoozing plus the length of time remaining formatted as '0:00' */ +"network.protection.status.snoozing.format" = "Em suspensão, %@ restante"; + +/* Snooze button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.snooze" = "Suspender por 20 minutos"; + +/* Wake Up button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.wake-up" = "Ativar"; /* Connection details label shown in NetworkProtection's status view. */ -"network.protection.status.view.connection.details" = "Connection Details"; +"network.protection.status.view.connection.details" = "Detalhes da ligação"; /* Custom DNS label shown in NetworkProtection's status view. */ -"network.protection.status.view.custom.dns" = "DNS Server"; +"network.protection.status.view.custom.dns" = "Servidor DNS"; /* Generic connection failed error message shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.message" = "Please try again later."; +"network.protection.status.view.error.connection.failed.message" = "Tenta novamente mais tarde."; /* Generic connection failed error title shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; +"network.protection.status.view.error.connection.failed.title" = "Falha ao ligar."; /* IP Address label shown in NetworkProtection's status view. */ -"network.protection.status.view.ip.address" = "IP Address"; +"network.protection.status.view.ip.address" = "Endereço IP"; /* Location label shown in NetworkProtection's status view. */ -"network.protection.status.view.location" = "Location"; +"network.protection.status.view.location" = "Localização"; /* Label shown on the title of the settings section in NetworkProtection's status view. */ "network.protection.status.view.settings.section.title" = "Gerir"; @@ -1673,12 +1772,15 @@ /* Title label text for the status view when netP is disconnected */ "network.protection.status.view.title" = "VPN"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "A Network Protection está ativada. A tua localização e atividade online estão protegidas."; +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "A VPN DuckDuckGo está ativada. A tua localização e atividade online estão protegidas."; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "A encaminhar o tráfego do dispositivo através de %@."; +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "A suspensão da VPN terminou. A encaminhar o tráfego do dispositivo por %@."; + /* Title for the button to link to the iOS app settings and enable notifications app-wide. */ "network.protection.turn.on.notifications.button.title" = "Ativar as notificações"; @@ -1689,13 +1791,13 @@ "network.protection.vpn.about" = "Acerca de"; /* Section header for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.section.header" = "Notifications"; +"network.protection.vpn.alerts.section.header" = "Notificações"; /* List section footer for the toggle for VPN alerts. */ "network.protection.vpn.alerts.toggle.section.footer" = "Recebe uma notificação se a tua ligação cair ou o estado da VPN mudar."; /* Title for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.toggle.title" = "VPN Notifications"; +"network.protection.vpn.alerts.toggle.title" = "Notificações da VPN"; /* Title for the data volume section in the VPN status screen */ "network.protection.vpn.data-volume" = "Volume de Dados"; @@ -1704,13 +1806,13 @@ "network.protection.vpn.exclude.local.networks.setting.footer" = "Permitir que o tráfego local ignore a VPN e ligue a dispositivos na tua rede local, como uma impressora."; /* Header text for the Exclude Local Networks setting item. */ -"network.protection.vpn.exclude.local.networks.setting.header" = "General"; +"network.protection.vpn.exclude.local.networks.setting.header" = "Geral"; /* Title for the Exclude Local Networks setting item. */ "network.protection.vpn.exclude.local.networks.setting.title" = "Excluir redes locais"; /* Title for the VPN Location screen's All Countries section. */ -"network.protection.vpn.location.all.countries.section.title" = "All Countries"; +"network.protection.vpn.location.all.countries.section.title" = "Todos os países"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.connected" = "Localização Ligada"; @@ -1725,7 +1827,7 @@ "network.protection.vpn.location.recommended.section.footer" = "Ligar automaticamente ao servidor mais próximo que encontrarmos."; /* Title for the VPN Location screen's Recommended section. */ -"network.protection.vpn.location.recommended.section.title" = "Recommended"; +"network.protection.vpn.location.recommended.section.title" = "Recomendada"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.selected" = "Localização Selecionada"; @@ -1743,13 +1845,13 @@ "network.protection.vpn.preferred.location.nearest" = "Localização Mais Próxima"; /* Title for the Preferred Location VPN Settings item. */ -"network.protection.vpn.preferred.location.title" = "Preferred Location"; +"network.protection.vpn.preferred.location.title" = "Localização preferida"; /* Footer text for the DNS server setting item. */ -"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo routes DNS queries through our DNS servers so your internet provider can't see what websites you visit."; +"network.protection.vpn.secure.dns.setting.footer" = "O DuckDuckGo encaminha as consultas DNS através dos nossos servidores DNS para que o teu fornecedor de Internet não possa ver os sites que visitas."; /* Title for the FAQ row in the VPN status screen. */ -"network.protection.vpn.settings.faq" = "FAQs and Support"; +"network.protection.vpn.settings.faq" = "Perguntas Frequentes e Apoio Técnico"; /* Title for the feedback row in the VPN status screen. */ "network.protection.vpn.settings.share-feedback" = "Partilhar Feedback Sobre a VPN"; @@ -1931,6 +2033,66 @@ /* Deactivate button */ "pm.deactivate" = "Desativar"; +/* Label for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.label" = "E-mail (opcional)"; + +/* Placeholder for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.placeholder" = "name@email.com"; + +/* Placeholder for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.placeholder" = "Envia-nos o teu feedback…"; + +/* Title for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.title" = "Feedback geral"; + +/* Title for the ITR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-itr-problem.title" = "Identity Theft Restoration"; + +/* Title for the PIR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-pir-problem.title" = "Personal Information Removal"; + +/* Title for the Subscriptions and Payments category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-ppro-problem.title" = "Subscrições e Pagamentos"; + +/* Placeholder for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.placeholder" = "Diz-nos o que se passa…"; + +/* Title for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.title" = "Comunicar um problema"; + +/* Title for the VPN category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-vpn-problem.title" = "VPN"; + +/* Placeholder for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.placeholder" = "Que funcionalidade gostarias de ver?"; + +/* Title for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.title" = "Pedido de funcionalidade"; + +/* Title for the category selection section in the Privacy Pro feedback form */ +"ppro.feedback-form.select-category.title" = "SELECIONA UMA CATEGORIA"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-1" = "Encontraste um problema que não é abordado no nosso [centro de ajuda](duck://)? Queremos perceber do que se trata.\n\nFornece um e-mail se quiseres que te contactemos sobre este problema (podemos não conseguir responder a todos os problemas):"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-2" = "Além dos detalhes introduzidos acima, enviamos algumas informações anónimas com o teu feedback:"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-3" = "• Se algumas funcionalidades do navegador estão ativas"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-4" = "• Diagnósticos agregados da aplicação (por exemplo, códigos de erro)"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-5" = "Ao tocares em \"Enviar\", concordas que o DuckDuckGo pode utilizar as informações enviadas para melhorar a aplicação."; + +/* Title for the text box in the Privacy Pro feedback form */ +"ppro.feedback-form.text-box.title" = "Comentários"; + +/* Title for the Privacy Pro feedback form */ +"ppro.feedback-form.title" = "Enviar comentário"; + /* Button title for sync bookmarks limits exceeded warning to go to manage bookmarks */ "prefrences.sync.bookmarks-limit-exceeded-action" = "Gerir marcadores"; @@ -2020,6 +2182,18 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Favoritos"; +/* Name of the option the user can chose to give general browser feedback */ +"send.browser.feedback.general-feedback" = "Feedback geral"; + +/* Name of the option the user can chose to give browser feedback about a problem they enountered */ +"send.browser.feedback.report-problem" = "Comunicar um problema"; + +/* Name of the option the user can chose to give browser feedback about a feature they would like */ +"send.browser.feedback.request-feature" = "Pedir uma funcionalidade"; + +/* Title of the picker where the user can chose the category of the feedback they want ot send. */ +"send.browser.feedback.select-category" = "SELECIONA UMA CATEGORIA"; + /* Settings cell for About DDG */ "settings.about.ddg" = "Sobre o DuckDuckGo"; @@ -2075,6 +2249,9 @@ /* Section footer Autolock description */ "settings.autolock.description" = "Se estiver definido um Touch ID, Face ID ou código de acesso de sistema, é-te pedido que desbloqueies a aplicação quando a abres."; +/* Settings cell for Browser Feedback */ +"settings.browser.feedback" = "Feedback do navegador"; + /* Settings screen cell text for Automatically Clearing Data */ "settings.clear.data" = "Limpar os dados automaticamente"; @@ -2173,7 +2350,7 @@ "settings.ppro" = "Privacy Pro"; /* Title for Link in the Footer of Privacy Pro section */ -"settings.ppro.footer" = "Privacy Policy and Terms of Service"; +"settings.ppro.footer" = "Política de Privacidade e Termos de Utilização"; /* Settings screen cell for long press previews */ "settings.previews" = "Pré-visualizações ao premir prolongadamente"; @@ -2199,23 +2376,20 @@ /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Define a Posição da Barra de Endereços"; -/* Subscription activation pending description */ -"settings.subscription.activation.pending.description" = "This is taking longer than usual, please check back later."; +/* Privacy pro description subtitle in settings when the is activating */ +"settings.subscription.activating" = "A ativar…"; -/* Subscription activation pending title */ -"settings.subscription.activation.pending.title" = "A tua subscrição está a ser ativada"; +/* Subscription activation pending description */ +"settings.subscription.activation.pending.description" = "Está a demorar mais tempo do que o habitual, volta mais tarde."; /* Data Broker protection cell subtitle for privacy pro */ -"settings.subscription.DBP.subtitle" = "Remove your info from sites that sell it"; +"settings.subscription.DBP.subtitle" = "Remove as tuas informações de sites que as vendem"; /* Data Broker protection cell title for privacy pro */ "settings.subscription.DBP.title" = "Personal Information Removal"; -/* Privacy pro description subtext */ -"settings.subscription.description" = "Privacidade mais simplificada com três novas proteções:"; - /* I have a Subscription button text for privacy pro */ -"settings.subscription.existing.subscription" = "I Have a Subscription"; +"settings.subscription.existing.subscription" = "Tenho uma Subscrição"; /* Subscription expired description */ "settings.subscription.expired.comment" = "Subscreve novamente para continuares a utilizar o Privacy Pro"; @@ -2223,23 +2397,26 @@ /* Subscription expired tittle message */ "settings.subscription.expired.title" = "A tua subscrição Privacy Pro expirou"; -/* Privacy pro features list */ -"settings.subscription.features" = " • VPN\n • Personal Information Removal\n • Identity Theft Restoration"; - /* Identity theft restoration cell subtitle for privacy pro */ -"settings.subscription.ITR.subtitle" = "If your identity is stolen, we'll help restore it"; +"settings.subscription.ITR.subtitle" = "Se a tua identidade for roubada, ajudaremos a restaurá-la"; /* Identity theft restoration cell title for privacy pro */ "settings.subscription.ITR.title" = "Identity Theft Restoration"; /* Get Privacy Pro button text for privacy pro */ -"settings.subscription.learn.more" = "Get Privacy Pro"; +"settings.subscription.learn.more" = "Obter Privacy Pro"; /* Subscription Settings button text for privacy pro */ -"settings.subscription.manage" = "Subscription Settings"; +"settings.subscription.manage" = "Definições de Subscrição"; -/* Call to action title for Privacy Pro */ -"settings.subscription.subscribe" = "Subscribe to Privacy Pro"; +/* Privacy Pro description subtitle in settings */ +"settings.subscription.row.description" = "Inclui a nossa VPN e a Identity Theft Restoration."; + +/* Call to action title for Privacy Pro settings */ +"settings.subscription.subscribe" = "Protege a tua ligação e identidade com o Privacy Pro"; + +/* Privacy pro description subtitle in settings */ +"settings.subscription.us.description" = "Inclui a nossa VPN, a Personal Information Removal e a Identity Theft Restoration."; /* VPN cell title for privacy pro */ "settings.subscription.VPN.title" = "VPN"; @@ -2251,7 +2428,7 @@ "settings.sync" = "Sincronização e cópia de segurança"; /* Settings screen cell text for text size */ -"settings.text.size" = "Tamanho do texto"; +"settings.text.size" = "Zoom do texto predefinido"; /* Settings screen cell text for theme */ "settings.theme" = "Tema"; @@ -2308,67 +2485,64 @@ "siteFeedback.urlPlaceholder" = "Que site está com falhas?"; /* Subscription Activation Info */ -"subscription.activate..header.description" = "Access your Privacy Pro subscription on this device via Apple ID or an email address."; +"subscription.activate..header.description" = "Acede à tua subscrição Privacy Pro neste dispositivo através do teu Apple ID ou de um endereço de e-mail."; /* Button for adding email address to subscription */ -"subscription.activate.add.email.button" = "Add Email"; +"subscription.activate.add.email.button" = "Adicionar E-mail"; /* Apple ID option for activation */ "subscription.activate.appleid" = "Apple ID"; /* Button text for restoring purchase via Apple ID */ -"subscription.activate.appleid.button" = "Restore Purchase"; +"subscription.activate.appleid.button" = "Restaurar Compra"; /* Description for Apple ID activation */ -"subscription.activate.appleid.description" = "Restore your purchase to activate your subscription on this device."; +"subscription.activate.appleid.description" = "Restaura a tua compra para ativares a tua subscrição neste dispositivo."; /* Subscription Activation Info */ -"subscription.activate.description" = "Your subscription is automatically available in DuckDuckGo on any device signed in to your Apple ID."; +"subscription.activate.description" = "A tua subscrição está automaticamente disponível no DuckDuckGo em qualquer dispositivo com sessão iniciada com o teu Apple ID."; /* Button for editing email address added to subscription */ -"subscription.activate.edit.email.button" = "Edit Email"; +"subscription.activate.edit.email.button" = "Editar e-mail"; /* View Title for editing your email account */ -"subscription.activate.edit.email.title" = "Edit Email"; +"subscription.activate.edit.email.title" = "Editar e-mail"; /* Email option for activation */ -"subscription.activate.email" = "Email"; +"subscription.activate.email" = "E-mail"; /* Restore button title for Email */ -"subscription.activate.email.button" = "Enter Email"; +"subscription.activate.email.button" = "Introduzir E-mail"; /* Description for Email activation */ -"subscription.activate.email.description" = "Use your email to activate your subscription on this device."; +"subscription.activate.email.description" = "Utiliza o teu e-mail para ativares a tua subscrição neste dispositivo."; /* Activate subscription title */ "subscription.activate.email.title" = "Ativar Subscrição"; /* Button title for cancelling email deletion */ -"subscription.activate.manage.email.cancel" = "Cancel"; +"subscription.activate.manage.email.cancel" = "Cancelar"; /* Button title for confirming email deletion */ "subscription.activate.manage.email.OK" = "OK"; /* Restore button title for AppleID */ -"subscription.activate.restore.apple" = "Restore Purchase"; +"subscription.activate.restore.apple" = "Restaurar Compra"; /* Subscription Activation Title */ -"subscription.activate.title" = "Activate your subscription on this device"; +"subscription.activate.title" = "Ativa a tua subscrição neste dispositivo"; /* Resend activation instructions button */ -"subscription.add.device.resend.instructions" = "Resend Instructions"; +"subscription.add.device.resend.instructions" = "Reenviar instruções"; /* Add email to an existing subscription */ -"subscription.add.email" = "Add an email address to activate your subscription on your other devices. We’ll only use this address to verify your subscription."; +"subscription.add.email" = "Adiciona um endereço de e-mail para ativares a tua subscrição nos teus outros dispositivos. Usaremos este endereço apenas para validar a tua subscrição."; /* View title for adding email to subscription */ -"subscription.add.email.title" = "Add Email"; - -/* Title for Alert messages */ -"subscription.alert.title" = "subscription.alert.title"; +"subscription.add.email.title" = "Adicionar E-mail"; /* Subscription availability message on Apple devices */ -"subscription.available.apple" = "Privacy Pro is available on any device signed in to the same Apple ID."; +"subscription.available.apple" = "O Privacy Pro está disponível em qualquer dispositivo com sessão iniciada com o mesmo ID Apple."; /* Text for the manage billing page */ "subscription.billing.google.text" = "A tua subscrição foi adquirida na Google Play Store. Para renovares a tua subscrição, abre as definições da subscrição na Google Play Store num dispositivo com sessão iniciada na Conta Google usada para comprar a subscrição."; @@ -2376,89 +2550,86 @@ /* Title for the manage billing page */ "subscription.billing.google.title" = "Planos de Subscrição"; -/* Subscription annual billing period type */ -"subscription.billing.period.annual" = "annual"; - -/* Subscription monthly billing period type */ -"subscription.billing.period.monthly" = "monthly"; - /* Subscription Removal confirmation message */ -"subscription.cancel.message" = "Your subscription has been removed from this device."; +"subscription.cancel.message" = "A tua subscrição foi removida deste dispositivo."; /* Change plan or cancel title */ -"subscription.change.plan" = "Update Plan or Cancel"; +"subscription.change.plan" = "Atualizar Plano ou Cancelar"; /* Navigation Button for closing subscription view */ -"subscription.close" = "Close"; +"subscription.close" = "Fechar"; /* Title for Confirm messages */ -"subscription.confirm.title" = "Are you sure?"; +"subscription.confirm.title" = "Tens a certeza?"; /* Header for section for activating subscription on other devices */ -"subscription.devices.header" = "Activate on Other Devices"; +"subscription.devices.header" = "Ativar Noutros Dispositivos"; /* Footer for section for activating subscription on other devices when email was not yet added */ -"subscription.devices.no.email.footer" = "Add an optional email to your subscription or use your Apple ID to access Privacy Pro on other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.no.email.footer" = "Adiciona um e-mail opcional à tua subscrição para acederes ao Privacy Pro noutros dispositivos. **[Sabe mais](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Footer for section for activating subscription on other devices when email is added */ -"subscription.devices.with.email.footer" = "Use this email to activate your subscription in Settings > Privacy Pro in the DuckDuckGo app on your other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.with.email.footer" = "Utiliza este e-mail para ativares a tua subscrição em Definições > Privacy Pro na aplicação DuckDuckGo nos teus restantes dispositivos. **[Sabe mais](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Alert content for not found subscription */ -"subscription.email.inactive.alert.message" = "The subscription associated with this email is no longer active."; +"subscription.email.inactive.alert.message" = "A tua subscrição associada a este e-mail já não está ativa."; /* Alert title for not found subscription */ -"subscription.email.inactive.alert.title" = "Subscription Not Found"; +"subscription.email.inactive.alert.title" = "Subscrição não encontrada"; /* Alert content for not found subscription */ -"subscription.expired.alert.message" = "The subscription associated with this Apple ID is no longer active."; +"subscription.expired.alert.message" = "A tua subscrição associada a este ID Apple já não está ativa."; /* FAQ Button */ -"subscription.faq" = "FAQs and Support"; +"subscription.faq" = "Perguntas Frequentes e Apoio Técnico"; /* FAQ Description */ -"subscription.faq.description" = "Get answers to frequently asked questions or contact Privacy Pro support from our help pages."; +"subscription.faq.description" = "Obtém respostas a perguntas frequentes ou contacta o apoio técnico do Privacy Pro a partir das nossas páginas de ajuda."; + +/* Send Feedback Button */ +"subscription.feedback" = "Enviar comentário"; /* Cancel action for the existing subscription dialog */ -"subscription.found.cancel" = "Cancel"; +"subscription.found.cancel" = "Cancelar"; /* Restore action for the existing subscription dialog */ -"subscription.found.restore" = "Restore"; +"subscription.found.restore" = "Repor"; /* Message for the existing subscription dialog */ -"subscription.found.text" = "We found a subscription associated with this Apple ID."; +"subscription.found.text" = "Encontrámos uma subscrição associada a este Apple ID."; /* Title for the existing subscription dialog */ -"subscription.found.title" = "Subscription Found"; +"subscription.found.title" = "Subscrição encontrada"; /* Help and support Section header */ -"subscription.help" = "Help and support"; +"subscription.help" = "Ajuda e Suporte"; /* Manage Plan header */ -"subscription.manage.plan" = "Manage Plan"; +"subscription.manage.plan" = "Faça a gestão do plano"; /* Header for the subscription section */ -"subscription.manage.title" = "Subscription"; +"subscription.manage.title" = "Subscrição"; /* Alert content for not found subscription */ -"subscription.notFound.alert.message" = "There is no subscription associated with this Apple ID."; +"subscription.notFound.alert.message" = "Não tem nenhuma subscrição associada a este Apple ID."; /* Alert title for not found subscription */ -"subscription.notFound.alert.title" = "Subscription Not Found"; +"subscription.notFound.alert.title" = "Subscrição não encontrada"; /* View plans button text */ -"subscription.notFound.view.plans" = "View Plans"; +"subscription.notFound.view.plans" = "Ver Planos"; /* Hero Text for Personal information removal */ -"subscription.pir.hero" = "Activate Privacy Pro on desktop to set up Personal Information Removal"; +"subscription.pir.hero" = "Ativa o Privacy Pro no computador para configurar a Personal Information Removal"; /* Description on how to use Personal information removal in desktop. The first placeholder references a location in the Desktop application. Privacy Pro>, and the second, the menu entry. i.e. */ -"subscription.pir.heroText" = "In the DuckDuckGo browser for desktop, go to %1$@ and click %2$@ to get started."; +"subscription.pir.heroText" = "No navegador DuckDuckGo para computador, acede a %1$@ e clica em %2$@ para começar."; /* Settings references a menu in the Desktop app, Privacy Pro, references our product name */ -"subscription.pir.heroTextLocation" = "Settings > Privacy Pro"; +"subscription.pir.heroTextLocation" = "Definições > Privacy Pro"; /* Menu item for enabling Personal Information Removal on Desktop */ -"subscription.pir.heroTextMenyEntry" = "I have a subscription"; +"subscription.pir.heroTextMenyEntry" = "Tenho uma Subscrição"; /* Text for the 'macOS' button */ "subscription.pir.macos" = "Mac"; @@ -2467,28 +2638,28 @@ "subscription.pir.windows" = "Windows"; /* Progress view title when completing the purchase */ -"subscription.progress.view.completing.purchase" = "Completing purchase..."; +"subscription.progress.view.completing.purchase" = "A concluir a compra…"; /* Progress view title when starting the purchase */ -"subscription.progress.view.purchasing.subscription" = "Purchase in progress..."; +"subscription.progress.view.purchasing.subscription" = "Compra em curso…"; /* Progress view title when restoring past subscription purchase */ -"subscription.progress.view.restoring.subscription" = "Restoring subscription..."; +"subscription.progress.view.restoring.subscription" = "A restaurar a subscrição…"; /* Remove from this device button */ -"subscription.remove.from.device.button" = "Remove From This Device"; +"subscription.remove.from.device.button" = "Remover Deste Dispositivo"; /* Remove from device confirmation dialog text */ -"subscription.remove.from.device.text" = "You will no longer be able to access your Privacy Pro subscription on this device. This will not cancel your subscription, and it will remain active on your other devices."; +"subscription.remove.from.device.text" = "Perderás acesso à subscrição Privacy Pro neste dispositivo. Isto não irá cancelar a tua subscrição, e ela permanecerá ativa nos teus outros dispositivos."; /* Remove from device confirmation dialog title */ -"subscription.remove.from.device.title" = "Remove from this device?"; +"subscription.remove.from.device.title" = "Remover deste dispositivo?"; /* Remove subscription button text */ -"subscription.remove.subscription" = "Remove Subscription"; +"subscription.remove.subscription" = "Remover Subscrição"; /* Remove subscription cancel button text */ -"subscription.remove.subscription.cancel" = "Cancel"; +"subscription.remove.subscription.cancel" = "Cancelar"; /* Button text for general error message */ "subscription.restore.backend.error.button" = "Voltar às Definições"; @@ -2500,28 +2671,43 @@ "subscription.restore.backend.error.title" = "Ocorreu um erro"; /* Alert for general error message */ -"subscription.restore.general.error.message" = "The App Store was unable to process your purchase. Please try again later."; +"subscription.restore.general.error.message" = "A App Store não conseguiu processar a tua compra. Tenta novamente mais tarde."; /* Alert for general error title */ -"subscription.restore.general.error.title" = "Something Went Wrong"; +"subscription.restore.general.error.title" = "Ocorreu um erro"; /* Alert button text for restored purchase alert */ "subscription.restore.success.alert.button" = "OK"; /* Alert message for restored purchase */ -"subscription.restore.success.alert.message" = "Your purchases have been restored."; +"subscription.restore.success.alert.message" = "As tuas compras foram restauradas."; /* Alert title for restored purchase */ -"subscription.restore.success.alert.title" = "You’re all set."; +"subscription.restore.success.alert.title" = "Está tudo pronto."; + +/* Subtitle in header when subscribed */ +"subscription.subscribed" = "Subscrito"; /* Subscription Expired Data. This reads as 'Your subscription expired on (date)' */ "subscription.subscription.expired.caption" = "A tua subscrição expirou a %@"; -/* Subscription expiration info. This reads as 'Your (monthly or annual) subscription expires on (date)' */ -"subscription.subscription.expiring.caption" = "Your %1$@ subscription expires on %2$@."; +/* Monthly subscription expiration info where parameter is expiration date. This reads as 'Your monthly subscription expires on (date)' */ +"subscription.subscription.expiring.monthly.caption" = "A tua subscrição mensal expira a %@."; + +/* Unknown period subscription expiration info where parameter is expiration date. This reads as 'Your subscription expires on (date)' */ +"subscription.subscription.expiring.unknown.caption" = "A tua subscrição expira a %@."; + +/* Annual subscription expiration info where parameter is expiration date. This reads as 'Your annual subscription expires on (date)' */ +"subscription.subscription.expiring.yearly.caption" = "A tua subscrição anual expira a %@."; + +/* Monthly subscription renewal info where parameter is renewal date. This reads as 'Your monthly subscription renews on (date)' */ +"subscription.subscription.renewing.monthly.caption" = "A tua subscrição mensal renova-se a %@."; + +/* Unknown period subscription renewal info where parameter is renewal date. This reads as 'Your subscription renews on (date)' */ +"subscription.subscription.renewing.unknown.caption" = "A tua subscrição renova-se a %@."; -/* Subscription renewal info. This reads as 'Your (monthly or annual) subscription renews on (date)' */ -"subscription.subscription.renewing.caption" = "Your %1$@ subscription renews on %2$@."; +/* Annual subscription renewal info where parameter is renewal date. This reads as 'Your annual subscription renews on (date)' */ +"subscription.subscription.renewing.yearly.caption" = "A tua subscrição anual renova-se a %@."; /* Navigation bar Title for subscriptions */ "subscription.title" = "Privacy Pro"; @@ -2599,7 +2785,7 @@ "tab.switcher.accessibility.label" = "Alternar de separador"; /* Description text for the text size adjustment setting */ -"textSize.description" = "Escolha o tamanho de texto da sua preferência. Os sites que vê no DuckDuckGo ajustar-se-ão."; +"textSize.description" = "Aumenta ou diminui o tamanho do texto em todos os sites."; /* Replacement string is a current percent value e.g. '120%' */ "textSize.footer" = "Tamanho do texto - %@"; @@ -2623,7 +2809,7 @@ "theme.name.light" = "Claro"; /* No comment provided by engineer. */ -"This list contains messages that have been shown plus at most 1 message that is scheduled for showing. There may be more messages in the config that will be presented, but they haven't been processed yet." = "This list contains messages that have been shown plus at most 1 message that is scheduled for showing. There may be more messages in the config that will be presented, but they haven't been processed yet."; +"This list contains messages that have been shown plus at most 1 message that is scheduled for showing. There may be more messages in the config that will be presented, but they haven't been processed yet." = "Esta lista contém mensagens que foram mostradas mais, no máximo, 1 mensagem que está programada para apresentação. Podem existir mais mensagens na configuração que serão apresentadas, mas ainda não foram processadas."; /* Confirmation of an action - populated with a domain name */ "toast.protection.disabled" = "Proteção de privacidade desativada para %@"; @@ -2668,118 +2854,118 @@ "voiceSearch.footer.note.old" = "O áudio é processado no dispositivo. Não foi armazenado ou partilhado com ninguém, incluindo o DuckDuckGo."; /* Cancel action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.cancel" = "Dismiss"; +"vpn.access-revoked.alert.action.cancel" = "Ignorar"; /* Primary action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.subscribe" = "Subscribe"; +"vpn.access-revoked.alert.action.subscribe" = "Subscrever"; /* Alert message for the alert when the Privacy Pro subscription expiress */ -"vpn.access-revoked.alert.message" = "Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"vpn.access-revoked.alert.message" = "Subscreve o Privacy Pro para voltar a ligar a DuckDuckGo VPN."; /* Alert title for the alert when the Privacy Pro subscription expires */ -"vpn.access-revoked.alert.title" = "VPN disconnected due to expired subscription"; +"vpn.access-revoked.alert.title" = "VPN desativada devido a subscrição expirada"; /* Title for the VPN widget onboarding screen */ "vpn.addWidget.settings.title" = "Encontra e seleciona DuckDuckGo. Em seguida, desliza para VPN e seleciona Adicionar widget."; /* Title for the Cancel button of the VPN feedback form */ -"vpn.feedback-form.button.cancel" = "Cancel"; +"vpn.feedback-form.button.cancel" = "Cancelar"; /* Title for the Done button of the VPN feedback form */ -"vpn.feedback-form.button.done" = "Done"; +"vpn.feedback-form.button.done" = "Feito"; /* Title for the Submit button of the VPN feedback form */ -"vpn.feedback-form.button.submit" = "Submit"; +"vpn.feedback-form.button.submit" = "Submeter"; /* Title for the Submitting state of the VPN feedback form */ -"vpn.feedback-form.button.submitting" = "Submitting…"; +"vpn.feedback-form.button.submitting" = "A enviar…"; /* Title for the browser crash/freeze category of the VPN feedback form */ -"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN causes browser to crash or freeze"; +"vpn.feedback-form.category.browser-crash-or-freeze" = "A VPN faz com que o navegador falhe ou bloqueie"; /* Title for the 'VPN fails to connect' category of the VPN feedback form */ -"vpn.feedback-form.category.fails-to-connect" = "VPN fails to connect"; +"vpn.feedback-form.category.fails-to-connect" = "A VPN não consegue ligar-se"; /* Title for the 'VPN feature request' category of the VPN feedback form */ -"vpn.feedback-form.category.feature-request" = "VPN feature request"; +"vpn.feedback-form.category.feature-request" = "Pedido de funcionalidade da VPN"; /* Title for the category 'VPN causes issues with other apps or websites' category of the VPN feedback form */ -"vpn.feedback-form.category.issues-with-apps" = "VPN causes issues with other apps or websites"; +"vpn.feedback-form.category.issues-with-apps" = "A VPN causa problemas com outras aplicações ou sites"; /* Title for the local device connectivity category of the VPN feedback form */ -"vpn.feedback-form.category.local-device-connectivity" = "VPN won't let me connect to local device"; +"vpn.feedback-form.category.local-device-connectivity" = "A VPN não me deixa ligar ao dispositivo local"; /* Title for the 'other VPN feedback' category of the VPN feedback form */ -"vpn.feedback-form.category.other" = "Other VPN feedback"; +"vpn.feedback-form.category.other" = "Outro feedback sobre VPN"; /* Title for the category selection state of the VPN feedback form */ -"vpn.feedback-form.category.select-category" = "Select a category"; +"vpn.feedback-form.category.select-category" = "SELECIONA UMA CATEGORIA"; /* Title for the 'VPN is too slow' category of the VPN feedback form */ -"vpn.feedback-form.category.too-slow" = "VPN connection is too slow"; +"vpn.feedback-form.category.too-slow" = "A ligação da VPN é demasiado lenta"; /* Title for the 'unable to install' category of the VPN feedback form */ -"vpn.feedback-form.category.unable-to-install" = "Unable to install VPN"; +"vpn.feedback-form.category.unable-to-install" = "Não é possível instalar a VPN"; /* Title for the feedback sent view description of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.description" = "Your feedback will help us improve the\nDuckDuckGo VPN."; +"vpn.feedback-form.sending-confirmation.description" = "O teu feedback ajuda-nos a melhorar a VPN DuckDuckGo."; /* Title for the feedback sending error text of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.error" = "We couldn't send your feedback right now, please try again."; +"vpn.feedback-form.sending-confirmation.error" = "Não foi possível enviar o teu feedback, tenta novamente."; /* Title for the feedback sent view title of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.title" = "Thank you!"; +"vpn.feedback-form.sending-confirmation.title" = "Obrigado!"; /* Toast message when the VPN feedback form is submitted successfully */ -"vpn.feedback-form.submitted.message" = "Thank You! Feedback submitted."; +"vpn.feedback-form.submitted.message" = "Obrigado! Comentário enviado"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-1" = "Please describe what's happening, what you expected to happen, and the steps that led to the issue:"; +"vpn.feedback-form.text-1" = "Descreve o que está a acontecer, o que esperavas que acontecesse e os passos que levaram ao problema:"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-2" = "In addition to the details entered into this form, your app issue report will contain:"; +"vpn.feedback-form.text-2" = "Para além dos detalhes introduzidos neste formulário, o teu relatório de problemas na aplicação vai incluir:"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-3" = "• Whether specific DuckDuckGo features are enabled"; +"vpn.feedback-form.text-3" = "• Se tens funcionalidades específicas do DuckDuckGo ativas"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-4" = "• Aggregate DuckDuckGo app diagnostics"; +"vpn.feedback-form.text-4" = "• Diagnósticos da aplicação DuckDuckGo agregados"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-5" = "By tapping \"Submit\" I agree that DuckDuckGo may use the information in this report for purposes of improving the app's features."; +"vpn.feedback-form.text-5" = "Ao tocar em \"Enviar\", concordo que o DuckDuckGo pode utilizar as informações neste relatório para melhorar as funcionalidades da aplicação."; /* Title for each screen of the VPN feedback form */ -"vpn.feedback-form.title" = "Help Improve the DuckDuckGo VPN"; +"vpn.feedback-form.title" = "Ajuda a melhorar a VPN DuckDuckGo"; /* VPN settings screen cell text for adding the VPN widget to the home screen */ "vpn.settings.add.widget" = "Adicionar widget de VPN ao ecrã inicial"; /* Disclaimer for the DNS Server section on the DNS Server screen */ -"vpn.settings.dns.section-disclaimer" = "Using a custom DNS server can impact browsing speeds and expose your activity to third parties if the server isn't secure or reliable."; +"vpn.settings.dns.section-disclaimer" = "A utilização de um servidor DNS personalizado pode afetar as velocidades de navegação e expor a tua atividade a terceiros se o servidor não for seguro ou fiável."; /* Header text for the DNS section on the VPN Settings screen */ "vpn.settings.dns.section-header" = "DNS"; /* Title for the Apply button on the DNS Server setting screen */ -"vpn.settings.dns.server.apply.button.title" = "Apply"; +"vpn.settings.dns.server.apply.button.title" = "Aplicar"; /* Default value for the DNS Server row on the VPN Settings screen */ "vpn.settings.dns.server.default.value" = "DuckDuckGo"; /* Title for the IPv4 Address setting */ -"vpn.settings.dns.server.ipv4.title" = "IPv4 Address"; +"vpn.settings.dns.server.ipv4.title" = "Endereço IPv4"; /* Custom option for the DNS Server setting */ -"vpn.settings.dns.server.option.custom" = "Custom"; +"vpn.settings.dns.server.option.custom" = "Personalizado"; /* Recommended option for the DNS Server setting */ -"vpn.settings.dns.server.option.default" = "DuckDuckGo (Recommended)"; +"vpn.settings.dns.server.option.default" = "DuckDuckGo (Recomendado)"; /* Title for the DNS Server setting screen */ -"vpn.settings.dns.server.screen.title" = "DNS Server"; +"vpn.settings.dns.server.screen.title" = "Servidor DNS"; /* Title for the DNS Server row on the VPN Settings screen */ -"vpn.settings.dns.server.title" = "DNS Server"; +"vpn.settings.dns.server.title" = "Servidor DNS"; /* Title for the button to enable push notifications in system settings */ "waitlist.allow-notifications" = "Permitir notificações"; diff --git a/DuckDuckGo/pt.lproj/OmniBar.strings b/DuckDuckGo/pt.lproj/OmniBar.strings index 1c617dac9d..c75910fc4d 100644 --- a/DuckDuckGo/pt.lproj/OmniBar.strings +++ b/DuckDuckGo/pt.lproj/OmniBar.strings @@ -7,6 +7,9 @@ /* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "Dt8-We-5tV"; */ "Dt8-We-5tV.accessibilityLabel" = "Limpar o texto"; +/* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "gb5-wL-4OW"; */ +"gb5-wL-4OW.accessibilityLabel" = "Limpar o texto"; + /* Class = "UIButton"; accessibilityLabel = "Share"; ObjectID = "hor-Jd-3kw"; */ "hor-Jd-3kw.accessibilityLabel" = "Partilhar"; diff --git a/DuckDuckGo/pt.lproj/Settings.strings b/DuckDuckGo/pt.lproj/Settings.strings index 1c4761d9bb..3961b1d103 100644 --- a/DuckDuckGo/pt.lproj/Settings.strings +++ b/DuckDuckGo/pt.lproj/Settings.strings @@ -10,9 +10,6 @@ /* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "5YD-Od-uDU"; */ "5YD-Od-uDU.text" = "Limpar os dados automaticamente"; -/* Class = "UIButton"; normalTitle = "Settings"; ObjectID = "8YG-7Q-Czd"; */ -"8YG-7Q-Czd.normalTitle" = "Definições"; - /* Class = "UILabel"; text = "App Exit, Inactive for 1 Hour"; ObjectID = "9EC-lx-77p"; */ "9EC-lx-77p.text" = "Saída da aplicação, inativa por 1 hora"; @@ -76,9 +73,6 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Sites desprotegidos"; -/* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ -"ssa-zd-L3T.title" = "Tamanho do texto"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Mostrar teclado em"; diff --git a/DuckDuckGo/ro.lproj/DaxOnboarding.strings b/DuckDuckGo/ro.lproj/DaxOnboarding.strings index 9f627dab5e..88580d637a 100644 --- a/DuckDuckGo/ro.lproj/DaxOnboarding.strings +++ b/DuckDuckGo/ro.lproj/DaxOnboarding.strings @@ -1,12 +1,6 @@ /* Class = "UIButton"; normalTitle = "Hide"; ObjectID = "hgy-75-OvJ"; */ "hgy-75-OvJ.normalTitle" = "Ascunde"; -/* Class = "UILabel"; text = "Welcome to\nDuckDuckGo!"; ObjectID = "Lvy-9G-3gr"; */ -"Lvy-9G-3gr.text" = "Bine ai venit la\nDuckDuckGo!"; - -/* Class = "UIButton"; normalTitle = "Let’s Do It!"; ObjectID = "oAv-Zt-uIt"; */ -"oAv-Zt-uIt.normalTitle" = "Să începem!"; - /* Class = "UIButton"; normalTitle = "Button"; ObjectID = "zJt-F9-L48"; */ "zJt-F9-L48.normalTitle" = "Buton"; diff --git a/DuckDuckGo/ro.lproj/Localizable.strings b/DuckDuckGo/ro.lproj/Localizable.strings index 1444305508..9bfa277d90 100644 --- a/DuckDuckGo/ro.lproj/Localizable.strings +++ b/DuckDuckGo/ro.lproj/Localizable.strings @@ -341,7 +341,7 @@ "autofill.keep-enabled.alert.disable" = "Dezactivează"; /* A link that takes the user to the DuckDuckGo help pages explaining password managers */ -"autofill.learn.more.link.title" = "Learn More"; +"autofill.learn.more.link.title" = "Află mai multe"; /* Button displayed after saving/updating an autofill login that takes the user to the saved login */ "autofill.login-save-action-button.toast" = "Vezi"; @@ -977,9 +977,6 @@ /* ad = advertisment */ "dax.onboarding.home.subsequent" = "Ai ghicit!\n\nReține: de fiecare dată când navighezi cu mine, o reclamă terifiantă își pierde aripile. 👍"; -/* No comment provided by engineer. */ -"dax.onboarding.message" = "Internetul poate fi cam terifiant.\n\nNu te îngrijora! Căutarea și navigarea în mod confidențial sunt mai ușoare decât crezi."; - /* No comment provided by engineer. */ "Debug" = "Depanează"; @@ -1080,7 +1077,7 @@ "duckPlayer.never.label" = "Niciodată"; /* Body text for the modal feature explanation */ -"duckplayer.presentation.modal.body" = "Duck Player îți permite să vizionezi videoclipuri pe YouTube fără reclame țintite, într-o experiență asemănătoare unui cinematograf, în DuckDuckGo, iar ceea ce vizionezi nu îți va influența recomandările."; +"duckplayer.presentation.modal.body" = "Duck Player îți permite să vizionezi YouTube fără reclame țintite în DuckDuckGo, iar ceea ce vizionezi nu îți va influența recomandările."; /* Button that will dismiss the modal */ "duckplayer.presentation.modal.dismiss-button" = "Am înțeles!"; @@ -1088,17 +1085,23 @@ /* Two line title (separated by \n) for the feature explanation */ "duckplayer.presentation.modal.title" = "Te copleșesc reclamele pe YouTube? Încearcă Duck Player!"; +/* Two line title (separated by \n) for the feature explanation */ +"duckplayer.presentation.modal.title.youtube" = "Te copleșesc reclamele pe YouTube? Nu și cu Duck Player!"; + /* Footer label in the settings screen for Duck Player */ "duckplayer.settings.footer" = "DuckDuckGo oferă toate elementele de confidențialitate esențiale de care ai nevoie pentru a te proteja în timp ce navighezi pe web."; /* Text explaining what Duck Player is in the settings screen. */ -"duckplayer.settings.info-text" = "Duck Player oferă o experiență de vizionare fără perturbări, fără reclame personalizate și împiedică activitatea de vizionare să îți influențeze recomandările YouTube."; +"duckplayer.settings.info-text" = "Duck Player îți permite să vizionezi YouTube fără reclame țintite în DuckDuckGo, iar ceea ce vizionezi nu îți va influența recomandările."; /* Button that takes the user to learn more about Duck Player. */ "duckplayer.settings.learn-more" = "Află mai multe"; +/* Settings screen cell text for DuckPlayer settings to open in new tab */ +"duckplayer.settings.open-new-tab-label" = "Deschide Duck Player într-o filă nouă"; + /* Settings screen cell text for DuckPlayer settings */ -"duckplayer.settings.open-videos-in" = "Deschide videoclipuri în Duck Player"; +"duckplayer.settings.open-videos-in" = "Deschide videoclipurile YouTube în Duck Player"; /* Settings screen cell text for DuckPlayer settings */ "duckplayer.settings.title" = "Duck Player"; @@ -1169,9 +1172,6 @@ /* Title for prompt to sign up for email protection */ "email.signup-prompt.title" = "Ascunde-ți e-mailul și blochează tehnologiile de urmărire"; -/* No comment provided by engineer. */ -"empty!" = "empty!"; - /* Empty list state placholder */ "empty.bookmarks" = "Nu s-a adăugat niciun semn de carte"; @@ -1322,6 +1322,39 @@ /* Confirmation button */ "feedback.form.submit" = "Trimite"; +/* Title of the feedback form */ +"feedback.form.title" = "Contribuie la îmbunătățirea Privacy Pro"; + +/* Category for Identity Theft Restoration feedback */ +"feedback.general.category.itr" = "Identity Theft Restoration"; + +/* Category for Personal Info Removal feedback */ +"feedback.general.category.pir" = "Eliminarea informațiilor personale"; + +/* Category for subscription and payments feedback */ +"feedback.general.category.ppro" = "Abonamente și plăți"; + +/* Prompt to select a category for general feedback */ +"feedback.general.category.select" = "SELECTEAZĂ O CATEGORIE"; + +/* Category for VPN feedback */ +"feedback.general.category.vpn" = "VPN"; + +/* Category for access code issues */ +"feedback.itr.category.access-code" = "Problemă cu codul de acces"; + +/* Category for issues contacting an advisor */ +"feedback.itr.category.cant-contact-advisor" = "Nu pot să iau legătura cu consilierul"; + +/* Prompt to select a category for Identity Theft Restoration feedback */ +"feedback.itr.category.select" = "SELECTEAZĂ O CATEGORIE"; + +/* Category for other Identity Theft Restoration issues */ +"feedback.itr.category.something-else" = "Altceva"; + +/* Category for unhelpful advisor calls */ +"feedback.itr.category.unhelpful" = "Apelul către consilier a fost inutil"; + /* No comment provided by engineer. */ "feedback.negative.form.genericPlaceholder" = "Vă rugăm să furnizați detalii cât mai concrete"; @@ -1361,6 +1394,24 @@ /* No comment provided by engineer. */ "feedback.performance.slowLoading" = "Paginile web sau rezultatele căutării se încarcă încet"; +/* Category for when scan finds incorrect records */ +"feedback.pir.category.not-me" = "Scanarea a găsit înregistrări care nu mă privesc"; + +/* Category for when scan doesn't find info on a specific site */ +"feedback.pir.category.nothing-on-site" = "Scanarea nu a găsit informațiile mele pe un site specific"; + +/* Category for other Personal Info Removal issues */ +"feedback.pir.category.other" = "Altceva"; + +/* Category for when the removal process is stuck */ +"feedback.pir.category.removal-stuck" = "Procesul de eliminare este blocat"; + +/* Category for when the scan is stuck */ +"feedback.pir.category.scan-stuck" = "Scanarea pentru înregistrări este blocată"; + +/* Prompt to select a category for Personal Info Removal feedback */ +"feedback.pir.category.select" = "SELECTEAZĂ O CATEGORIE"; + /* Header above input field */ "feedback.positive.form.header" = "Partajare detalii"; @@ -1379,6 +1430,15 @@ /* Button encouraging uses to share details aboout their feedback */ "feedback.positive.submit" = "Partajare detalii"; +/* Category for other Privacy Pro issues */ +"feedback.ppro.category.other" = "Altceva"; + +/* Category for one-time password issues */ +"feedback.ppro.category.otp" = "Problemă cu parola de unică folosință"; + +/* Prompt to select a category for Privacy Pro feedback */ +"feedback.ppro.category.select" = "SELECTEAZĂ O CATEGORIE"; + /* No comment provided by engineer. */ "feedback.start.footer" = "Feedbackul dvs. anonim este important pentru noi."; @@ -1529,12 +1589,6 @@ /* No comment provided by engineer. */ "keyCommandShowAllTabs" = "Afișează toate filele"; -/* Please preserve newline character */ -"launchscreenWelcomeMessage" = "Bine ai venit la\nDuckDuckGo!"; - -/* No comment provided by engineer. */ -"LOREM IPSUM" = "LOREM IPSUM"; - /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "DuckDuckGo pentru Mac are viteza de care ai nevoie, funcționalitățile de răsfoire pe care ți le dorești și este prevăzută cu cele mai bune caracteristici de bază în privința confidențialității din domeniu."; @@ -1586,62 +1640,83 @@ /* Edit button */ "navigation.title.edit" = "Editați"; -/* String indicating NetP is connected when viewed from the settings screen */ -"netP.cell.connected" = "Connected"; - -/* String indicating NetP is disconnected when viewed from the settings screen */ -"netP.cell.disconnected" = "Not connected"; - -/* Title for the DuckDuckGo VPN feature in settings */ -"netP.settings.title" = "VPN"; +/* String indicating NetP is snoozing when viewed from the settings screen */ +"netP.cell.snoozing" = "Amânat"; /* Title for the DuckDuckGo VPN feature */ "netP.title" = "DuckDuckGo VPN"; +/* Button title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.action" = "Adăugare widget"; + +/* Message for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.message" = "Activează și dezactivează VPN-ul chiar din ecranul de pornire."; + +/* Title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.title" = "Adaugă widgetul VPN"; + /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN deconectat din cauza expirării abonamentului. Abonează-te la Privacy Pro pentru a reconecta DuckDuckGo VPN."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Network Protection nu a reușit să se conecteze. Încearcă din nou mai târziu."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "DuckDuckGo VPN nu a reușit să se conecteze. Încearcă din nou mai târziu."; /* Title for the VPN FAQ screen. */ "network.protection.faq.title" = "Întrebări frecvente despre DuckDuckGo VPN"; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Network Protection a fost întreruptă. Se încearcă reconectarea acum..."; +/* Message for tooltip about geoswitching */ +"network.protection.geoswitching.tip.message" = "Poți personaliza locația VPN-ului tău, conectându-te la oricare dintre serverele noastre din întreaga lume."; + +/* Title for tooltip about geoswitching */ +"network.protection.geoswitching.tip.title" = "Schimbă-ți locația"; + +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "DuckDuckGo VPN a fost întrerupt. Se încearcă reconectarea acum..."; /* Message for the network protection invite dialog */ -"network.protection.invite.dialog.message" = "Enter your invite code to get started."; +"network.protection.invite.dialog.message" = "Completează codul de invitație pentru a începe."; /* Title for the network protection invite screen */ "network.protection.invite.dialog.title" = "Te invităm să încerci DuckDuckGo VPN"; /* Prompt for the network protection invite code text field */ -"network.protection.invite.field.prompt" = "Invite Code"; +"network.protection.invite.field.prompt" = "Codul de invitație"; /* Message for the network protection invite success view */ -"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; +"network.protection.invite.success.message" = "Ascunde-ți locația de pe site-uri și disimulează-ți activitatea online față de furnizorii de internet și de alte entități din rețeaua ta."; /* Title for the network protection invite success view */ -"network.protection.invite.success.title" = "Success! You’re in."; +"network.protection.invite.success.title" = "Ai reușit! Acum o utilizezi."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; /* Title text for an iOS quick action that opens VPN settings */ "network.protection.quick-action.open-vpn" = "Deschide VPN"; +/* Button title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.action" = "Află mai multe"; + +/* Message for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.message" = "Poți utiliza site-uri sau aplicații care blochează traficul VPN, amânând conectarea la VPN."; + +/* Title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.title" = "Evită conflictele VPN"; + +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN-ul este amânat pentru %@"; + /* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ "network.protection.status.connected.format" = "Conectat · %@"; /* The label for the NetP VPN when connecting */ -"network.protection.status.connecting" = "Connecting..."; +"network.protection.status.connecting" = "Se conectează..."; /* The label for the NetP VPN when disconnected */ -"network.protection.status.disconnected" = "Not connected"; +"network.protection.status.disconnected" = "Nu este conectat"; /* The label for the NetP VPN when disconnecting */ -"network.protection.status.disconnecting" = "Disconnecting..."; +"network.protection.status.disconnecting" = "Se deconectează..."; /* Message label text for the status view when VPN is disconnected */ "network.protection.status.header.message.off" = "Conectează-te pentru a securiza traficul pe internet\n pentru toate dispozitivele tale."; @@ -1655,26 +1730,41 @@ /* Header title label text for the status view when VPN is connected */ "network.protection.status.header.title.on" = "DuckDuckGo VPN este pornit"; +/* Header title label text for the status view when VPN is snoozing */ +"network.protection.status.header.title.snoozed" = "DuckDuckGo VPN este amânat"; + /* The status view 'Share Feedback' button which is shown inline on the status view after the temporary free use footer text */ -"network.protection.status.menu.share.feedback" = "Share Feedback"; +"network.protection.status.menu.share.feedback" = "Partajează feedback"; + +/* The label for the NetP VPN when paused */ +"network.protection.status.paused" = "Întrerupt"; + +/* The label for when NetP VPN is snoozing plus the length of time remaining formatted as '0:00' */ +"network.protection.status.snoozing.format" = "Amânare, %@ rămase"; + +/* Snooze button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.snooze" = "Amână pentru 20 de minute"; + +/* Wake Up button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.wake-up" = "Trezește-te"; /* Connection details label shown in NetworkProtection's status view. */ -"network.protection.status.view.connection.details" = "Connection Details"; +"network.protection.status.view.connection.details" = "Detalii conexiune"; /* Custom DNS label shown in NetworkProtection's status view. */ -"network.protection.status.view.custom.dns" = "DNS Server"; +"network.protection.status.view.custom.dns" = "Server DNS"; /* Generic connection failed error message shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.message" = "Please try again later."; +"network.protection.status.view.error.connection.failed.message" = "Încearcă din nou mai târziu."; /* Generic connection failed error title shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; +"network.protection.status.view.error.connection.failed.title" = "Conectarea nu a reușit."; /* IP Address label shown in NetworkProtection's status view. */ -"network.protection.status.view.ip.address" = "IP Address"; +"network.protection.status.view.ip.address" = "Adresa IP"; /* Location label shown in NetworkProtection's status view. */ -"network.protection.status.view.location" = "Location"; +"network.protection.status.view.location" = "Locație"; /* Label shown on the title of the settings section in NetworkProtection's status view. */ "network.protection.status.view.settings.section.title" = "Gestionează"; @@ -1682,12 +1772,15 @@ /* Title label text for the status view when netP is disconnected */ "network.protection.status.view.title" = "VPN"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Network Protection este activată. Locația și activitatea ta online sunt protejate."; +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "DuckDuckGo VPN este activat. Locația și activitatea ta online sunt protejate."; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Dirijarea traficului dispozitivului prin %@."; +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "Amânarea VPN a luat sfârșit. Se dirijează traficul dispozitivului prin %@."; + /* Title for the button to link to the iOS app settings and enable notifications app-wide. */ "network.protection.turn.on.notifications.button.title" = "Activează Notificările"; @@ -1698,13 +1791,13 @@ "network.protection.vpn.about" = "Despre"; /* Section header for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.section.header" = "Notifications"; +"network.protection.vpn.alerts.section.header" = "Notificări"; /* List section footer for the toggle for VPN alerts. */ "network.protection.vpn.alerts.toggle.section.footer" = "Primește o notificare dacă conexiunea ta devine mai slabă sau dacă starea VPN-ului se schimbă."; /* Title for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.toggle.title" = "VPN Notifications"; +"network.protection.vpn.alerts.toggle.title" = "Notificări VPN"; /* Title for the data volume section in the VPN status screen */ "network.protection.vpn.data-volume" = "Volum de date"; @@ -1713,13 +1806,13 @@ "network.protection.vpn.exclude.local.networks.setting.footer" = "Lasă traficul local să ocolească VPN-ul și să se conecteze la dispozitivele din rețeaua locală, cum ar fi o imprimantă."; /* Header text for the Exclude Local Networks setting item. */ -"network.protection.vpn.exclude.local.networks.setting.header" = "General"; +"network.protection.vpn.exclude.local.networks.setting.header" = "Generale"; /* Title for the Exclude Local Networks setting item. */ "network.protection.vpn.exclude.local.networks.setting.title" = "Exclude rețelele locale"; /* Title for the VPN Location screen's All Countries section. */ -"network.protection.vpn.location.all.countries.section.title" = "All Countries"; +"network.protection.vpn.location.all.countries.section.title" = "Toate țările"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.connected" = "Locație conectată"; @@ -1734,7 +1827,7 @@ "network.protection.vpn.location.recommended.section.footer" = "Conectează-te automat la cel mai apropiat server pe care îl putem găsi."; /* Title for the VPN Location screen's Recommended section. */ -"network.protection.vpn.location.recommended.section.title" = "Recommended"; +"network.protection.vpn.location.recommended.section.title" = "Recomandat"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.selected" = "Locație selectată"; @@ -1752,13 +1845,13 @@ "network.protection.vpn.preferred.location.nearest" = "Cea mai apropiată locație"; /* Title for the Preferred Location VPN Settings item. */ -"network.protection.vpn.preferred.location.title" = "Preferred Location"; +"network.protection.vpn.preferred.location.title" = "Locație preferată"; /* Footer text for the DNS server setting item. */ -"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo routes DNS queries through our DNS servers so your internet provider can't see what websites you visit."; +"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo direcționează interogările DNS prin serverele noastre DNS, astfel încât furnizorul tău de internet să nu poată vedea ce site-uri web vizitezi."; /* Title for the FAQ row in the VPN status screen. */ -"network.protection.vpn.settings.faq" = "FAQs and Support"; +"network.protection.vpn.settings.faq" = "Întrebări frecvente și asistență"; /* Title for the feedback row in the VPN status screen. */ "network.protection.vpn.settings.share-feedback" = "Oferă feedback despre VPN"; @@ -1940,6 +2033,66 @@ /* Deactivate button */ "pm.deactivate" = "Dezactivează"; +/* Label for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.label" = "E-mail (opțional)"; + +/* Placeholder for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.placeholder" = "name@email.com"; + +/* Placeholder for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.placeholder" = "Te rugăm să ne comunici feedbackul tău..."; + +/* Title for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.title" = "Feedback general"; + +/* Title for the ITR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-itr-problem.title" = "Identity Theft Restoration"; + +/* Title for the PIR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-pir-problem.title" = "Personal Information Removal"; + +/* Title for the Subscriptions and Payments category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-ppro-problem.title" = "Abonamente și plăți"; + +/* Placeholder for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.placeholder" = "Spune-ne ce se întâmplă..."; + +/* Title for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.title" = "Raportează o problemă"; + +/* Title for the VPN category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-vpn-problem.title" = "VPN"; + +/* Placeholder for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.placeholder" = "Ce caracteristică ai dori să vezi?"; + +/* Title for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.title" = "Solicitare de funcționalitate"; + +/* Title for the category selection section in the Privacy Pro feedback form */ +"ppro.feedback-form.select-category.title" = "SELECTEAZĂ O CATEGORIE"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-1" = "Ai găsit o problemă care nu este acoperită în [centrul nostru de ajutor](duck://)? Ne dorim foarte mult să știm acest lucru.Furnizează o adresă de e-mail dacă dorești să te contactăm cu privire la această problemă (este posibil să nu putem răspunde la toate problemele):"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-2" = "Pe lângă detaliile menționate mai sus, trimitem câteva informații anonimizate împreună cu feedbackul tău:"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-3" = "• dacă anumite funcții ale browserului sunt active;"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-4" = "• diagnosticare agregată a aplicației (de exemplu, coduri de eroare)."; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-5" = "Atingând „Transmite”, ești de acord ca DuckDuckGo să utilizeze informațiile trimise pentru a îmbunătăți aplicația."; + +/* Title for the text box in the Privacy Pro feedback form */ +"ppro.feedback-form.text-box.title" = "Feedback"; + +/* Title for the Privacy Pro feedback form */ +"ppro.feedback-form.title" = "Trimite feedback"; + /* Button title for sync bookmarks limits exceeded warning to go to manage bookmarks */ "prefrences.sync.bookmarks-limit-exceeded-action" = "Gestionează marcajele"; @@ -2029,6 +2182,18 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Preferințe"; +/* Name of the option the user can chose to give general browser feedback */ +"send.browser.feedback.general-feedback" = "Feedback general"; + +/* Name of the option the user can chose to give browser feedback about a problem they enountered */ +"send.browser.feedback.report-problem" = "Raportează o problemă"; + +/* Name of the option the user can chose to give browser feedback about a feature they would like */ +"send.browser.feedback.request-feature" = "Solicită o funcționalitate"; + +/* Title of the picker where the user can chose the category of the feedback they want ot send. */ +"send.browser.feedback.select-category" = "SELECTEAZĂ O CATEGORIE"; + /* Settings cell for About DDG */ "settings.about.ddg" = "Despre DuckDuckGo"; @@ -2084,6 +2249,9 @@ /* Section footer Autolock description */ "settings.autolock.description" = "Dacă identificatorul tactil, identificatorul facial sau parola de sistem sunt activate, ți se va solicita să deblochezi aplicația la deschidere."; +/* Settings cell for Browser Feedback */ +"settings.browser.feedback" = "Feedback browser"; + /* Settings screen cell text for Automatically Clearing Data */ "settings.clear.data" = "Șterge automat datele"; @@ -2182,7 +2350,7 @@ "settings.ppro" = "Privacy Pro"; /* Title for Link in the Footer of Privacy Pro section */ -"settings.ppro.footer" = "Privacy Policy and Terms of Service"; +"settings.ppro.footer" = "Politica de confidențialitate și Condițiile de prestare a serviciului"; /* Settings screen cell for long press previews */ "settings.previews" = "Previzualizări prin apăsare lungă"; @@ -2208,23 +2376,20 @@ /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Setează poziția barei de adrese"; -/* Subscription activation pending description */ -"settings.subscription.activation.pending.description" = "This is taking longer than usual, please check back later."; +/* Privacy pro description subtitle in settings when the is activating */ +"settings.subscription.activating" = "Se activează"; -/* Subscription activation pending title */ -"settings.subscription.activation.pending.title" = "Abonamentul tău este în curs de activare"; +/* Subscription activation pending description */ +"settings.subscription.activation.pending.description" = "Acest lucru durează mai mult decât de obicei, te rugăm să revii mai târziu."; /* Data Broker protection cell subtitle for privacy pro */ -"settings.subscription.DBP.subtitle" = "Remove your info from sites that sell it"; +"settings.subscription.DBP.subtitle" = "Elimină-ți informațiile de pe site-urile care le vând"; /* Data Broker protection cell title for privacy pro */ "settings.subscription.DBP.title" = "Personal Information Removal"; -/* Privacy pro description subtext */ -"settings.subscription.description" = "Confidențialitate optimizată, cu trei noi mecanisme de protecție:"; - /* I have a Subscription button text for privacy pro */ -"settings.subscription.existing.subscription" = "I Have a Subscription"; +"settings.subscription.existing.subscription" = "Am un abonament"; /* Subscription expired description */ "settings.subscription.expired.comment" = "Abonează-te din nou pentru a continua să utilizezi Privacy Pro"; @@ -2232,23 +2397,26 @@ /* Subscription expired tittle message */ "settings.subscription.expired.title" = "Abonamentul tău Privacy Pro a expirat"; -/* Privacy pro features list */ -"settings.subscription.features" = " • VPN\n • Personal Information Removal\n • Identity Theft Restoration"; - /* Identity theft restoration cell subtitle for privacy pro */ -"settings.subscription.ITR.subtitle" = "If your identity is stolen, we'll help restore it"; +"settings.subscription.ITR.subtitle" = "Dacă identitatea ta este furată, te vom ajuta să o restabilești."; /* Identity theft restoration cell title for privacy pro */ "settings.subscription.ITR.title" = "Identity Theft Restoration"; /* Get Privacy Pro button text for privacy pro */ -"settings.subscription.learn.more" = "Get Privacy Pro"; +"settings.subscription.learn.more" = "Obține Privacy Pro"; /* Subscription Settings button text for privacy pro */ -"settings.subscription.manage" = "Subscription Settings"; +"settings.subscription.manage" = "Setări pentru abonament"; + +/* Privacy Pro description subtitle in settings */ +"settings.subscription.row.description" = "Include VPN-ul nostru și Identity Theft Restoration."; -/* Call to action title for Privacy Pro */ -"settings.subscription.subscribe" = "Subscribe to Privacy Pro"; +/* Call to action title for Privacy Pro settings */ +"settings.subscription.subscribe" = "Protejează-ți conexiunea și identitatea cu Privacy Pro"; + +/* Privacy pro description subtitle in settings */ +"settings.subscription.us.description" = "Include VPN, Personal Information Removal și Identity Theft Restoration."; /* VPN cell title for privacy pro */ "settings.subscription.VPN.title" = "VPN"; @@ -2317,67 +2485,64 @@ "siteFeedback.urlPlaceholder" = "Ce site este defect?"; /* Subscription Activation Info */ -"subscription.activate..header.description" = "Access your Privacy Pro subscription on this device via Apple ID or an email address."; +"subscription.activate..header.description" = "Accesează-ți abonamentul Privacy Pro pe acest dispozitiv prin Apple ID sau o adresă de e-mail."; /* Button for adding email address to subscription */ -"subscription.activate.add.email.button" = "Add Email"; +"subscription.activate.add.email.button" = "Adaugă adresa de e-mail"; /* Apple ID option for activation */ "subscription.activate.appleid" = "Apple ID"; /* Button text for restoring purchase via Apple ID */ -"subscription.activate.appleid.button" = "Restore Purchase"; +"subscription.activate.appleid.button" = "Restabilește achiziția"; /* Description for Apple ID activation */ -"subscription.activate.appleid.description" = "Restore your purchase to activate your subscription on this device."; +"subscription.activate.appleid.description" = "Restaurează-ți achiziția pentru a-ți activa abonamentul pe acest dispozitiv."; /* Subscription Activation Info */ -"subscription.activate.description" = "Your subscription is automatically available in DuckDuckGo on any device signed in to your Apple ID."; +"subscription.activate.description" = "Abonamentul tău este disponibil automat în DuckDuckGo pe orice dispozitiv conectat la Apple ID-ul tău."; /* Button for editing email address added to subscription */ -"subscription.activate.edit.email.button" = "Edit Email"; +"subscription.activate.edit.email.button" = "Editează adresa de e-mail"; /* View Title for editing your email account */ -"subscription.activate.edit.email.title" = "Edit Email"; +"subscription.activate.edit.email.title" = "Editează adresa de e-mail"; /* Email option for activation */ -"subscription.activate.email" = "Email"; +"subscription.activate.email" = "E-mail"; /* Restore button title for Email */ -"subscription.activate.email.button" = "Enter Email"; +"subscription.activate.email.button" = "Completează adresa de e-mail"; /* Description for Email activation */ -"subscription.activate.email.description" = "Use your email to activate your subscription on this device."; +"subscription.activate.email.description" = "Folosește-ți e-mailul pentru a-ți activa abonamentul pe acest dispozitiv."; /* Activate subscription title */ "subscription.activate.email.title" = "Activează abonamentul"; /* Button title for cancelling email deletion */ -"subscription.activate.manage.email.cancel" = "Cancel"; +"subscription.activate.manage.email.cancel" = "Renunță"; /* Button title for confirming email deletion */ "subscription.activate.manage.email.OK" = "OK"; /* Restore button title for AppleID */ -"subscription.activate.restore.apple" = "Restore Purchase"; +"subscription.activate.restore.apple" = "Restabilește achiziția"; /* Subscription Activation Title */ -"subscription.activate.title" = "Activate your subscription on this device"; +"subscription.activate.title" = "Activează-ți abonamentul pe acest dispozitiv"; /* Resend activation instructions button */ -"subscription.add.device.resend.instructions" = "Resend Instructions"; +"subscription.add.device.resend.instructions" = "Retrimite instrucțiunile"; /* Add email to an existing subscription */ -"subscription.add.email" = "Add an email address to activate your subscription on your other devices. We’ll only use this address to verify your subscription."; +"subscription.add.email" = "Adaugă o adresă de e-mail pentru a-ți activa abonamentul pe celelalte dispozitive ale tale. Vom folosi această adresă doar pentru a-ți verifica abonamentul."; /* View title for adding email to subscription */ -"subscription.add.email.title" = "Add Email"; - -/* Title for Alert messages */ -"subscription.alert.title" = "subscription.alert.title"; +"subscription.add.email.title" = "Adaugă adresa de e-mail"; /* Subscription availability message on Apple devices */ -"subscription.available.apple" = "Privacy Pro is available on any device signed in to the same Apple ID."; +"subscription.available.apple" = "Privacy Pro este disponibil pe orice dispozitiv conectat la același Apple ID."; /* Text for the manage billing page */ "subscription.billing.google.text" = "Abonamentul tău a fost achiziționat prin Magazinul Google Play. Pentru a-ți reînnoi abonamentul, deschide setările abonamentului din Magazinului Google Play pe un dispozitiv conectat la același cont Google folosit pentru achiziționarea inițială a abonamentului."; @@ -2385,89 +2550,86 @@ /* Title for the manage billing page */ "subscription.billing.google.title" = "Tipuri de abonament"; -/* Subscription annual billing period type */ -"subscription.billing.period.annual" = "annual"; - -/* Subscription monthly billing period type */ -"subscription.billing.period.monthly" = "monthly"; - /* Subscription Removal confirmation message */ -"subscription.cancel.message" = "Your subscription has been removed from this device."; +"subscription.cancel.message" = "Abonamentul tău a fost eliminat de pe acest dispozitiv."; /* Change plan or cancel title */ -"subscription.change.plan" = "Update Plan or Cancel"; +"subscription.change.plan" = "Actualizează planul sau anulează"; /* Navigation Button for closing subscription view */ -"subscription.close" = "Close"; +"subscription.close" = "Închidere"; /* Title for Confirm messages */ -"subscription.confirm.title" = "Are you sure?"; +"subscription.confirm.title" = "Sigur?"; /* Header for section for activating subscription on other devices */ -"subscription.devices.header" = "Activate on Other Devices"; +"subscription.devices.header" = "Activează pe alte dispozitive"; /* Footer for section for activating subscription on other devices when email was not yet added */ -"subscription.devices.no.email.footer" = "Add an optional email to your subscription or use your Apple ID to access Privacy Pro on other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.no.email.footer" = "Adaugă o adresă de e-mail opțională la abonamentul tău pentru a accesa Privacy Pro pe alte dispozitive. **[Află mai multe](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Footer for section for activating subscription on other devices when email is added */ -"subscription.devices.with.email.footer" = "Use this email to activate your subscription in Settings > Privacy Pro in the DuckDuckGo app on your other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.with.email.footer" = "Folosește această adresă de e-mail pentru a-ți activa abonamentul în Setări > Privacy Pro în aplicația DuckDuckGo pe celelalte dispozitive ale tale. **[Află mai multe](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Alert content for not found subscription */ -"subscription.email.inactive.alert.message" = "The subscription associated with this email is no longer active."; +"subscription.email.inactive.alert.message" = "Abonamentul asociat acestei adrese de e-mail nu mai este activ."; /* Alert title for not found subscription */ -"subscription.email.inactive.alert.title" = "Subscription Not Found"; +"subscription.email.inactive.alert.title" = "Abonament inexistent"; /* Alert content for not found subscription */ -"subscription.expired.alert.message" = "The subscription associated with this Apple ID is no longer active."; +"subscription.expired.alert.message" = "Abonamentul asociat acestui Apple ID nu mai este activ."; /* FAQ Button */ -"subscription.faq" = "FAQs and Support"; +"subscription.faq" = "Întrebări frecvente și asistență"; /* FAQ Description */ -"subscription.faq.description" = "Get answers to frequently asked questions or contact Privacy Pro support from our help pages."; +"subscription.faq.description" = "Primește răspunsuri la întrebările frecvente sau contactează asistența Privacy Pro din paginile noastre de ajutor."; + +/* Send Feedback Button */ +"subscription.feedback" = "Trimite feedback"; /* Cancel action for the existing subscription dialog */ -"subscription.found.cancel" = "Cancel"; +"subscription.found.cancel" = "Renunță"; /* Restore action for the existing subscription dialog */ -"subscription.found.restore" = "Restore"; +"subscription.found.restore" = "Restaurează"; /* Message for the existing subscription dialog */ -"subscription.found.text" = "We found a subscription associated with this Apple ID."; +"subscription.found.text" = "Am găsit un abonament asociat cu acest Apple ID."; /* Title for the existing subscription dialog */ -"subscription.found.title" = "Subscription Found"; +"subscription.found.title" = "Abonament găsit"; /* Help and support Section header */ -"subscription.help" = "Help and support"; +"subscription.help" = "Ajutor și suport"; /* Manage Plan header */ -"subscription.manage.plan" = "Manage Plan"; +"subscription.manage.plan" = "Gestionează planul"; /* Header for the subscription section */ -"subscription.manage.title" = "Subscription"; +"subscription.manage.title" = "Abonament"; /* Alert content for not found subscription */ -"subscription.notFound.alert.message" = "There is no subscription associated with this Apple ID."; +"subscription.notFound.alert.message" = "Nu există niciun abonament asociat cu acest Apple ID."; /* Alert title for not found subscription */ -"subscription.notFound.alert.title" = "Subscription Not Found"; +"subscription.notFound.alert.title" = "Abonament inexistent"; /* View plans button text */ -"subscription.notFound.view.plans" = "View Plans"; +"subscription.notFound.view.plans" = "Vezi planurile"; /* Hero Text for Personal information removal */ -"subscription.pir.hero" = "Activate Privacy Pro on desktop to set up Personal Information Removal"; +"subscription.pir.hero" = "Activează Privacy Pro pe desktop pentru a configura Personal Information Removal"; /* Description on how to use Personal information removal in desktop. The first placeholder references a location in the Desktop application. Privacy Pro>, and the second, the menu entry. i.e. */ -"subscription.pir.heroText" = "In the DuckDuckGo browser for desktop, go to %1$@ and click %2$@ to get started."; +"subscription.pir.heroText" = "În browserul DuckDuckGo pentru desktop, accesează %1$@ și dă clic pe %2$@ pentru a începe."; /* Settings references a menu in the Desktop app, Privacy Pro, references our product name */ -"subscription.pir.heroTextLocation" = "Settings > Privacy Pro"; +"subscription.pir.heroTextLocation" = "Setări > Privacy Pro"; /* Menu item for enabling Personal Information Removal on Desktop */ -"subscription.pir.heroTextMenyEntry" = "I have a subscription"; +"subscription.pir.heroTextMenyEntry" = "Am un abonament"; /* Text for the 'macOS' button */ "subscription.pir.macos" = "Mac"; @@ -2476,28 +2638,28 @@ "subscription.pir.windows" = "Windows"; /* Progress view title when completing the purchase */ -"subscription.progress.view.completing.purchase" = "Completing purchase..."; +"subscription.progress.view.completing.purchase" = "Se finalizează achiziția..."; /* Progress view title when starting the purchase */ -"subscription.progress.view.purchasing.subscription" = "Purchase in progress..."; +"subscription.progress.view.purchasing.subscription" = "Achiziție în desfășurare..."; /* Progress view title when restoring past subscription purchase */ -"subscription.progress.view.restoring.subscription" = "Restoring subscription..."; +"subscription.progress.view.restoring.subscription" = "Se restabilește abonamentul..."; /* Remove from this device button */ -"subscription.remove.from.device.button" = "Remove From This Device"; +"subscription.remove.from.device.button" = "Elimină de pe acest dispozitiv"; /* Remove from device confirmation dialog text */ -"subscription.remove.from.device.text" = "You will no longer be able to access your Privacy Pro subscription on this device. This will not cancel your subscription, and it will remain active on your other devices."; +"subscription.remove.from.device.text" = "Nu vei mai putea-ți să accesezi abonamentul Privacy Pro de pe acest dispozitiv. Acest lucru nu îți va anula abonamentul, care va rămâne activ pe celelalte dispozitive."; /* Remove from device confirmation dialog title */ -"subscription.remove.from.device.title" = "Remove from this device?"; +"subscription.remove.from.device.title" = "Dorești să elimini de pe acest dispozitiv?"; /* Remove subscription button text */ -"subscription.remove.subscription" = "Remove Subscription"; +"subscription.remove.subscription" = "Elimină abonamentul"; /* Remove subscription cancel button text */ -"subscription.remove.subscription.cancel" = "Cancel"; +"subscription.remove.subscription.cancel" = "Renunță"; /* Button text for general error message */ "subscription.restore.backend.error.button" = "Înapoi la Setări"; @@ -2509,28 +2671,43 @@ "subscription.restore.backend.error.title" = "Ceva nu a funcționat corect"; /* Alert for general error message */ -"subscription.restore.general.error.message" = "The App Store was unable to process your purchase. Please try again later."; +"subscription.restore.general.error.message" = "Magazinul de aplicații nu a putut prelucra achiziția ta. Încearcă din nou mai târziu."; /* Alert for general error title */ -"subscription.restore.general.error.title" = "Something Went Wrong"; +"subscription.restore.general.error.title" = "Ceva nu a funcționat corect"; /* Alert button text for restored purchase alert */ "subscription.restore.success.alert.button" = "OK"; /* Alert message for restored purchase */ -"subscription.restore.success.alert.message" = "Your purchases have been restored."; +"subscription.restore.success.alert.message" = "Achizițiile tale au fost restaurate."; /* Alert title for restored purchase */ -"subscription.restore.success.alert.title" = "You’re all set."; +"subscription.restore.success.alert.title" = "Ești gata."; + +/* Subtitle in header when subscribed */ +"subscription.subscribed" = "Abonat"; /* Subscription Expired Data. This reads as 'Your subscription expired on (date)' */ "subscription.subscription.expired.caption" = "Abonamentul tău a expirat la data de %@"; -/* Subscription expiration info. This reads as 'Your (monthly or annual) subscription expires on (date)' */ -"subscription.subscription.expiring.caption" = "Your %1$@ subscription expires on %2$@."; +/* Monthly subscription expiration info where parameter is expiration date. This reads as 'Your monthly subscription expires on (date)' */ +"subscription.subscription.expiring.monthly.caption" = "Abonamentul tău lunar expiră la data de %@."; + +/* Unknown period subscription expiration info where parameter is expiration date. This reads as 'Your subscription expires on (date)' */ +"subscription.subscription.expiring.unknown.caption" = "Abonamentul tău expiră la data de %@."; + +/* Annual subscription expiration info where parameter is expiration date. This reads as 'Your annual subscription expires on (date)' */ +"subscription.subscription.expiring.yearly.caption" = "Abonamentul tău anual expiră la data de %@."; + +/* Monthly subscription renewal info where parameter is renewal date. This reads as 'Your monthly subscription renews on (date)' */ +"subscription.subscription.renewing.monthly.caption" = "Abonamentul tău lunar se reînnoiește la data de %@."; + +/* Unknown period subscription renewal info where parameter is renewal date. This reads as 'Your subscription renews on (date)' */ +"subscription.subscription.renewing.unknown.caption" = "Abonamentul tău se reînnoiește la data de %@."; -/* Subscription renewal info. This reads as 'Your (monthly or annual) subscription renews on (date)' */ -"subscription.subscription.renewing.caption" = "Your %1$@ subscription renews on %2$@."; +/* Annual subscription renewal info where parameter is renewal date. This reads as 'Your annual subscription renews on (date)' */ +"subscription.subscription.renewing.yearly.caption" = "Abonamentul tău anual se reînnoiește la data de %@."; /* Navigation bar Title for subscriptions */ "subscription.title" = "Privacy Pro"; @@ -2560,7 +2737,7 @@ "sync.promo.dismiss.action" = "Nu, mulțumesc"; /* Message for the Sync Promotion banner when user has passwords that can be synced */ -"sync.promo.passwords.message" = "Nu este nevoie să ai un cont. Criptarea de la un punct la altul (end-to-end) înseamnă că nimeni în afară de tine nu îți poate vedea parolele, nici măcar noi."; +"sync.promo.passwords.message" = "Nu este nevoie să ai un cont. Criptarea end-to-end înseamnă că nimeni în afară de tine nu îți poate vedea parolele, nici măcar noi."; /* Title for the Sync Promotion banner */ "sync.promo.passwords.title" = "Sincronizează și creează copii de rezervă pentru parolele tale"; @@ -2677,118 +2854,118 @@ "voiceSearch.footer.note.old" = "Componenta audio este prelucrată pe dispozitiv. Nu este stocată sau distribuită nimănui, nici DuckDuckGo."; /* Cancel action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.cancel" = "Dismiss"; +"vpn.access-revoked.alert.action.cancel" = "Renunță"; /* Primary action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.subscribe" = "Subscribe"; +"vpn.access-revoked.alert.action.subscribe" = "Înscrie-te"; /* Alert message for the alert when the Privacy Pro subscription expiress */ -"vpn.access-revoked.alert.message" = "Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"vpn.access-revoked.alert.message" = "Abonează-te la Privacy Pro pentru a reconecta DuckDuckGo VPN."; /* Alert title for the alert when the Privacy Pro subscription expires */ -"vpn.access-revoked.alert.title" = "VPN disconnected due to expired subscription"; +"vpn.access-revoked.alert.title" = "VPN-ul s-a deconectat din cauza unui abonament expirat"; /* Title for the VPN widget onboarding screen */ "vpn.addWidget.settings.title" = "Găsește și selectează DuckDuckGo. Apoi glisează la VPN și selectează Adaugă widget."; /* Title for the Cancel button of the VPN feedback form */ -"vpn.feedback-form.button.cancel" = "Cancel"; +"vpn.feedback-form.button.cancel" = "Renunță"; /* Title for the Done button of the VPN feedback form */ -"vpn.feedback-form.button.done" = "Done"; +"vpn.feedback-form.button.done" = "Terminat"; /* Title for the Submit button of the VPN feedback form */ -"vpn.feedback-form.button.submit" = "Submit"; +"vpn.feedback-form.button.submit" = "Trimite"; /* Title for the Submitting state of the VPN feedback form */ -"vpn.feedback-form.button.submitting" = "Submitting…"; +"vpn.feedback-form.button.submitting" = "Se transmite..."; /* Title for the browser crash/freeze category of the VPN feedback form */ -"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN causes browser to crash or freeze"; +"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN face ca browserul să se închidă sau să se blocheze"; /* Title for the 'VPN fails to connect' category of the VPN feedback form */ -"vpn.feedback-form.category.fails-to-connect" = "VPN fails to connect"; +"vpn.feedback-form.category.fails-to-connect" = "VPN nu reușește să se conecteze"; /* Title for the 'VPN feature request' category of the VPN feedback form */ -"vpn.feedback-form.category.feature-request" = "VPN feature request"; +"vpn.feedback-form.category.feature-request" = "Solicitare de funcționalitate VPN"; /* Title for the category 'VPN causes issues with other apps or websites' category of the VPN feedback form */ -"vpn.feedback-form.category.issues-with-apps" = "VPN causes issues with other apps or websites"; +"vpn.feedback-form.category.issues-with-apps" = "VPN cauzează probleme cu alte aplicații sau site-uri"; /* Title for the local device connectivity category of the VPN feedback form */ -"vpn.feedback-form.category.local-device-connectivity" = "VPN won't let me connect to local device"; +"vpn.feedback-form.category.local-device-connectivity" = "VPN nu mă lasă să mă conectez la dispozitivul local"; /* Title for the 'other VPN feedback' category of the VPN feedback form */ -"vpn.feedback-form.category.other" = "Other VPN feedback"; +"vpn.feedback-form.category.other" = "Alt feedback despre VPN"; /* Title for the category selection state of the VPN feedback form */ -"vpn.feedback-form.category.select-category" = "Select a category"; +"vpn.feedback-form.category.select-category" = "SELECTEAZĂ O CATEGORIE"; /* Title for the 'VPN is too slow' category of the VPN feedback form */ -"vpn.feedback-form.category.too-slow" = "VPN connection is too slow"; +"vpn.feedback-form.category.too-slow" = "Conexiunea VPN este prea lentă"; /* Title for the 'unable to install' category of the VPN feedback form */ -"vpn.feedback-form.category.unable-to-install" = "Unable to install VPN"; +"vpn.feedback-form.category.unable-to-install" = "Nu pot instala VPN"; /* Title for the feedback sent view description of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.description" = "Your feedback will help us improve the\nDuckDuckGo VPN."; +"vpn.feedback-form.sending-confirmation.description" = "Feedbackul tău ne va ajuta să îmbunătățim\nDuckDuckGo VPN."; /* Title for the feedback sending error text of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.error" = "We couldn't send your feedback right now, please try again."; +"vpn.feedback-form.sending-confirmation.error" = "Nu am putut trimite feedbackul tău acum, încearcă din nou."; /* Title for the feedback sent view title of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.title" = "Thank you!"; +"vpn.feedback-form.sending-confirmation.title" = "Mulțumim!"; /* Toast message when the VPN feedback form is submitted successfully */ -"vpn.feedback-form.submitted.message" = "Thank You! Feedback submitted."; +"vpn.feedback-form.submitted.message" = "Mulțumesc! Feedback trimis."; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-1" = "Please describe what's happening, what you expected to happen, and the steps that led to the issue:"; +"vpn.feedback-form.text-1" = "Descrie ce se întâmplă, ce te așteptai să se întâmple și pașii care au dus la apariția problemei:"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-2" = "In addition to the details entered into this form, your app issue report will contain:"; +"vpn.feedback-form.text-2" = "Pe lângă detaliile introduse în acest formular, raportul privind problemele aplicației tale va conține:"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-3" = "• Whether specific DuckDuckGo features are enabled"; +"vpn.feedback-form.text-3" = "• Dacă funcțiile specifice DuckDuckGo sunt activate"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-4" = "• Aggregate DuckDuckGo app diagnostics"; +"vpn.feedback-form.text-4" = "• Diagnosticare agregată a aplicației DuckDuckGo"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-5" = "By tapping \"Submit\" I agree that DuckDuckGo may use the information in this report for purposes of improving the app's features."; +"vpn.feedback-form.text-5" = "Atingând „Transmite”, sunt de acord ca DuckDuckGo să utilizeze informațiile din acest raport în scopul îmbunătățirii funcțiilor aplicației."; /* Title for each screen of the VPN feedback form */ -"vpn.feedback-form.title" = "Help Improve the DuckDuckGo VPN"; +"vpn.feedback-form.title" = "Contribuie la îmbunătățirea DuckDuckGo VPN"; /* VPN settings screen cell text for adding the VPN widget to the home screen */ "vpn.settings.add.widget" = "Adaugă un widget VPN la ecranul de întâmpinare"; /* Disclaimer for the DNS Server section on the DNS Server screen */ -"vpn.settings.dns.section-disclaimer" = "Using a custom DNS server can impact browsing speeds and expose your activity to third parties if the server isn't secure or reliable."; +"vpn.settings.dns.section-disclaimer" = "Folosirea unui server DNS personalizat poate afecta viteza de navigare și îți poate expune activitatea unor terțe părți, dacă serverul nu este sigur sau de încredere."; /* Header text for the DNS section on the VPN Settings screen */ "vpn.settings.dns.section-header" = "DNS"; /* Title for the Apply button on the DNS Server setting screen */ -"vpn.settings.dns.server.apply.button.title" = "Apply"; +"vpn.settings.dns.server.apply.button.title" = "Aplică"; /* Default value for the DNS Server row on the VPN Settings screen */ "vpn.settings.dns.server.default.value" = "DuckDuckGo"; /* Title for the IPv4 Address setting */ -"vpn.settings.dns.server.ipv4.title" = "IPv4 Address"; +"vpn.settings.dns.server.ipv4.title" = "Adresa IPv4"; /* Custom option for the DNS Server setting */ -"vpn.settings.dns.server.option.custom" = "Custom"; +"vpn.settings.dns.server.option.custom" = "Personalizat"; /* Recommended option for the DNS Server setting */ -"vpn.settings.dns.server.option.default" = "DuckDuckGo (Recommended)"; +"vpn.settings.dns.server.option.default" = "DuckDuckGo (Recomandat)"; /* Title for the DNS Server setting screen */ -"vpn.settings.dns.server.screen.title" = "DNS Server"; +"vpn.settings.dns.server.screen.title" = "Server DNS"; /* Title for the DNS Server row on the VPN Settings screen */ -"vpn.settings.dns.server.title" = "DNS Server"; +"vpn.settings.dns.server.title" = "Server DNS"; /* Title for the button to enable push notifications in system settings */ "waitlist.allow-notifications" = "Permite notificările"; diff --git a/DuckDuckGo/ro.lproj/OmniBar.strings b/DuckDuckGo/ro.lproj/OmniBar.strings index 790c760196..b48fd2d612 100644 --- a/DuckDuckGo/ro.lproj/OmniBar.strings +++ b/DuckDuckGo/ro.lproj/OmniBar.strings @@ -7,6 +7,9 @@ /* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "Dt8-We-5tV"; */ "Dt8-We-5tV.accessibilityLabel" = "Ștergere text"; +/* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "gb5-wL-4OW"; */ +"gb5-wL-4OW.accessibilityLabel" = "Ștergere text"; + /* Class = "UIButton"; accessibilityLabel = "Share"; ObjectID = "hor-Jd-3kw"; */ "hor-Jd-3kw.accessibilityLabel" = "Partajare"; diff --git a/DuckDuckGo/ro.lproj/Settings.strings b/DuckDuckGo/ro.lproj/Settings.strings index 7a72b0fcf1..70453292c3 100644 --- a/DuckDuckGo/ro.lproj/Settings.strings +++ b/DuckDuckGo/ro.lproj/Settings.strings @@ -10,9 +10,6 @@ /* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "5YD-Od-uDU"; */ "5YD-Od-uDU.text" = "Șterge automat datele"; -/* Class = "UIButton"; normalTitle = "Settings"; ObjectID = "8YG-7Q-Czd"; */ -"8YG-7Q-Czd.normalTitle" = "Setări"; - /* Class = "UILabel"; text = "App Exit, Inactive for 1 Hour"; ObjectID = "9EC-lx-77p"; */ "9EC-lx-77p.text" = "Ieșire din aplicație, inactiv timp de 1 oră"; @@ -76,9 +73,6 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Site-uri neprotejate"; -/* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ -"ssa-zd-L3T.title" = "Dimensiune text"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Afișează tastatura la"; diff --git a/DuckDuckGo/ru.lproj/DaxOnboarding.strings b/DuckDuckGo/ru.lproj/DaxOnboarding.strings index 47cab7362e..966c660c81 100644 --- a/DuckDuckGo/ru.lproj/DaxOnboarding.strings +++ b/DuckDuckGo/ru.lproj/DaxOnboarding.strings @@ -1,12 +1,6 @@ /* Class = "UIButton"; normalTitle = "Hide"; ObjectID = "hgy-75-OvJ"; */ "hgy-75-OvJ.normalTitle" = "Скрыть"; -/* Class = "UILabel"; text = "Welcome to\nDuckDuckGo!"; ObjectID = "Lvy-9G-3gr"; */ -"Lvy-9G-3gr.text" = "Добро пожаловать в\nDuckDuckGo!"; - -/* Class = "UIButton"; normalTitle = "Let’s Do It!"; ObjectID = "oAv-Zt-uIt"; */ -"oAv-Zt-uIt.normalTitle" = "Поехали!"; - /* Class = "UIButton"; normalTitle = "Button"; ObjectID = "zJt-F9-L48"; */ "zJt-F9-L48.normalTitle" = "Кнопка"; diff --git a/DuckDuckGo/ru.lproj/Localizable.strings b/DuckDuckGo/ru.lproj/Localizable.strings index 8d3dec89b8..660e9b547e 100644 --- a/DuckDuckGo/ru.lproj/Localizable.strings +++ b/DuckDuckGo/ru.lproj/Localizable.strings @@ -341,7 +341,7 @@ "autofill.keep-enabled.alert.disable" = "Выключить"; /* A link that takes the user to the DuckDuckGo help pages explaining password managers */ -"autofill.learn.more.link.title" = "Learn More"; +"autofill.learn.more.link.title" = "Узнать больше"; /* Button displayed after saving/updating an autofill login that takes the user to the saved login */ "autofill.login-save-action-button.toast" = "Просмотр"; @@ -977,9 +977,6 @@ /* ad = advertisment */ "dax.onboarding.home.subsequent" = "Проще некуда!\n\nПросматривать сайтам с нами — значит навсегда попрощаться с назойливой рекламой 👍"; -/* No comment provided by engineer. */ -"dax.onboarding.message" = "Интернет набит трекерами.\n\nНо выход есть! Пользоваться сетью без слежки проще, чем вы думали."; - /* No comment provided by engineer. */ "Debug" = "Отладка"; @@ -1080,7 +1077,7 @@ "duckPlayer.never.label" = "Никогда"; /* Body text for the modal feature explanation */ -"duckplayer.presentation.modal.body" = "Проигрыватель Duck Player позволяет смотреть видео из YouTube в браузере DuckDuckGo в режиме кинотеатра без целевой рекламы. Просмотренные ролики не влияют на рекомендации."; +"duckplayer.presentation.modal.body" = "Проигрыватель Duck Player позволяет смотреть видео из YouTube в браузере DuckDuckGo без целевой рекламы. Просмотренные ролики не влияют на рекомендации."; /* Button that will dismiss the modal */ "duckplayer.presentation.modal.dismiss-button" = "Понятно"; @@ -1088,17 +1085,23 @@ /* Two line title (separated by \n) for the feature explanation */ "duckplayer.presentation.modal.title" = "Шквал рекламы на YouTube? Попробуйте Duck Player!"; +/* Two line title (separated by \n) for the feature explanation */ +"duckplayer.presentation.modal.title.youtube" = "Шквал рекламы на YouTube? Только не с Duck Player!"; + /* Footer label in the settings screen for Duck Player */ "duckplayer.settings.footer" = "DuckDuckGo обеспечивает все основные функции для защиты конфиденциальности в интернете."; /* Text explaining what Duck Player is in the settings screen. */ -"duckplayer.settings.info-text" = "Проигрыватель Duck Player обеспечивает беспрепятственный просмотр без персонализированной рекламы и влияния просмотренных роликов на рекомендации в YouTube."; +"duckplayer.settings.info-text" = "Проигрыватель Duck Player позволяет смотреть видео из YouTube в браузере DuckDuckGo без целевой рекламы. Просмотренные ролики не влияют на рекомендации."; /* Button that takes the user to learn more about Duck Player. */ "duckplayer.settings.learn-more" = "Узнать больше"; +/* Settings screen cell text for DuckPlayer settings to open in new tab */ +"duckplayer.settings.open-new-tab-label" = "Открывать Duck Player в новой вкладке"; + /* Settings screen cell text for DuckPlayer settings */ -"duckplayer.settings.open-videos-in" = "Открывать видео в Duck Player"; +"duckplayer.settings.open-videos-in" = "Открывать видео из YouTube в Duck Player"; /* Settings screen cell text for DuckPlayer settings */ "duckplayer.settings.title" = "Проигрыватель Duck Player"; @@ -1169,9 +1172,6 @@ /* Title for prompt to sign up for email protection */ "email.signup-prompt.title" = "Скрытие адреса почты\nи блокировка трекеров"; -/* No comment provided by engineer. */ -"empty!" = "empty!"; - /* Empty list state placholder */ "empty.bookmarks" = "Закладок пока нет"; @@ -1322,6 +1322,39 @@ /* Confirmation button */ "feedback.form.submit" = "Отправить"; +/* Title of the feedback form */ +"feedback.form.title" = "Помогите нам улучшить Privacy Pro"; + +/* Category for Identity Theft Restoration feedback */ +"feedback.general.category.itr" = "Identity Theft Restoration"; + +/* Category for Personal Info Removal feedback */ +"feedback.general.category.pir" = "Удаление личной информации"; + +/* Category for subscription and payments feedback */ +"feedback.general.category.ppro" = "Подписки и платежи"; + +/* Prompt to select a category for general feedback */ +"feedback.general.category.select" = "ВЫБЕРИТЕ КАТЕГОРИЮ"; + +/* Category for VPN feedback */ +"feedback.general.category.vpn" = "VPN"; + +/* Category for access code issues */ +"feedback.itr.category.access-code" = "Проблема с кодом доступа"; + +/* Category for issues contacting an advisor */ +"feedback.itr.category.cant-contact-advisor" = "Невозможно связаться с консультантом"; + +/* Prompt to select a category for Identity Theft Restoration feedback */ +"feedback.itr.category.select" = "ВЫБЕРИТЕ КАТЕГОРИЮ"; + +/* Category for other Identity Theft Restoration issues */ +"feedback.itr.category.something-else" = "Что-то другое"; + +/* Category for unhelpful advisor calls */ +"feedback.itr.category.unhelpful" = "Мало толку от звонка консультанту"; + /* No comment provided by engineer. */ "feedback.negative.form.genericPlaceholder" = "Опишите максимально подробно"; @@ -1361,6 +1394,24 @@ /* No comment provided by engineer. */ "feedback.performance.slowLoading" = "Медленно загружаются страницы или результаты поиска"; +/* Category for when scan finds incorrect records */ +"feedback.pir.category.not-me" = "Найденная информация принадлежит не мне"; + +/* Category for when scan doesn't find info on a specific site */ +"feedback.pir.category.nothing-on-site" = "Поиск не обнаружил мои данные на конкретном сайте"; + +/* Category for other Personal Info Removal issues */ +"feedback.pir.category.other" = "Что-то другое"; + +/* Category for when the removal process is stuck */ +"feedback.pir.category.removal-stuck" = "Процесс удаления застопорился"; + +/* Category for when the scan is stuck */ +"feedback.pir.category.scan-stuck" = "Поиск данных застопорился"; + +/* Prompt to select a category for Personal Info Removal feedback */ +"feedback.pir.category.select" = "ВЫБЕРИТЕ КАТЕГОРИЮ"; + /* Header above input field */ "feedback.positive.form.header" = "Добавить комментарий"; @@ -1379,6 +1430,15 @@ /* Button encouraging uses to share details aboout their feedback */ "feedback.positive.submit" = "Добавить комментарий"; +/* Category for other Privacy Pro issues */ +"feedback.ppro.category.other" = "Что-то другое"; + +/* Category for one-time password issues */ +"feedback.ppro.category.otp" = "Проблема с одноразовым паролем"; + +/* Prompt to select a category for Privacy Pro feedback */ +"feedback.ppro.category.select" = "ВЫБЕРИТЕ КАТЕГОРИЮ"; + /* No comment provided by engineer. */ "feedback.start.footer" = "Ваш отзыв важен для нас. Он полностью анонимный."; @@ -1529,12 +1589,6 @@ /* No comment provided by engineer. */ "keyCommandShowAllTabs" = "Показать все вкладки"; -/* Please preserve newline character */ -"launchscreenWelcomeMessage" = "Добро пожаловать в\nDuckDuckGo!"; - -/* No comment provided by engineer. */ -"LOREM IPSUM" = "LOREM IPSUM"; - /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "Приложение DuckDuckGo для Mac — это высокая скорость, функциональность и беспрецедентная конфиденциальность."; @@ -1586,62 +1640,83 @@ /* Edit button */ "navigation.title.edit" = "Редактировать"; -/* String indicating NetP is connected when viewed from the settings screen */ -"netP.cell.connected" = "Connected"; - -/* String indicating NetP is disconnected when viewed from the settings screen */ -"netP.cell.disconnected" = "Not connected"; - -/* Title for the DuckDuckGo VPN feature in settings */ -"netP.settings.title" = "VPN"; +/* String indicating NetP is snoozing when viewed from the settings screen */ +"netP.cell.snoozing" = "Отложено"; /* Title for the DuckDuckGo VPN feature */ "netP.title" = "DuckDuckGo VPN"; +/* Button title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.action" = "Добавить виджет"; + +/* Message for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.message" = "Включать и выключать VPN можно прямо с домашнего экрана."; + +/* Title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.title" = "Специальный VPN-виджет"; + /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN отключен ввиду окончания подписки. Чтобы вновь подключиться к DuckDuckGo VPN, оформите подписку Privacy Pro."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Функции защиты сети (Network Protection) не удалось установить соединение. Повторите попытку позже."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "Подключиться к DuckDuckGo VPN не удалось. Повторите попытку позже."; /* Title for the VPN FAQ screen. */ "network.protection.faq.title" = "Часто задаваемые вопросы по DuckDuckGo VPN"; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Прервана работа функции защиты сети (Network Protection). Выполняется попытка повторного подключения..."; +/* Message for tooltip about geoswitching */ +"network.protection.geoswitching.tip.message" = "Чтобы изменить свою VPN-геопозицию, достаточно подключиться к одному из наших серверов, расположенных по всему миру."; + +/* Title for tooltip about geoswitching */ +"network.protection.geoswitching.tip.title" = "Выбор геопозиции"; + +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "Работа DuckDuckGo VPN была прервана. Выполняется попытка повторного подключения..."; /* Message for the network protection invite dialog */ -"network.protection.invite.dialog.message" = "Enter your invite code to get started."; +"network.protection.invite.dialog.message" = "Для начала введите пригласительный код."; /* Title for the network protection invite screen */ "network.protection.invite.dialog.title" = "Приглашаем вас оценить сервис DuckDuckGo VPN"; /* Prompt for the network protection invite code text field */ -"network.protection.invite.field.prompt" = "Invite Code"; +"network.protection.invite.field.prompt" = "Код приглашения"; /* Message for the network protection invite success view */ -"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; +"network.protection.invite.success.message" = "Скройте свое местоположение от сайтов и список посещаемых адресов — от интернет-провайдеров и других пользователей в своей сети."; /* Title for the network protection invite success view */ -"network.protection.invite.success.title" = "Success! You’re in."; +"network.protection.invite.success.title" = "Готово. Добро пожаловать!"; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; /* Title text for an iOS quick action that opens VPN settings */ "network.protection.quick-action.open-vpn" = "Открыть настройки VPN"; +/* Button title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.action" = "Подробнее"; + +/* Message for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.message" = "VPN-соединение можно приостановить для посещения сайтов и приложений, которые блокируют трафик по VPN."; + +/* Title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.title" = "Без проблем из-за VPN"; + +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "Работа VPN отложена на %@"; + /* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ "network.protection.status.connected.format" = "Подключен · %@"; /* The label for the NetP VPN when connecting */ -"network.protection.status.connecting" = "Connecting..."; +"network.protection.status.connecting" = "Выполняется подключение..."; /* The label for the NetP VPN when disconnected */ -"network.protection.status.disconnected" = "Not connected"; +"network.protection.status.disconnected" = "Не подключен"; /* The label for the NetP VPN when disconnecting */ -"network.protection.status.disconnecting" = "Disconnecting..."; +"network.protection.status.disconnecting" = "Отключение..."; /* Message label text for the status view when VPN is disconnected */ "network.protection.status.header.message.off" = "Выполните подключение для защиты\nтрафика всех устройств."; @@ -1655,26 +1730,41 @@ /* Header title label text for the status view when VPN is connected */ "network.protection.status.header.title.on" = "DuckDuckGo VPN включен"; +/* Header title label text for the status view when VPN is snoozing */ +"network.protection.status.header.title.snoozed" = "Работа DuckDuckGo VPN отложена"; + /* The status view 'Share Feedback' button which is shown inline on the status view after the temporary free use footer text */ -"network.protection.status.menu.share.feedback" = "Share Feedback"; +"network.protection.status.menu.share.feedback" = "Оставьте нам отзыв"; + +/* The label for the NetP VPN when paused */ +"network.protection.status.paused" = "На паузе"; + +/* The label for when NetP VPN is snoozing plus the length of time remaining formatted as '0:00' */ +"network.protection.status.snoozing.format" = "Работа отложена, осталось %@"; + +/* Snooze button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.snooze" = "Отложить на 20 минут"; + +/* Wake Up button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.wake-up" = "Разбудить"; /* Connection details label shown in NetworkProtection's status view. */ -"network.protection.status.view.connection.details" = "Connection Details"; +"network.protection.status.view.connection.details" = "О подключении"; /* Custom DNS label shown in NetworkProtection's status view. */ -"network.protection.status.view.custom.dns" = "DNS Server"; +"network.protection.status.view.custom.dns" = "DNS-сервер"; /* Generic connection failed error message shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.message" = "Please try again later."; +"network.protection.status.view.error.connection.failed.message" = "Повторите попытку позже."; /* Generic connection failed error title shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; +"network.protection.status.view.error.connection.failed.title" = "Подключиться не удалось."; /* IP Address label shown in NetworkProtection's status view. */ -"network.protection.status.view.ip.address" = "IP Address"; +"network.protection.status.view.ip.address" = "IP-адрес"; /* Location label shown in NetworkProtection's status view. */ -"network.protection.status.view.location" = "Location"; +"network.protection.status.view.location" = "Геопозиция"; /* Label shown on the title of the settings section in NetworkProtection's status view. */ "network.protection.status.view.settings.section.title" = "Настроить"; @@ -1682,12 +1772,15 @@ /* Title label text for the status view when netP is disconnected */ "network.protection.status.view.title" = "VPN"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Защита сети (Network Protection) включена. Ваши геопозиция и онлайн-активность скрыты от посторонних глаз."; +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "DuckDuckGo VPN включен. Ваши геопозиция и онлайн-активность скрыты от посторонних глаз."; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Трафик устройства направляется через: %@"; +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "VPN вышел из отложенного режима. Трафик устройства направляется через: %@."; + /* Title for the button to link to the iOS app settings and enable notifications app-wide. */ "network.protection.turn.on.notifications.button.title" = "Включить уведомления"; @@ -1698,13 +1791,13 @@ "network.protection.vpn.about" = "О нас"; /* Section header for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.section.header" = "Notifications"; +"network.protection.vpn.alerts.section.header" = "Уведомления"; /* List section footer for the toggle for VPN alerts. */ "network.protection.vpn.alerts.toggle.section.footer" = "DuckDuckGo оповестит вас о разрыве соединения или изменении состояния VPN."; /* Title for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.toggle.title" = "VPN Notifications"; +"network.protection.vpn.alerts.toggle.title" = "Уведомления о VPN"; /* Title for the data volume section in the VPN status screen */ "network.protection.vpn.data-volume" = "Объем трафика"; @@ -1713,13 +1806,13 @@ "network.protection.vpn.exclude.local.networks.setting.footer" = "Настройте локальный трафик так, чтобы он поступал в обход VPN на устройства локальной сети, например, на принтер."; /* Header text for the Exclude Local Networks setting item. */ -"network.protection.vpn.exclude.local.networks.setting.header" = "General"; +"network.protection.vpn.exclude.local.networks.setting.header" = "Основные"; /* Title for the Exclude Local Networks setting item. */ "network.protection.vpn.exclude.local.networks.setting.title" = "Исключить локальные сети"; /* Title for the VPN Location screen's All Countries section. */ -"network.protection.vpn.location.all.countries.section.title" = "All Countries"; +"network.protection.vpn.location.all.countries.section.title" = "Все страны"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.connected" = "Место подключения"; @@ -1734,7 +1827,7 @@ "network.protection.vpn.location.recommended.section.footer" = "Автоматически подключаться к ближайшему найденному серверу."; /* Title for the VPN Location screen's Recommended section. */ -"network.protection.vpn.location.recommended.section.title" = "Recommended"; +"network.protection.vpn.location.recommended.section.title" = "Рекомендуем"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.selected" = "Выбранное место"; @@ -1752,13 +1845,13 @@ "network.protection.vpn.preferred.location.nearest" = "Ближайшее место"; /* Title for the Preferred Location VPN Settings item. */ -"network.protection.vpn.preferred.location.title" = "Preferred Location"; +"network.protection.vpn.preferred.location.title" = "Предпочитаемое местоположение"; /* Footer text for the DNS server setting item. */ -"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo routes DNS queries through our DNS servers so your internet provider can't see what websites you visit."; +"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo направляет DNS-запросы через собственные DNS-серверы, поэтому ваш интернет-провайдер не увидит, какие сайты вы посещаете."; /* Title for the FAQ row in the VPN status screen. */ -"network.protection.vpn.settings.faq" = "FAQs and Support"; +"network.protection.vpn.settings.faq" = "ЧаВо и поддержка"; /* Title for the feedback row in the VPN status screen. */ "network.protection.vpn.settings.share-feedback" = "Отзыв о VPN-сервисе"; @@ -1940,6 +2033,66 @@ /* Deactivate button */ "pm.deactivate" = "Деактивировать"; +/* Label for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.label" = "Электронная почта (необязательно)"; + +/* Placeholder for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.placeholder" = "имя@почта.com"; + +/* Placeholder for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.placeholder" = "Оставьте комментарий здесь..."; + +/* Title for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.title" = "Общий комментарий"; + +/* Title for the ITR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-itr-problem.title" = "Identity Theft Restoration"; + +/* Title for the PIR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-pir-problem.title" = "Personal Information Removal"; + +/* Title for the Subscriptions and Payments category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-ppro-problem.title" = "Подписки и платежи"; + +/* Placeholder for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.placeholder" = "Опишите, что происходит…"; + +/* Title for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.title" = "Отчет о проблеме"; + +/* Title for the VPN category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-vpn-problem.title" = "VPN"; + +/* Placeholder for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.placeholder" = "Какую функцию вы хотели бы увидеть?"; + +/* Title for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.title" = "Запрос функции"; + +/* Title for the category selection section in the Privacy Pro feedback form */ +"ppro.feedback-form.select-category.title" = "ВЫБЕРИТЕ КАТЕГОРИЮ"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-1" = "Столкнулись с проблемой, которая не затронута в нашем [справочном центре](duck://)? Мы просто обязаны о ней знать.\n\nУкажите адрес эл. почты, если вы хотите, чтобы мы связались с вами по этому вопросу (возможно, мы не ответим на все вопросы):"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-2" = "Помимо указанных выше данных, вместе с отзывом отправляется следующая обезличенная информация:"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-3" = "• сведения о том, были ли активированы некоторые функции браузера"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-4" = "• обобщенные результаты диагностики приложения (например, коды ошибок)"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-5" = "Нажимая кнопку «Отправить», вы разрешаете DuckDuckGo использовать предоставленную информацию для улучшения работы приложения."; + +/* Title for the text box in the Privacy Pro feedback form */ +"ppro.feedback-form.text-box.title" = "Отзыв"; + +/* Title for the Privacy Pro feedback form */ +"ppro.feedback-form.title" = "Отправить отзыв"; + /* Button title for sync bookmarks limits exceeded warning to go to manage bookmarks */ "prefrences.sync.bookmarks-limit-exceeded-action" = "Управление закладками"; @@ -2029,6 +2182,18 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Избранное"; +/* Name of the option the user can chose to give general browser feedback */ +"send.browser.feedback.general-feedback" = "Общий комментарий"; + +/* Name of the option the user can chose to give browser feedback about a problem they enountered */ +"send.browser.feedback.report-problem" = "Отчет о проблеме"; + +/* Name of the option the user can chose to give browser feedback about a feature they would like */ +"send.browser.feedback.request-feature" = "Запрос функции"; + +/* Title of the picker where the user can chose the category of the feedback they want ot send. */ +"send.browser.feedback.select-category" = "ВЫБЕРИТЕ КАТЕГОРИЮ"; + /* Settings cell for About DDG */ "settings.about.ddg" = "Несколько слов о DuckDuckGo"; @@ -2084,6 +2249,9 @@ /* Section footer Autolock description */ "settings.autolock.description" = "Если в системе работает технология Touch ID, Face ID или код-пароль, при запуске вам придется разблокировать приложение."; +/* Settings cell for Browser Feedback */ +"settings.browser.feedback" = "Отзыв о браузере"; + /* Settings screen cell text for Automatically Clearing Data */ "settings.clear.data" = "Автоудаление данных"; @@ -2182,7 +2350,7 @@ "settings.ppro" = "Privacy Pro"; /* Title for Link in the Footer of Privacy Pro section */ -"settings.ppro.footer" = "Privacy Policy and Terms of Service"; +"settings.ppro.footer" = "Политика конфиденциальности и условия обслуживания"; /* Settings screen cell for long press previews */ "settings.previews" = "Предпросмотр долгим нажатием"; @@ -2208,23 +2376,20 @@ /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Настроить положение адресной строки"; -/* Subscription activation pending description */ -"settings.subscription.activation.pending.description" = "This is taking longer than usual, please check back later."; +/* Privacy pro description subtitle in settings when the is activating */ +"settings.subscription.activating" = "Выполняется активация"; -/* Subscription activation pending title */ -"settings.subscription.activation.pending.title" = "Ваша подписка активируется"; +/* Subscription activation pending description */ +"settings.subscription.activation.pending.description" = "Этот процесс занимает больше времени, чем обычно. Вернитесь сюда чуть позже."; /* Data Broker protection cell subtitle for privacy pro */ -"settings.subscription.DBP.subtitle" = "Remove your info from sites that sell it"; +"settings.subscription.DBP.subtitle" = "Удаляет ваши данные с сайтов, которые занимаются их продажей"; /* Data Broker protection cell title for privacy pro */ "settings.subscription.DBP.title" = "Personal Information Removal"; -/* Privacy pro description subtext */ -"settings.subscription.description" = "Три новых рычага для более органичной защиты конфиденциальности:"; - /* I have a Subscription button text for privacy pro */ -"settings.subscription.existing.subscription" = "I Have a Subscription"; +"settings.subscription.existing.subscription" = "У меня уже есть подписка"; /* Subscription expired description */ "settings.subscription.expired.comment" = "Чтобы использовать Privacy Pro, подпишитесь заново"; @@ -2232,23 +2397,26 @@ /* Subscription expired tittle message */ "settings.subscription.expired.title" = "Срок действия вашей подписки Privacy Pro истек"; -/* Privacy pro features list */ -"settings.subscription.features" = " • VPN\n • Personal Information Removal\n • Identity Theft Restoration"; - /* Identity theft restoration cell subtitle for privacy pro */ -"settings.subscription.ITR.subtitle" = "If your identity is stolen, we'll help restore it"; +"settings.subscription.ITR.subtitle" = "Восстанавливает контроль над украденными личными данными"; /* Identity theft restoration cell title for privacy pro */ "settings.subscription.ITR.title" = "Identity Theft Restoration"; /* Get Privacy Pro button text for privacy pro */ -"settings.subscription.learn.more" = "Get Privacy Pro"; +"settings.subscription.learn.more" = "Перейти на Privacy Pro"; /* Subscription Settings button text for privacy pro */ -"settings.subscription.manage" = "Subscription Settings"; +"settings.subscription.manage" = "Настройки подписки"; + +/* Privacy Pro description subtitle in settings */ +"settings.subscription.row.description" = "Включает услуги: VPN и и Identity Theft Restoration (восстановление данных после «кражи личности»)."; -/* Call to action title for Privacy Pro */ -"settings.subscription.subscribe" = "Subscribe to Privacy Pro"; +/* Call to action title for Privacy Pro settings */ +"settings.subscription.subscribe" = "Тариф Privacy Pro для защиты соединения и личных данных"; + +/* Privacy pro description subtitle in settings */ +"settings.subscription.us.description" = "Включает услуги: VPN, Personal Information Removal (удаление личной информации) и Identity Theft Restoration (восстановление данных после «кражи личности»)."; /* VPN cell title for privacy pro */ "settings.subscription.VPN.title" = "VPN"; @@ -2317,67 +2485,64 @@ "siteFeedback.urlPlaceholder" = "На каком сайте возникает проблема?"; /* Subscription Activation Info */ -"subscription.activate..header.description" = "Access your Privacy Pro subscription on this device via Apple ID or an email address."; +"subscription.activate..header.description" = "Преимуществами подписки Privacy Pro можно пользоваться на этом устройстве с помощью Apple ID или адреса электронной почты."; /* Button for adding email address to subscription */ -"subscription.activate.add.email.button" = "Add Email"; +"subscription.activate.add.email.button" = "Добавить адрес эл. почты"; /* Apple ID option for activation */ "subscription.activate.appleid" = "Apple ID"; /* Button text for restoring purchase via Apple ID */ -"subscription.activate.appleid.button" = "Restore Purchase"; +"subscription.activate.appleid.button" = "Восстановить покупку"; /* Description for Apple ID activation */ -"subscription.activate.appleid.description" = "Restore your purchase to activate your subscription on this device."; +"subscription.activate.appleid.description" = "Восстановите покупку, чтобы активировать подписку на этом устройстве."; /* Subscription Activation Info */ -"subscription.activate.description" = "Your subscription is automatically available in DuckDuckGo on any device signed in to your Apple ID."; +"subscription.activate.description" = "Ваша подписка на DuckDuckGo автоматически доступна на любом устройстве: просто войдите в учетную запись Apple ID."; /* Button for editing email address added to subscription */ -"subscription.activate.edit.email.button" = "Edit Email"; +"subscription.activate.edit.email.button" = "Изменить адрес почты"; /* View Title for editing your email account */ -"subscription.activate.edit.email.title" = "Edit Email"; +"subscription.activate.edit.email.title" = "Изменить адрес почты"; /* Email option for activation */ -"subscription.activate.email" = "Email"; +"subscription.activate.email" = "Электронная почта"; /* Restore button title for Email */ -"subscription.activate.email.button" = "Enter Email"; +"subscription.activate.email.button" = "Ввести адрес эл. почты"; /* Description for Email activation */ -"subscription.activate.email.description" = "Use your email to activate your subscription on this device."; +"subscription.activate.email.description" = "Используйте адрес электронной почты, чтобы активировать подписку на этом устройстве."; /* Activate subscription title */ "subscription.activate.email.title" = "Активация подписки"; /* Button title for cancelling email deletion */ -"subscription.activate.manage.email.cancel" = "Cancel"; +"subscription.activate.manage.email.cancel" = "Отменить"; /* Button title for confirming email deletion */ -"subscription.activate.manage.email.OK" = "OK"; +"subscription.activate.manage.email.OK" = "Хорошо"; /* Restore button title for AppleID */ -"subscription.activate.restore.apple" = "Restore Purchase"; +"subscription.activate.restore.apple" = "Восстановить покупку"; /* Subscription Activation Title */ -"subscription.activate.title" = "Activate your subscription on this device"; +"subscription.activate.title" = "Активировать подписку на этом устройстве"; /* Resend activation instructions button */ -"subscription.add.device.resend.instructions" = "Resend Instructions"; +"subscription.add.device.resend.instructions" = "Отправить заново"; /* Add email to an existing subscription */ -"subscription.add.email" = "Add an email address to activate your subscription on your other devices. We’ll only use this address to verify your subscription."; +"subscription.add.email" = "Чтобы активировать подписку на других устройствах, добавьте адрес электронной почты. Он будет использоваться только для подтверждения подписки."; /* View title for adding email to subscription */ -"subscription.add.email.title" = "Add Email"; - -/* Title for Alert messages */ -"subscription.alert.title" = "subscription.alert.title"; +"subscription.add.email.title" = "Добавить адрес эл. почты"; /* Subscription availability message on Apple devices */ -"subscription.available.apple" = "Privacy Pro is available on any device signed in to the same Apple ID."; +"subscription.available.apple" = "Подписка Privacy Pro доступна на любом устройстве, где выполнен вход в ту же учетную запись Apple ID."; /* Text for the manage billing page */ "subscription.billing.google.text" = "Ваша подписка приобретена в магазине приложений Google Play Store. Чтобы продлить ее, откройте настройки подписок в Google Play Store на устройстве, где выполнен вход в ту же учетную запись Google, через которую была изначально оформлена подписка."; @@ -2385,89 +2550,86 @@ /* Title for the manage billing page */ "subscription.billing.google.title" = "Тарифные планы"; -/* Subscription annual billing period type */ -"subscription.billing.period.annual" = "annual"; - -/* Subscription monthly billing period type */ -"subscription.billing.period.monthly" = "monthly"; - /* Subscription Removal confirmation message */ -"subscription.cancel.message" = "Your subscription has been removed from this device."; +"subscription.cancel.message" = "Ваша подписка удалена с этого устройства."; /* Change plan or cancel title */ -"subscription.change.plan" = "Update Plan or Cancel"; +"subscription.change.plan" = "Изменить или отменить подписку"; /* Navigation Button for closing subscription view */ -"subscription.close" = "Close"; +"subscription.close" = "Закрыть"; /* Title for Confirm messages */ -"subscription.confirm.title" = "Are you sure?"; +"subscription.confirm.title" = "Вы уверены?"; /* Header for section for activating subscription on other devices */ -"subscription.devices.header" = "Activate on Other Devices"; +"subscription.devices.header" = "Активация подписки на других устройствах"; /* Footer for section for activating subscription on other devices when email was not yet added */ -"subscription.devices.no.email.footer" = "Add an optional email to your subscription or use your Apple ID to access Privacy Pro on other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.no.email.footer" = "Привяжите к подписке дополнительный адрес электронной почты, чтобы пользоваться преимуществами тарифа Privacy Pro на других устройствах. **[Подробнее...](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Footer for section for activating subscription on other devices when email is added */ -"subscription.devices.with.email.footer" = "Use this email to activate your subscription in Settings > Privacy Pro in the DuckDuckGo app on your other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.with.email.footer" = "Используйте этот адрес для активации подписки в разделе «Настройки > Privacy Pro» в приложении DuckDuckGo на других устройствах. **[Подробнее...](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Alert content for not found subscription */ -"subscription.email.inactive.alert.message" = "The subscription associated with this email is no longer active."; +"subscription.email.inactive.alert.message" = "Подписка, закрепленная за этой учетной записью, утратила активный статус."; /* Alert title for not found subscription */ -"subscription.email.inactive.alert.title" = "Subscription Not Found"; +"subscription.email.inactive.alert.title" = "Подписка не найдена"; /* Alert content for not found subscription */ -"subscription.expired.alert.message" = "The subscription associated with this Apple ID is no longer active."; +"subscription.expired.alert.message" = "Подписка, закрепленная за этой учетной записью Apple ID, утратила активный статус."; /* FAQ Button */ -"subscription.faq" = "FAQs and Support"; +"subscription.faq" = "ЧаВо и поддержка"; /* FAQ Description */ -"subscription.faq.description" = "Get answers to frequently asked questions or contact Privacy Pro support from our help pages."; +"subscription.faq.description" = "На страницах справочного раздела вы найдете ответы на часто задаваемые вопросы, а также контактную информацию службы поддержки Privacy Pro."; + +/* Send Feedback Button */ +"subscription.feedback" = "Отправить отзыв"; /* Cancel action for the existing subscription dialog */ -"subscription.found.cancel" = "Cancel"; +"subscription.found.cancel" = "Отменить"; /* Restore action for the existing subscription dialog */ -"subscription.found.restore" = "Restore"; +"subscription.found.restore" = "Восстановить"; /* Message for the existing subscription dialog */ -"subscription.found.text" = "We found a subscription associated with this Apple ID."; +"subscription.found.text" = "Мы нашли подписку, привязанную к этой учетной записи Apple ID."; /* Title for the existing subscription dialog */ -"subscription.found.title" = "Subscription Found"; +"subscription.found.title" = "Подписка найдена"; /* Help and support Section header */ -"subscription.help" = "Help and support"; +"subscription.help" = "Справка и поддержка"; /* Manage Plan header */ -"subscription.manage.plan" = "Manage Plan"; +"subscription.manage.plan" = "Управление тарифом"; /* Header for the subscription section */ -"subscription.manage.title" = "Subscription"; +"subscription.manage.title" = "По подписке"; /* Alert content for not found subscription */ -"subscription.notFound.alert.message" = "There is no subscription associated with this Apple ID."; +"subscription.notFound.alert.message" = "К этой учетной записи Apple ID не привязано ни одной подписки."; /* Alert title for not found subscription */ -"subscription.notFound.alert.title" = "Subscription Not Found"; +"subscription.notFound.alert.title" = "Подписка не найдена"; /* View plans button text */ -"subscription.notFound.view.plans" = "View Plans"; +"subscription.notFound.view.plans" = "Просмотреть тарифы"; /* Hero Text for Personal information removal */ -"subscription.pir.hero" = "Activate Privacy Pro on desktop to set up Personal Information Removal"; +"subscription.pir.hero" = "Чтобы включить функцию Personal Information Removal, активируйте подписку Privacy Pro на настольном компьютере"; /* Description on how to use Personal information removal in desktop. The first placeholder references a location in the Desktop application. Privacy Pro>, and the second, the menu entry. i.e. */ -"subscription.pir.heroText" = "In the DuckDuckGo browser for desktop, go to %1$@ and click %2$@ to get started."; +"subscription.pir.heroText" = "Для начала запустите настольную версию браузера DuckDuckGo, откройте раздел «%1$@» и нажмите «%2$@»."; /* Settings references a menu in the Desktop app, Privacy Pro, references our product name */ -"subscription.pir.heroTextLocation" = "Settings > Privacy Pro"; +"subscription.pir.heroTextLocation" = "Настройки > Privacy Pro"; /* Menu item for enabling Personal Information Removal on Desktop */ -"subscription.pir.heroTextMenyEntry" = "I have a subscription"; +"subscription.pir.heroTextMenyEntry" = "У меня уже есть подписка"; /* Text for the 'macOS' button */ "subscription.pir.macos" = "Mac"; @@ -2476,28 +2638,28 @@ "subscription.pir.windows" = "Windows"; /* Progress view title when completing the purchase */ -"subscription.progress.view.completing.purchase" = "Completing purchase..."; +"subscription.progress.view.completing.purchase" = "Завершение покупки..."; /* Progress view title when starting the purchase */ -"subscription.progress.view.purchasing.subscription" = "Purchase in progress..."; +"subscription.progress.view.purchasing.subscription" = "Оформление покупки..."; /* Progress view title when restoring past subscription purchase */ -"subscription.progress.view.restoring.subscription" = "Restoring subscription..."; +"subscription.progress.view.restoring.subscription" = "Восстановление подписки..."; /* Remove from this device button */ -"subscription.remove.from.device.button" = "Remove From This Device"; +"subscription.remove.from.device.button" = "Удалить с этого устройства"; /* Remove from device confirmation dialog text */ -"subscription.remove.from.device.text" = "You will no longer be able to access your Privacy Pro subscription on this device. This will not cancel your subscription, and it will remain active on your other devices."; +"subscription.remove.from.device.text" = "Вы утратите доступ к возможностям тарифа Privacy Pro на этом устройстве. Сама же подписка не отменяется и по-прежнему будет работать на других устройствах."; /* Remove from device confirmation dialog title */ -"subscription.remove.from.device.title" = "Remove from this device?"; +"subscription.remove.from.device.title" = "Удалить с этого устройства?"; /* Remove subscription button text */ -"subscription.remove.subscription" = "Remove Subscription"; +"subscription.remove.subscription" = "Удалить подписку"; /* Remove subscription cancel button text */ -"subscription.remove.subscription.cancel" = "Cancel"; +"subscription.remove.subscription.cancel" = "Отменить"; /* Button text for general error message */ "subscription.restore.backend.error.button" = "Назад к настройкам"; @@ -2509,28 +2671,43 @@ "subscription.restore.backend.error.title" = "Что-то пошло не так"; /* Alert for general error message */ -"subscription.restore.general.error.message" = "The App Store was unable to process your purchase. Please try again later."; +"subscription.restore.general.error.message" = "Сервису App Store не удалось обработать вашу покупку. Повторите попытку позже."; /* Alert for general error title */ -"subscription.restore.general.error.title" = "Something Went Wrong"; +"subscription.restore.general.error.title" = "Что-то пошло не так"; /* Alert button text for restored purchase alert */ -"subscription.restore.success.alert.button" = "OK"; +"subscription.restore.success.alert.button" = "Хорошо"; /* Alert message for restored purchase */ -"subscription.restore.success.alert.message" = "Your purchases have been restored."; +"subscription.restore.success.alert.message" = "Ваши покупки восстановлены."; /* Alert title for restored purchase */ -"subscription.restore.success.alert.title" = "You’re all set."; +"subscription.restore.success.alert.title" = "Готово!"; + +/* Subtitle in header when subscribed */ +"subscription.subscribed" = "Подписка оформлена"; /* Subscription Expired Data. This reads as 'Your subscription expired on (date)' */ "subscription.subscription.expired.caption" = "Срок действия вашей подписки истек %@"; -/* Subscription expiration info. This reads as 'Your (monthly or annual) subscription expires on (date)' */ -"subscription.subscription.expiring.caption" = "Your %1$@ subscription expires on %2$@."; +/* Monthly subscription expiration info where parameter is expiration date. This reads as 'Your monthly subscription expires on (date)' */ +"subscription.subscription.expiring.monthly.caption" = "Ваша ежемесячная подписка завершится %@."; + +/* Unknown period subscription expiration info where parameter is expiration date. This reads as 'Your subscription expires on (date)' */ +"subscription.subscription.expiring.unknown.caption" = "Ваша подписка завершится %@."; + +/* Annual subscription expiration info where parameter is expiration date. This reads as 'Your annual subscription expires on (date)' */ +"subscription.subscription.expiring.yearly.caption" = "Ваша годовая подписка завершится %@."; + +/* Monthly subscription renewal info where parameter is renewal date. This reads as 'Your monthly subscription renews on (date)' */ +"subscription.subscription.renewing.monthly.caption" = "Ваша ежемесячная подписка продлится %@."; + +/* Unknown period subscription renewal info where parameter is renewal date. This reads as 'Your subscription renews on (date)' */ +"subscription.subscription.renewing.unknown.caption" = "Ваша подписка продлится %@."; -/* Subscription renewal info. This reads as 'Your (monthly or annual) subscription renews on (date)' */ -"subscription.subscription.renewing.caption" = "Your %1$@ subscription renews on %2$@."; +/* Annual subscription renewal info where parameter is renewal date. This reads as 'Your annual subscription renews on (date)' */ +"subscription.subscription.renewing.yearly.caption" = "Ваша годовая подписка продлится %@."; /* Navigation bar Title for subscriptions */ "subscription.title" = "Privacy Pro"; @@ -2677,118 +2854,118 @@ "voiceSearch.footer.note.old" = "Аудио обрабатывается непосредственно на устройстве, не хранится и не передается никому, даже DuckDuckGo."; /* Cancel action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.cancel" = "Dismiss"; +"vpn.access-revoked.alert.action.cancel" = "Отклонить"; /* Primary action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.subscribe" = "Subscribe"; +"vpn.access-revoked.alert.action.subscribe" = "Подписаться"; /* Alert message for the alert when the Privacy Pro subscription expiress */ -"vpn.access-revoked.alert.message" = "Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"vpn.access-revoked.alert.message" = "Чтобы вновь подключиться к DuckDuckGo VPN, оформите подписку Privacy Pro."; /* Alert title for the alert when the Privacy Pro subscription expires */ -"vpn.access-revoked.alert.title" = "VPN disconnected due to expired subscription"; +"vpn.access-revoked.alert.title" = "VPN отключен ввиду окончания подписки"; /* Title for the VPN widget onboarding screen */ "vpn.addWidget.settings.title" = "Найдите и выберите DuckDuckGo. Затем с помощью свайпа перейдите к VPN и выберите опцию «Добавить виджет»."; /* Title for the Cancel button of the VPN feedback form */ -"vpn.feedback-form.button.cancel" = "Cancel"; +"vpn.feedback-form.button.cancel" = "Отменить"; /* Title for the Done button of the VPN feedback form */ -"vpn.feedback-form.button.done" = "Done"; +"vpn.feedback-form.button.done" = "Готово"; /* Title for the Submit button of the VPN feedback form */ -"vpn.feedback-form.button.submit" = "Submit"; +"vpn.feedback-form.button.submit" = "Отправить"; /* Title for the Submitting state of the VPN feedback form */ -"vpn.feedback-form.button.submitting" = "Submitting…"; +"vpn.feedback-form.button.submitting" = "Отправка..."; /* Title for the browser crash/freeze category of the VPN feedback form */ -"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN causes browser to crash or freeze"; +"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN приводит к сбою или зависанию браузера"; /* Title for the 'VPN fails to connect' category of the VPN feedback form */ -"vpn.feedback-form.category.fails-to-connect" = "VPN fails to connect"; +"vpn.feedback-form.category.fails-to-connect" = "VPN не подключается"; /* Title for the 'VPN feature request' category of the VPN feedback form */ -"vpn.feedback-form.category.feature-request" = "VPN feature request"; +"vpn.feedback-form.category.feature-request" = "Запрос функции VPN"; /* Title for the category 'VPN causes issues with other apps or websites' category of the VPN feedback form */ -"vpn.feedback-form.category.issues-with-apps" = "VPN causes issues with other apps or websites"; +"vpn.feedback-form.category.issues-with-apps" = "VPN вызывает проблемы с другими приложениями или сайтами"; /* Title for the local device connectivity category of the VPN feedback form */ -"vpn.feedback-form.category.local-device-connectivity" = "VPN won't let me connect to local device"; +"vpn.feedback-form.category.local-device-connectivity" = "VPN не дает подключиться к локальному устройству"; /* Title for the 'other VPN feedback' category of the VPN feedback form */ -"vpn.feedback-form.category.other" = "Other VPN feedback"; +"vpn.feedback-form.category.other" = "Другие отзывы о VPN"; /* Title for the category selection state of the VPN feedback form */ -"vpn.feedback-form.category.select-category" = "Select a category"; +"vpn.feedback-form.category.select-category" = "ВЫБЕРИТЕ КАТЕГОРИЮ"; /* Title for the 'VPN is too slow' category of the VPN feedback form */ -"vpn.feedback-form.category.too-slow" = "VPN connection is too slow"; +"vpn.feedback-form.category.too-slow" = "Слишком медленное соединение по VPN"; /* Title for the 'unable to install' category of the VPN feedback form */ -"vpn.feedback-form.category.unable-to-install" = "Unable to install VPN"; +"vpn.feedback-form.category.unable-to-install" = "Невозможно установить VPN"; /* Title for the feedback sent view description of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.description" = "Your feedback will help us improve the\nDuckDuckGo VPN."; +"vpn.feedback-form.sending-confirmation.description" = "Ваш отзыв поможет нам улучшить сервис DuckDuckGo VPN."; /* Title for the feedback sending error text of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.error" = "We couldn't send your feedback right now, please try again."; +"vpn.feedback-form.sending-confirmation.error" = "Отправить ваш отзыв прямо сейчас не удалось. Повторите попытку."; /* Title for the feedback sent view title of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.title" = "Thank you!"; +"vpn.feedback-form.sending-confirmation.title" = "Спасибо!"; /* Toast message when the VPN feedback form is submitted successfully */ -"vpn.feedback-form.submitted.message" = "Thank You! Feedback submitted."; +"vpn.feedback-form.submitted.message" = "Спасибо! Ваш отзыв отправлен."; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-1" = "Please describe what's happening, what you expected to happen, and the steps that led to the issue:"; +"vpn.feedback-form.text-1" = "Опишите, что происходит, что ожидалось и какие действия привели к неполадке."; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-2" = "In addition to the details entered into this form, your app issue report will contain:"; +"vpn.feedback-form.text-2" = "Помимо информации, внесенной в эту форму, в отчет войдет следующее:"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-3" = "• Whether specific DuckDuckGo features are enabled"; +"vpn.feedback-form.text-3" = "• сведения о том, были ли активированы конкретные функции DuckDuckGo;"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-4" = "• Aggregate DuckDuckGo app diagnostics"; +"vpn.feedback-form.text-4" = "• обобщенные результаты диагностики приложения DuckDuckGo."; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-5" = "By tapping \"Submit\" I agree that DuckDuckGo may use the information in this report for purposes of improving the app's features."; +"vpn.feedback-form.text-5" = "Нажимая кнопку «Отправить», я разрешаю DuckDuckGo использовать данные в этом отчете для улучшения работы приложения."; /* Title for each screen of the VPN feedback form */ -"vpn.feedback-form.title" = "Help Improve the DuckDuckGo VPN"; +"vpn.feedback-form.title" = "Помогите нам улучшить DuckDuckGo VPN"; /* VPN settings screen cell text for adding the VPN widget to the home screen */ "vpn.settings.add.widget" = "Добавить виджет VPN на главный экран"; /* Disclaimer for the DNS Server section on the DNS Server screen */ -"vpn.settings.dns.section-disclaimer" = "Using a custom DNS server can impact browsing speeds and expose your activity to third parties if the server isn't secure or reliable."; +"vpn.settings.dns.section-disclaimer" = "Использование собственного DNS-сервера может снизить скорость просмотра веб-страниц и обнаружить ваши действия для третьих лиц, если сервер плохо защищен или работает ненадежно."; /* Header text for the DNS section on the VPN Settings screen */ "vpn.settings.dns.section-header" = "DNS"; /* Title for the Apply button on the DNS Server setting screen */ -"vpn.settings.dns.server.apply.button.title" = "Apply"; +"vpn.settings.dns.server.apply.button.title" = "Применить"; /* Default value for the DNS Server row on the VPN Settings screen */ "vpn.settings.dns.server.default.value" = "DuckDuckGo"; /* Title for the IPv4 Address setting */ -"vpn.settings.dns.server.ipv4.title" = "IPv4 Address"; +"vpn.settings.dns.server.ipv4.title" = "IPv4-адрес"; /* Custom option for the DNS Server setting */ -"vpn.settings.dns.server.option.custom" = "Custom"; +"vpn.settings.dns.server.option.custom" = "Свой"; /* Recommended option for the DNS Server setting */ -"vpn.settings.dns.server.option.default" = "DuckDuckGo (Recommended)"; +"vpn.settings.dns.server.option.default" = "DuckDuckGo (рекомендуется)"; /* Title for the DNS Server setting screen */ -"vpn.settings.dns.server.screen.title" = "DNS Server"; +"vpn.settings.dns.server.screen.title" = "DNS-сервер"; /* Title for the DNS Server row on the VPN Settings screen */ -"vpn.settings.dns.server.title" = "DNS Server"; +"vpn.settings.dns.server.title" = "DNS-сервер"; /* Title for the button to enable push notifications in system settings */ "waitlist.allow-notifications" = "Разрешить уведомления"; diff --git a/DuckDuckGo/ru.lproj/OmniBar.strings b/DuckDuckGo/ru.lproj/OmniBar.strings index 81efa98a8e..526148eb12 100644 --- a/DuckDuckGo/ru.lproj/OmniBar.strings +++ b/DuckDuckGo/ru.lproj/OmniBar.strings @@ -7,6 +7,9 @@ /* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "Dt8-We-5tV"; */ "Dt8-We-5tV.accessibilityLabel" = "Удалить текст"; +/* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "gb5-wL-4OW"; */ +"gb5-wL-4OW.accessibilityLabel" = "Удалить текст"; + /* Class = "UIButton"; accessibilityLabel = "Share"; ObjectID = "hor-Jd-3kw"; */ "hor-Jd-3kw.accessibilityLabel" = "Поделиться"; diff --git a/DuckDuckGo/ru.lproj/Settings.strings b/DuckDuckGo/ru.lproj/Settings.strings index fa13ed39ac..85aae45f43 100644 --- a/DuckDuckGo/ru.lproj/Settings.strings +++ b/DuckDuckGo/ru.lproj/Settings.strings @@ -10,9 +10,6 @@ /* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "5YD-Od-uDU"; */ "5YD-Od-uDU.text" = "Автоудаление данных"; -/* Class = "UIButton"; normalTitle = "Settings"; ObjectID = "8YG-7Q-Czd"; */ -"8YG-7Q-Czd.normalTitle" = "Настройки"; - /* Class = "UILabel"; text = "App Exit, Inactive for 1 Hour"; ObjectID = "9EC-lx-77p"; */ "9EC-lx-77p.text" = "После выхода из приложения, через 1 час бездействия"; @@ -76,9 +73,6 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Незащищенные сайты"; -/* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ -"ssa-zd-L3T.title" = "Размер текста"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Показывать клавиатуру"; diff --git a/DuckDuckGo/sk.lproj/DaxOnboarding.strings b/DuckDuckGo/sk.lproj/DaxOnboarding.strings index 2ca1c109e7..c8e2760f07 100644 --- a/DuckDuckGo/sk.lproj/DaxOnboarding.strings +++ b/DuckDuckGo/sk.lproj/DaxOnboarding.strings @@ -1,12 +1,6 @@ /* Class = "UIButton"; normalTitle = "Hide"; ObjectID = "hgy-75-OvJ"; */ "hgy-75-OvJ.normalTitle" = "Skryť"; -/* Class = "UILabel"; text = "Welcome to\nDuckDuckGo!"; ObjectID = "Lvy-9G-3gr"; */ -"Lvy-9G-3gr.text" = "Vitajte v\nprehliadači DuckDuckGo!"; - -/* Class = "UIButton"; normalTitle = "Let’s Do It!"; ObjectID = "oAv-Zt-uIt"; */ -"oAv-Zt-uIt.normalTitle" = "Poďme na to!"; - /* Class = "UIButton"; normalTitle = "Button"; ObjectID = "zJt-F9-L48"; */ "zJt-F9-L48.normalTitle" = "Tlačidlo"; diff --git a/DuckDuckGo/sk.lproj/Localizable.strings b/DuckDuckGo/sk.lproj/Localizable.strings index 86e801f76e..e8cd577412 100644 --- a/DuckDuckGo/sk.lproj/Localizable.strings +++ b/DuckDuckGo/sk.lproj/Localizable.strings @@ -341,7 +341,7 @@ "autofill.keep-enabled.alert.disable" = "Zakázať"; /* A link that takes the user to the DuckDuckGo help pages explaining password managers */ -"autofill.learn.more.link.title" = "Learn More"; +"autofill.learn.more.link.title" = "Zistite viac"; /* Button displayed after saving/updating an autofill login that takes the user to the saved login */ "autofill.login-save-action-button.toast" = "Zobraziť"; @@ -977,9 +977,6 @@ /* ad = advertisment */ "dax.onboarding.home.subsequent" = "Máte to!\n\nPamätajte: zakaždým, keď surfujete na webe v našej aplikácii, príšerným reklamám pristrihávate krídla. 👍"; -/* No comment provided by engineer. */ -"dax.onboarding.message" = "Internet môže byť dosť nevyspytateľný.\n\nNemajte žiadne obavy! Súkromné vyhľadávanie a prehliadanie je jednoduchšie, ako si myslíte."; - /* No comment provided by engineer. */ "Debug" = "Ladenie (debug)"; @@ -1080,7 +1077,7 @@ "duckPlayer.never.label" = "Nikdy"; /* Body text for the modal feature explanation */ -"duckplayer.presentation.modal.body" = "Duck Player vám umožňuje v DuckDuckGo sledovať YouTube bez cielených reklám ako v kine a to, čo sledujete, nebude mať vplyv na vaše odporúčania."; +"duckplayer.presentation.modal.body" = "Prehrávač Duck Player vám umožňuje sledovať YouTube v prehliadači DuckDuckGo bez cielených reklám a to, čo sledujete, nebude mať vplyv na vaše odporúčania."; /* Button that will dismiss the modal */ "duckplayer.presentation.modal.dismiss-button" = "Rozumiem!"; @@ -1088,17 +1085,23 @@ /* Two line title (separated by \n) for the feature explanation */ "duckplayer.presentation.modal.title" = "Utápať sa v reklamách na YouTube? Vyskúšajte Duck Player!"; +/* Two line title (separated by \n) for the feature explanation */ +"duckplayer.presentation.modal.title.youtube" = "Utápať sa v reklamách na YouTube? Nie s Duck Player!"; + /* Footer label in the settings screen for Duck Player */ "duckplayer.settings.footer" = "DuckDuckGo poskytuje všetky Privacy Essentials, ktoré potrebujete na ochranu pri prehliadaní webu."; /* Text explaining what Duck Player is in the settings screen. */ -"duckplayer.settings.info-text" = "Duck Player poskytuje čisté zobrazenie bez personalizovaných reklám a zabraňuje tomu, aby aktivita pri sledovaní ovplyvňovala vaše odporúčania v službe YouTube."; +"duckplayer.settings.info-text" = "Prehrávač Duck Player vám umožňuje sledovať YouTube v prehliadači DuckDuckGo bez cielených reklám a to, čo sledujete, nebude mať vplyv na vaše odporúčania."; /* Button that takes the user to learn more about Duck Player. */ "duckplayer.settings.learn-more" = "Zistite viac"; +/* Settings screen cell text for DuckPlayer settings to open in new tab */ +"duckplayer.settings.open-new-tab-label" = "Otvoriť Duck Player na novej karte"; + /* Settings screen cell text for DuckPlayer settings */ -"duckplayer.settings.open-videos-in" = "Otvorenie videí v programe Duck Player"; +"duckplayer.settings.open-videos-in" = "Otvárať YouTube videá s Duck Player?"; /* Settings screen cell text for DuckPlayer settings */ "duckplayer.settings.title" = "Duck Player"; @@ -1169,9 +1172,6 @@ /* Title for prompt to sign up for email protection */ "email.signup-prompt.title" = "Skryte svoj e-mail\na blokujte sledovače"; -/* No comment provided by engineer. */ -"empty!" = "prázdne!"; - /* Empty list state placholder */ "empty.bookmarks" = "Zatiaľ neboli pridané žiadne záložky"; @@ -1322,6 +1322,39 @@ /* Confirmation button */ "feedback.form.submit" = "Potvrdiť"; +/* Title of the feedback form */ +"feedback.form.title" = "Pomôž zlepšiť ochranu osobných údajov Pro"; + +/* Category for Identity Theft Restoration feedback */ +"feedback.general.category.itr" = "Identity Theft Restoration"; + +/* Category for Personal Info Removal feedback */ +"feedback.general.category.pir" = "Odstránenie osobných informácií"; + +/* Category for subscription and payments feedback */ +"feedback.general.category.ppro" = "Predplatné a platby"; + +/* Prompt to select a category for general feedback */ +"feedback.general.category.select" = "VYBERTE KATEGÓRIU"; + +/* Category for VPN feedback */ +"feedback.general.category.vpn" = "VPN"; + +/* Category for access code issues */ +"feedback.itr.category.access-code" = "Problém s prístupovým kódom"; + +/* Category for issues contacting an advisor */ +"feedback.itr.category.cant-contact-advisor" = "Nemôžem sa spojiť s poradcom."; + +/* Prompt to select a category for Identity Theft Restoration feedback */ +"feedback.itr.category.select" = "VYBERTE KATEGÓRIU"; + +/* Category for other Identity Theft Restoration issues */ +"feedback.itr.category.something-else" = "Niečo iné"; + +/* Category for unhelpful advisor calls */ +"feedback.itr.category.unhelpful" = "Telefonát s poradcom nebol užitočný"; + /* No comment provided by engineer. */ "feedback.negative.form.genericPlaceholder" = "Buďte čo najkonkrétnejší"; @@ -1361,6 +1394,24 @@ /* No comment provided by engineer. */ "feedback.performance.slowLoading" = "Pomalé načítavanie webových stránok alebo výsledkov vyhľadávania"; +/* Category for when scan finds incorrect records */ +"feedback.pir.category.not-me" = "Skenovanie našlo záznamy, ktoré sa ma netýkajú"; + +/* Category for when scan doesn't find info on a specific site */ +"feedback.pir.category.nothing-on-site" = "Skenovanie nenašlo moje informácie na konkrétnej stránke"; + +/* Category for other Personal Info Removal issues */ +"feedback.pir.category.other" = "Niečo iné"; + +/* Category for when the removal process is stuck */ +"feedback.pir.category.removal-stuck" = "Proces odstránenia je zaseknutý"; + +/* Category for when the scan is stuck */ +"feedback.pir.category.scan-stuck" = "Skenovanie záznamov je zaseknuté"; + +/* Prompt to select a category for Personal Info Removal feedback */ +"feedback.pir.category.select" = "VYBERTE KATEGÓRIU"; + /* Header above input field */ "feedback.positive.form.header" = "Povedzte nám viac detailov"; @@ -1379,6 +1430,15 @@ /* Button encouraging uses to share details aboout their feedback */ "feedback.positive.submit" = "Povedzte nám viac detailov"; +/* Category for other Privacy Pro issues */ +"feedback.ppro.category.other" = "Niečo iné"; + +/* Category for one-time password issues */ +"feedback.ppro.category.otp" = "Problém s jednorazovým heslom"; + +/* Prompt to select a category for Privacy Pro feedback */ +"feedback.ppro.category.select" = "VYBERTE KATEGÓRIU"; + /* No comment provided by engineer. */ "feedback.start.footer" = "Anonymná spätná väzba je pre nás dôležitá."; @@ -1529,12 +1589,6 @@ /* No comment provided by engineer. */ "keyCommandShowAllTabs" = "Zobraziť všetky karty"; -/* Please preserve newline character */ -"launchscreenWelcomeMessage" = "Vitajte v\nprehliadači DuckDuckGo!"; - -/* No comment provided by engineer. */ -"LOREM IPSUM" = "LOREM IPSUM"; - /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "DuckDuckGo pre Mac má rýchlosť, ktorú potrebujete, funkcie prehliadania, ktoré očakávate, a obsahuje naše prvotriedne privacy essentials."; @@ -1586,62 +1640,83 @@ /* Edit button */ "navigation.title.edit" = "Upraviť"; -/* String indicating NetP is connected when viewed from the settings screen */ -"netP.cell.connected" = "Connected"; - -/* String indicating NetP is disconnected when viewed from the settings screen */ -"netP.cell.disconnected" = "Not connected"; - -/* Title for the DuckDuckGo VPN feature in settings */ -"netP.settings.title" = "VPN"; +/* String indicating NetP is snoozing when viewed from the settings screen */ +"netP.cell.snoozing" = "Pozastavenie"; /* Title for the DuckDuckGo VPN feature */ "netP.title" = "DuckDuckGo VPN"; +/* Button title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.action" = "Pridať miniaplikáciu"; + +/* Message for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.message" = "Zapnite a vypnite sieť VPN priamo z úvodnej obrazovky."; + +/* Title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.title" = "Pridať miniaplikáciu VPN"; + /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN bola odpojená kvôli vypršaniu platnosti predplatného. Predplať si Privacy Pro, aby si znova pripojil/a DuckDuckGo VPN."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Ochrana siete sa nepripojila. Prosím, skúste to neskôr znova."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "DuckDuckGo VPN sa nepodarilo pripojiť. Skús to neskôr znova, prosím."; /* Title for the VPN FAQ screen. */ "network.protection.faq.title" = "DuckDuckGo VPN - často kladené otázky"; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Ochrana siete bola prerušená. Prebieha pokus o opätovné pripojenie..."; +/* Message for tooltip about geoswitching */ +"network.protection.geoswitching.tip.message" = "Svoju polohu VPN môžete prispôsobiť pripojením k ktorémukoľvek z našich serverov na celom svete."; + +/* Title for tooltip about geoswitching */ +"network.protection.geoswitching.tip.title" = "Zmeniť vašu polohu"; + +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "DuckDuckGo VPN bola prerušená. Prebieha pokus o opätovné pripojenie..."; /* Message for the network protection invite dialog */ -"network.protection.invite.dialog.message" = "Enter your invite code to get started."; +"network.protection.invite.dialog.message" = "Začni zadaním pozývacieho kódu."; /* Title for the network protection invite screen */ "network.protection.invite.dialog.title" = "Pozývame vás vyskúšať DuckDuckGo VPN"; /* Prompt for the network protection invite code text field */ -"network.protection.invite.field.prompt" = "Invite Code"; +"network.protection.invite.field.prompt" = "Pozývací kód"; /* Message for the network protection invite success view */ -"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; +"network.protection.invite.success.message" = "Skry svoju polohu pred webovými stránkami a svoju online aktivitu pred poskytovateľmi internetu a ostatnými v tvojej sieti."; /* Title for the network protection invite success view */ -"network.protection.invite.success.title" = "Success! You’re in."; +"network.protection.invite.success.title" = "Podarilo sa to! Si tam."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; /* Title text for an iOS quick action that opens VPN settings */ "network.protection.quick-action.open-vpn" = "Otvoriť VPN sieť"; +/* Button title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.action" = "Zistite viac"; + +/* Message for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.message" = "Môžete použiť stránky alebo aplikácie blokujúce prevádzku VPN tak, že prerušíte pripojenie VPN."; + +/* Title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.title" = "Vyhýbanie sa konfliktom VPN"; + +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN je pozastavená na %@"; + /* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ "network.protection.status.connected.format" = "Pripojené · %@"; /* The label for the NetP VPN when connecting */ -"network.protection.status.connecting" = "Connecting..."; +"network.protection.status.connecting" = "Pripája sa..."; /* The label for the NetP VPN when disconnected */ -"network.protection.status.disconnected" = "Not connected"; +"network.protection.status.disconnected" = "Nie je pripojený"; /* The label for the NetP VPN when disconnecting */ -"network.protection.status.disconnecting" = "Disconnecting..."; +"network.protection.status.disconnecting" = "Odpájanie..."; /* Message label text for the status view when VPN is disconnected */ "network.protection.status.header.message.off" = "Pripojte sa, aby ste zabezpečili všetky svoje zariadenia\n Internetový prenos."; @@ -1655,26 +1730,41 @@ /* Header title label text for the status view when VPN is connected */ "network.protection.status.header.title.on" = "DuckDuckGo VPN je zapnutá"; +/* Header title label text for the status view when VPN is snoozing */ +"network.protection.status.header.title.snoozed" = "DuckDuckGo VPN je pozastavená"; + /* The status view 'Share Feedback' button which is shown inline on the status view after the temporary free use footer text */ -"network.protection.status.menu.share.feedback" = "Share Feedback"; +"network.protection.status.menu.share.feedback" = "Zdieľať spätnú väzbu"; + +/* The label for the NetP VPN when paused */ +"network.protection.status.paused" = "Pozastavené"; + +/* The label for when NetP VPN is snoozing plus the length of time remaining formatted as '0:00' */ +"network.protection.status.snoozing.format" = "Odkladám, %@ zostáva"; + +/* Snooze button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.snooze" = "Odložiť na 20 minút"; + +/* Wake Up button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.wake-up" = "Prebuď sa"; /* Connection details label shown in NetworkProtection's status view. */ -"network.protection.status.view.connection.details" = "Connection Details"; +"network.protection.status.view.connection.details" = "Podrobnosti pripojenia"; /* Custom DNS label shown in NetworkProtection's status view. */ -"network.protection.status.view.custom.dns" = "DNS Server"; +"network.protection.status.view.custom.dns" = "DNS server"; /* Generic connection failed error message shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.message" = "Please try again later."; +"network.protection.status.view.error.connection.failed.message" = "Prosím, skúste to neskôr znova."; /* Generic connection failed error title shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; +"network.protection.status.view.error.connection.failed.title" = "Nepodarilo sa pripojiť."; /* IP Address label shown in NetworkProtection's status view. */ -"network.protection.status.view.ip.address" = "IP Address"; +"network.protection.status.view.ip.address" = "IP adresa"; /* Location label shown in NetworkProtection's status view. */ -"network.protection.status.view.location" = "Location"; +"network.protection.status.view.location" = "Poloha"; /* Label shown on the title of the settings section in NetworkProtection's status view. */ "network.protection.status.view.settings.section.title" = "Spravujte stránku"; @@ -1682,12 +1772,15 @@ /* Title label text for the status view when netP is disconnected */ "network.protection.status.view.title" = "VPN"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Ochrana siete je zapnutá. Vaša poloha a online aktivita sú chránené."; +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "DuckDuckGo VPN je zapnutá. Tvoja poloha a online aktivita sú chránené."; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Smerovanie komunikácie zariadenia cez %@."; +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "Odloženie VPN sa skončilo. Smerovanie prevádzky zariadenia cez %@."; + /* Title for the button to link to the iOS app settings and enable notifications app-wide. */ "network.protection.turn.on.notifications.button.title" = "Zapnúť oznámenia"; @@ -1698,13 +1791,13 @@ "network.protection.vpn.about" = "O nás"; /* Section header for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.section.header" = "Notifications"; +"network.protection.vpn.alerts.section.header" = "Oznámenia"; /* List section footer for the toggle for VPN alerts. */ "network.protection.vpn.alerts.toggle.section.footer" = "Dostávajte upozornenia, ak dôjde k prerušeniu pripojenia alebo sa zmení stav VPN siete."; /* Title for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.toggle.title" = "VPN Notifications"; +"network.protection.vpn.alerts.toggle.title" = "Oznámenia VPN"; /* Title for the data volume section in the VPN status screen */ "network.protection.vpn.data-volume" = "Dátový objem"; @@ -1713,13 +1806,13 @@ "network.protection.vpn.exclude.local.networks.setting.footer" = "Umožnite miestnej prevádzke obísť sieť VPN sieť a pripojiť sa k zariadeniam v miestnej sieti, napríklad k tlačiarni."; /* Header text for the Exclude Local Networks setting item. */ -"network.protection.vpn.exclude.local.networks.setting.header" = "General"; +"network.protection.vpn.exclude.local.networks.setting.header" = "Všeobecné"; /* Title for the Exclude Local Networks setting item. */ "network.protection.vpn.exclude.local.networks.setting.title" = "Vylúčiť miestne siete"; /* Title for the VPN Location screen's All Countries section. */ -"network.protection.vpn.location.all.countries.section.title" = "All Countries"; +"network.protection.vpn.location.all.countries.section.title" = "Všetky krajiny"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.connected" = "Pripojené umiestnenie"; @@ -1734,7 +1827,7 @@ "network.protection.vpn.location.recommended.section.footer" = "Automaticky sa pripojí k najbližšiemu serveru, ktorý nájdeme."; /* Title for the VPN Location screen's Recommended section. */ -"network.protection.vpn.location.recommended.section.title" = "Recommended"; +"network.protection.vpn.location.recommended.section.title" = "Odporúčané"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.selected" = "Vybratá poloha"; @@ -1752,13 +1845,13 @@ "network.protection.vpn.preferred.location.nearest" = "Najbližšie umiestnenie"; /* Title for the Preferred Location VPN Settings item. */ -"network.protection.vpn.preferred.location.title" = "Preferred Location"; +"network.protection.vpn.preferred.location.title" = "Preferovaná lokalita"; /* Footer text for the DNS server setting item. */ -"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo routes DNS queries through our DNS servers so your internet provider can't see what websites you visit."; +"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo smeruje dopyty DNS cez naše servery DNS, aby tvoj poskytovateľ internetu nemohol vidieť, ktoré webové stránky navštevuješ."; /* Title for the FAQ row in the VPN status screen. */ -"network.protection.vpn.settings.faq" = "FAQs and Support"; +"network.protection.vpn.settings.faq" = "Často kladené otázky a podpora"; /* Title for the feedback row in the VPN status screen. */ "network.protection.vpn.settings.share-feedback" = "Zdieľať spätnú väzbu k VPN"; @@ -1940,6 +2033,66 @@ /* Deactivate button */ "pm.deactivate" = "Reaktivovať"; +/* Label for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.label" = "E-mail (nepovinné)"; + +/* Placeholder for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.placeholder" = "meno@email.com"; + +/* Placeholder for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.placeholder" = "Prosím, daj nám svoju spätnú väzbu..."; + +/* Title for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.title" = "Všeobecná spätná väzba"; + +/* Title for the ITR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-itr-problem.title" = "Identity Theft Restoration"; + +/* Title for the PIR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-pir-problem.title" = "Personal Information Removal"; + +/* Title for the Subscriptions and Payments category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-ppro-problem.title" = "Predplatné a platby"; + +/* Placeholder for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.placeholder" = "Povedz nám, čo sa deje..."; + +/* Title for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.title" = "Nahlásiť problém"; + +/* Title for the VPN category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-vpn-problem.title" = "VPN"; + +/* Placeholder for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.placeholder" = "Akú funkciu by si chcel/-a?"; + +/* Title for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.title" = "Požiadavka na funkciu"; + +/* Title for the category selection section in the Privacy Pro feedback form */ +"ppro.feedback-form.select-category.title" = "VYBERTE KATEGÓRIU"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-1" = "Našiel/našla si problém, ktorý nie je pokrytý v našom [centre pomoci](duck://)? Určite o tom chceme vedieť.\n\nUveď e-mail, ak chceš, aby sme ťa ohľadom tohto problému kontaktovali (nemusíme byť schopní odpovedať na všetky problémy):"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-2" = "Okrem vyššie uvedených údajov posielame spolu s tvojou spätnou väzbou aj anonymizované informácie:"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-3" = "• či sú niektoré funkcie prehliadača aktívne"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-4" = "• agregované diagnostické údaje aplikácií (napr. chybové kódy)"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-5" = "Klepnutím na „Odoslať“ súhlasíš s tým, že DuckDuckGo môže použiť poskytnuté informácie na zlepšenie aplikácie."; + +/* Title for the text box in the Privacy Pro feedback form */ +"ppro.feedback-form.text-box.title" = "Spätná väzba"; + +/* Title for the Privacy Pro feedback form */ +"ppro.feedback-form.title" = "Odoslať spätnú väzbu"; + /* Button title for sync bookmarks limits exceeded warning to go to manage bookmarks */ "prefrences.sync.bookmarks-limit-exceeded-action" = "Správa záložiek"; @@ -2029,6 +2182,18 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Obľúbené položky"; +/* Name of the option the user can chose to give general browser feedback */ +"send.browser.feedback.general-feedback" = "Všeobecná spätná väzba"; + +/* Name of the option the user can chose to give browser feedback about a problem they enountered */ +"send.browser.feedback.report-problem" = "Nahlásiť problém"; + +/* Name of the option the user can chose to give browser feedback about a feature they would like */ +"send.browser.feedback.request-feature" = "Požiadaj o funkciu"; + +/* Title of the picker where the user can chose the category of the feedback they want ot send. */ +"send.browser.feedback.select-category" = "VYBERTE KATEGÓRIU"; + /* Settings cell for About DDG */ "settings.about.ddg" = "O službe DuckDuckGo"; @@ -2084,6 +2249,9 @@ /* Section footer Autolock description */ "settings.autolock.description" = "Ak je povolená funkcia Touch ID, Face ID alebo systémový prístupový kód, pri otvorení aplikácie sa zobrazí výzva na odomknutie."; +/* Settings cell for Browser Feedback */ +"settings.browser.feedback" = "Spätná väzba prehliadača"; + /* Settings screen cell text for Automatically Clearing Data */ "settings.clear.data" = "Automaticky vymazať údaje"; @@ -2179,10 +2347,10 @@ "settings.on" = "Zapnuté"; /* Product name for the subscription bundle */ -"settings.ppro" = "Privacy Pro"; +"settings.ppro" = "Ochrana osobných údajov Pro"; /* Title for Link in the Footer of Privacy Pro section */ -"settings.ppro.footer" = "Privacy Policy and Terms of Service"; +"settings.ppro.footer" = "Zásady ochrany osobných údajov a podmienky služby"; /* Settings screen cell for long press previews */ "settings.previews" = "Náhľady po dlhodobom stlačení"; @@ -2208,23 +2376,20 @@ /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Nastavenie polohy panela s adresou"; -/* Subscription activation pending description */ -"settings.subscription.activation.pending.description" = "This is taking longer than usual, please check back later."; +/* Privacy pro description subtitle in settings when the is activating */ +"settings.subscription.activating" = "Aktivácia"; -/* Subscription activation pending title */ -"settings.subscription.activation.pending.title" = "Vaše predplatné sa aktivuje"; +/* Subscription activation pending description */ +"settings.subscription.activation.pending.description" = "Trvá to dlhšie ako zvyčajne, prosím, skús to znova neskôr."; /* Data Broker protection cell subtitle for privacy pro */ -"settings.subscription.DBP.subtitle" = "Remove your info from sites that sell it"; +"settings.subscription.DBP.subtitle" = "Odstráň svoje informácie zo stránok, ktoré ich predávajú"; /* Data Broker protection cell title for privacy pro */ "settings.subscription.DBP.title" = "Personal Information Removal"; -/* Privacy pro description subtext */ -"settings.subscription.description" = "Bezproblémové súkromie s tromi novými ochranami:"; - /* I have a Subscription button text for privacy pro */ -"settings.subscription.existing.subscription" = "I Have a Subscription"; +"settings.subscription.existing.subscription" = "Mám predplatné"; /* Subscription expired description */ "settings.subscription.expired.comment" = "Znova sa prihláste na odber, aby ste mohli naďalej používať Privacy Pro"; @@ -2232,23 +2397,26 @@ /* Subscription expired tittle message */ "settings.subscription.expired.title" = "Vaše predplatné Privacy Pro vypršalo"; -/* Privacy pro features list */ -"settings.subscription.features" = " • VPN\n • Personal Information Removal\n • Identity Theft Restoration"; - /* Identity theft restoration cell subtitle for privacy pro */ -"settings.subscription.ITR.subtitle" = "If your identity is stolen, we'll help restore it"; +"settings.subscription.ITR.subtitle" = "Ak je tvoja identita odcudzená, pomôžeme ti ju obnoviť."; /* Identity theft restoration cell title for privacy pro */ "settings.subscription.ITR.title" = "Identity Theft Restoration"; /* Get Privacy Pro button text for privacy pro */ -"settings.subscription.learn.more" = "Get Privacy Pro"; +"settings.subscription.learn.more" = "Získaj Privacy Pro"; /* Subscription Settings button text for privacy pro */ -"settings.subscription.manage" = "Subscription Settings"; +"settings.subscription.manage" = "Nastavenia predplatného"; + +/* Privacy Pro description subtitle in settings */ +"settings.subscription.row.description" = "Zahŕňa našu VPN a Identity Theft Restoration."; -/* Call to action title for Privacy Pro */ -"settings.subscription.subscribe" = "Subscribe to Privacy Pro"; +/* Call to action title for Privacy Pro settings */ +"settings.subscription.subscribe" = "Chráň si pripojenie a totožnosť pomocou Privacy Pro"; + +/* Privacy pro description subtitle in settings */ +"settings.subscription.us.description" = "Zahŕňa naše služby VPN, Personal Information Removal a Identity Theft Restoration."; /* VPN cell title for privacy pro */ "settings.subscription.VPN.title" = "VPN"; @@ -2317,67 +2485,64 @@ "siteFeedback.urlPlaceholder" = "Ktorá webová stránka je nefunkčná?"; /* Subscription Activation Info */ -"subscription.activate..header.description" = "Access your Privacy Pro subscription on this device via Apple ID or an email address."; +"subscription.activate..header.description" = "Získaj prístup k svojmu predplatnému Privacy Pro na tomto zariadení cez Apple ID alebo e-mailovú adresu."; /* Button for adding email address to subscription */ -"subscription.activate.add.email.button" = "Add Email"; +"subscription.activate.add.email.button" = "Pridať e-mail"; /* Apple ID option for activation */ "subscription.activate.appleid" = "Apple ID"; /* Button text for restoring purchase via Apple ID */ -"subscription.activate.appleid.button" = "Restore Purchase"; +"subscription.activate.appleid.button" = "Obnoviť nákup"; /* Description for Apple ID activation */ -"subscription.activate.appleid.description" = "Restore your purchase to activate your subscription on this device."; +"subscription.activate.appleid.description" = "Obnov svoj nákup, aby si aktivoval svoje predplatné na tomto zariadení."; /* Subscription Activation Info */ -"subscription.activate.description" = "Your subscription is automatically available in DuckDuckGo on any device signed in to your Apple ID."; +"subscription.activate.description" = "Tvoje predplatné je automaticky k dispozícii v službe DuckDuckGo na akomkoľvek zariadení prihlásenom do tvojho Apple ID."; /* Button for editing email address added to subscription */ -"subscription.activate.edit.email.button" = "Edit Email"; +"subscription.activate.edit.email.button" = "Upraviť e-mail"; /* View Title for editing your email account */ -"subscription.activate.edit.email.title" = "Edit Email"; +"subscription.activate.edit.email.title" = "Upraviť e-mail"; /* Email option for activation */ -"subscription.activate.email" = "Email"; +"subscription.activate.email" = "E-mail"; /* Restore button title for Email */ -"subscription.activate.email.button" = "Enter Email"; +"subscription.activate.email.button" = "Zadaj e-mail"; /* Description for Email activation */ -"subscription.activate.email.description" = "Use your email to activate your subscription on this device."; +"subscription.activate.email.description" = "Na aktiváciu svojho predplatného na tomto zariadení použi svoj e-mail."; /* Activate subscription title */ "subscription.activate.email.title" = "Aktivovať predplatné"; /* Button title for cancelling email deletion */ -"subscription.activate.manage.email.cancel" = "Cancel"; +"subscription.activate.manage.email.cancel" = "Zrušiť"; /* Button title for confirming email deletion */ "subscription.activate.manage.email.OK" = "OK"; /* Restore button title for AppleID */ -"subscription.activate.restore.apple" = "Restore Purchase"; +"subscription.activate.restore.apple" = "Obnoviť nákup"; /* Subscription Activation Title */ -"subscription.activate.title" = "Activate your subscription on this device"; +"subscription.activate.title" = "Aktivuj svoje predplatné na tomto zariadení"; /* Resend activation instructions button */ -"subscription.add.device.resend.instructions" = "Resend Instructions"; +"subscription.add.device.resend.instructions" = "Pokyny na opätovné odoslanie"; /* Add email to an existing subscription */ -"subscription.add.email" = "Add an email address to activate your subscription on your other devices. We’ll only use this address to verify your subscription."; +"subscription.add.email" = "Pridaj e-mailovú adresu na aktiváciu svojho predplatného v ďalších zariadeniach. Túto adresu použijeme iba na overenie tvojho predplatného."; /* View title for adding email to subscription */ -"subscription.add.email.title" = "Add Email"; - -/* Title for Alert messages */ -"subscription.alert.title" = "subscription.alert.title"; +"subscription.add.email.title" = "Pridať e-mail"; /* Subscription availability message on Apple devices */ -"subscription.available.apple" = "Privacy Pro is available on any device signed in to the same Apple ID."; +"subscription.available.apple" = "Privacy Pro je dostupný na akomkoľvek zariadení prihlásenom do rovnakého Apple ID."; /* Text for the manage billing page */ "subscription.billing.google.text" = "Vaše predplatné bolo zakúpené prostredníctvom obchodu Google Play. Ak chcete obnoviť predplatné, otvorte nastavenia predplatného v obchode Google Play v zariadení prihlásenom do rovnakého účtu Google, ktorý ste pôvodne použili na zakúpenie predplatného."; @@ -2385,89 +2550,86 @@ /* Title for the manage billing page */ "subscription.billing.google.title" = "Plány predplatného"; -/* Subscription annual billing period type */ -"subscription.billing.period.annual" = "annual"; - -/* Subscription monthly billing period type */ -"subscription.billing.period.monthly" = "monthly"; - /* Subscription Removal confirmation message */ -"subscription.cancel.message" = "Your subscription has been removed from this device."; +"subscription.cancel.message" = "Tvoje predplatné bolo z tohto zariadenia odstránené."; /* Change plan or cancel title */ -"subscription.change.plan" = "Update Plan or Cancel"; +"subscription.change.plan" = "Aktualizovať plán alebo zrušiť"; /* Navigation Button for closing subscription view */ -"subscription.close" = "Close"; +"subscription.close" = "Zatvoriť"; /* Title for Confirm messages */ -"subscription.confirm.title" = "Are you sure?"; +"subscription.confirm.title" = "Si si istý/-á?"; /* Header for section for activating subscription on other devices */ -"subscription.devices.header" = "Activate on Other Devices"; +"subscription.devices.header" = "Aktivuj na iných zariadeniach"; /* Footer for section for activating subscription on other devices when email was not yet added */ -"subscription.devices.no.email.footer" = "Add an optional email to your subscription or use your Apple ID to access Privacy Pro on other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.no.email.footer" = "Pridaj k svojmu predplatnému voliteľný e-mail, aby si mal/-a prístup k službe Privacy Pro na iných zariadeniach. **[Viac informácií](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Footer for section for activating subscription on other devices when email is added */ -"subscription.devices.with.email.footer" = "Use this email to activate your subscription in Settings > Privacy Pro in the DuckDuckGo app on your other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.with.email.footer" = "Tento e-mail použi na aktiváciu predplatného v Nastaveniach > Privacy Pro v aplikácii DuckDuckGo na iných zariadeniach. **[Viac informácií](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Alert content for not found subscription */ -"subscription.email.inactive.alert.message" = "The subscription associated with this email is no longer active."; +"subscription.email.inactive.alert.message" = "Predplatné spojené s týmto e-mailom už nie je aktívne."; /* Alert title for not found subscription */ -"subscription.email.inactive.alert.title" = "Subscription Not Found"; +"subscription.email.inactive.alert.title" = "Predplatné nebolo nájdené"; /* Alert content for not found subscription */ -"subscription.expired.alert.message" = "The subscription associated with this Apple ID is no longer active."; +"subscription.expired.alert.message" = "Predplatné spojené s týmto Apple ID už nie je aktívne."; /* FAQ Button */ -"subscription.faq" = "FAQs and Support"; +"subscription.faq" = "Často kladené otázky a podpora"; /* FAQ Description */ -"subscription.faq.description" = "Get answers to frequently asked questions or contact Privacy Pro support from our help pages."; +"subscription.faq.description" = "Získaj odpovede na často kladené otázky alebo kontaktuj podporu programu Privacy Pro na našich stránkach pomoci."; + +/* Send Feedback Button */ +"subscription.feedback" = "Odoslať spätnú väzbu"; /* Cancel action for the existing subscription dialog */ -"subscription.found.cancel" = "Cancel"; +"subscription.found.cancel" = "Zrušiť"; /* Restore action for the existing subscription dialog */ -"subscription.found.restore" = "Restore"; +"subscription.found.restore" = "Obnovenie"; /* Message for the existing subscription dialog */ -"subscription.found.text" = "We found a subscription associated with this Apple ID."; +"subscription.found.text" = "Našli sme predplatné spojené s týmto Apple ID."; /* Title for the existing subscription dialog */ -"subscription.found.title" = "Subscription Found"; +"subscription.found.title" = "Predplatné bolo nájdené"; /* Help and support Section header */ -"subscription.help" = "Help and support"; +"subscription.help" = "Pomoc a podpora"; /* Manage Plan header */ -"subscription.manage.plan" = "Manage Plan"; +"subscription.manage.plan" = "Spravovať plán"; /* Header for the subscription section */ -"subscription.manage.title" = "Subscription"; +"subscription.manage.title" = "Predplatné"; /* Alert content for not found subscription */ -"subscription.notFound.alert.message" = "There is no subscription associated with this Apple ID."; +"subscription.notFound.alert.message" = "S týmto Apple ID nie je spojené žiadne predplatné."; /* Alert title for not found subscription */ -"subscription.notFound.alert.title" = "Subscription Not Found"; +"subscription.notFound.alert.title" = "Predplatné nebolo nájdené"; /* View plans button text */ -"subscription.notFound.view.plans" = "View Plans"; +"subscription.notFound.view.plans" = "Zobraziť plány"; /* Hero Text for Personal information removal */ -"subscription.pir.hero" = "Activate Privacy Pro on desktop to set up Personal Information Removal"; +"subscription.pir.hero" = "Aktivuj si Privacy Pro na pracovnej ploche a nastav si Personal Information Removal"; /* Description on how to use Personal information removal in desktop. The first placeholder references a location in the Desktop application. Privacy Pro>, and the second, the menu entry. i.e. */ -"subscription.pir.heroText" = "In the DuckDuckGo browser for desktop, go to %1$@ and click %2$@ to get started."; +"subscription.pir.heroText" = "V prehliadači DuckDuckGo pre počítače prejdi na %1$@ a pre začiatok klikni na %2$@."; /* Settings references a menu in the Desktop app, Privacy Pro, references our product name */ -"subscription.pir.heroTextLocation" = "Settings > Privacy Pro"; +"subscription.pir.heroTextLocation" = "Nastavenia > Privacy Pro"; /* Menu item for enabling Personal Information Removal on Desktop */ -"subscription.pir.heroTextMenyEntry" = "I have a subscription"; +"subscription.pir.heroTextMenyEntry" = "Mám predplatné"; /* Text for the 'macOS' button */ "subscription.pir.macos" = "Mac"; @@ -2476,28 +2638,28 @@ "subscription.pir.windows" = "Windows"; /* Progress view title when completing the purchase */ -"subscription.progress.view.completing.purchase" = "Completing purchase..."; +"subscription.progress.view.completing.purchase" = "Dokončujem nákup..."; /* Progress view title when starting the purchase */ -"subscription.progress.view.purchasing.subscription" = "Purchase in progress..."; +"subscription.progress.view.purchasing.subscription" = "Prebieha nákup..."; /* Progress view title when restoring past subscription purchase */ -"subscription.progress.view.restoring.subscription" = "Restoring subscription..."; +"subscription.progress.view.restoring.subscription" = "Obnovuje sa predplatné..."; /* Remove from this device button */ -"subscription.remove.from.device.button" = "Remove From This Device"; +"subscription.remove.from.device.button" = "Odstrániť z tohto zariadenia"; /* Remove from device confirmation dialog text */ -"subscription.remove.from.device.text" = "You will no longer be able to access your Privacy Pro subscription on this device. This will not cancel your subscription, and it will remain active on your other devices."; +"subscription.remove.from.device.text" = "Na tomto zariadení už nebudeš mať prístup k predplatnému služby Privacy Pro. Tým sa tvoje predplatné nezruší a zostane aktívne na tvojich ostatných zariadeniach."; /* Remove from device confirmation dialog title */ -"subscription.remove.from.device.title" = "Remove from this device?"; +"subscription.remove.from.device.title" = "Odstrániť z tohto zariadenia?"; /* Remove subscription button text */ -"subscription.remove.subscription" = "Remove Subscription"; +"subscription.remove.subscription" = "Zrušiť predplatné"; /* Remove subscription cancel button text */ -"subscription.remove.subscription.cancel" = "Cancel"; +"subscription.remove.subscription.cancel" = "Zrušiť"; /* Button text for general error message */ "subscription.restore.backend.error.button" = "Späť na nastavenia"; @@ -2509,31 +2671,46 @@ "subscription.restore.backend.error.title" = "Vyskytla sa chyba"; /* Alert for general error message */ -"subscription.restore.general.error.message" = "The App Store was unable to process your purchase. Please try again later."; +"subscription.restore.general.error.message" = "App Store nemohol spracovať tvoj nákup. Skús to neskôr znova, prosím."; /* Alert for general error title */ -"subscription.restore.general.error.title" = "Something Went Wrong"; +"subscription.restore.general.error.title" = "Vyskytla sa chyba"; /* Alert button text for restored purchase alert */ "subscription.restore.success.alert.button" = "OK"; /* Alert message for restored purchase */ -"subscription.restore.success.alert.message" = "Your purchases have been restored."; +"subscription.restore.success.alert.message" = "Tvoje nákupy boli obnovené."; /* Alert title for restored purchase */ -"subscription.restore.success.alert.title" = "You’re all set."; +"subscription.restore.success.alert.title" = "Všetko je hotové."; + +/* Subtitle in header when subscribed */ +"subscription.subscribed" = "Prihlásený/-á"; /* Subscription Expired Data. This reads as 'Your subscription expired on (date)' */ "subscription.subscription.expired.caption" = "Platnosť vášho predplatného vypršala dňa %@"; -/* Subscription expiration info. This reads as 'Your (monthly or annual) subscription expires on (date)' */ -"subscription.subscription.expiring.caption" = "Your %1$@ subscription expires on %2$@."; +/* Monthly subscription expiration info where parameter is expiration date. This reads as 'Your monthly subscription expires on (date)' */ +"subscription.subscription.expiring.monthly.caption" = "Tvoje mesačné predplatné vyprší dňa %@."; + +/* Unknown period subscription expiration info where parameter is expiration date. This reads as 'Your subscription expires on (date)' */ +"subscription.subscription.expiring.unknown.caption" = "Platnosť tvojho predplatného vyprší dňa %@."; + +/* Annual subscription expiration info where parameter is expiration date. This reads as 'Your annual subscription expires on (date)' */ +"subscription.subscription.expiring.yearly.caption" = "Tvoje ročné predplatné vyprší dňa %@."; + +/* Monthly subscription renewal info where parameter is renewal date. This reads as 'Your monthly subscription renews on (date)' */ +"subscription.subscription.renewing.monthly.caption" = "Tvoje mesačné predplatné sa obnoví %@."; + +/* Unknown period subscription renewal info where parameter is renewal date. This reads as 'Your subscription renews on (date)' */ +"subscription.subscription.renewing.unknown.caption" = "Tvoje predplatné sa obnoví %@."; -/* Subscription renewal info. This reads as 'Your (monthly or annual) subscription renews on (date)' */ -"subscription.subscription.renewing.caption" = "Your %1$@ subscription renews on %2$@."; +/* Annual subscription renewal info where parameter is renewal date. This reads as 'Your annual subscription renews on (date)' */ +"subscription.subscription.renewing.yearly.caption" = "Tvoje ročné predplatné sa obnoví %@."; /* Navigation bar Title for subscriptions */ -"subscription.title" = "Privacy Pro"; +"subscription.title" = "Ochrana osobných údajov Pro"; /* Message confirming that recovery code was copied to clipboard */ "sync.code.copied" = "Kód obnovenia bol skopírovaný do schránky"; @@ -2677,118 +2854,118 @@ "voiceSearch.footer.note.old" = "Zvuk sa spracováva v zariadení. Neukladá sa nikde a s nikým, vrátane DuckDuckGo, sa nezdieľa."; /* Cancel action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.cancel" = "Dismiss"; +"vpn.access-revoked.alert.action.cancel" = "Odmietnuť"; /* Primary action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.subscribe" = "Subscribe"; +"vpn.access-revoked.alert.action.subscribe" = "Prihlásiť sa na odber"; /* Alert message for the alert when the Privacy Pro subscription expiress */ -"vpn.access-revoked.alert.message" = "Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"vpn.access-revoked.alert.message" = "Predplať si Privacy Pro, aby si znova pripojil/a DuckDuckGo VPN."; /* Alert title for the alert when the Privacy Pro subscription expires */ -"vpn.access-revoked.alert.title" = "VPN disconnected due to expired subscription"; +"vpn.access-revoked.alert.title" = "VPN bola odpojená kvôli vypršaniu platnosti predplatného."; /* Title for the VPN widget onboarding screen */ "vpn.addWidget.settings.title" = "Nájdite a vyberte DuckDuckGo. Potom potiahnite prstom na položku VPN a vyberte položku Pridať widget."; /* Title for the Cancel button of the VPN feedback form */ -"vpn.feedback-form.button.cancel" = "Cancel"; +"vpn.feedback-form.button.cancel" = "Zrušiť"; /* Title for the Done button of the VPN feedback form */ -"vpn.feedback-form.button.done" = "Done"; +"vpn.feedback-form.button.done" = "Hotovo"; /* Title for the Submit button of the VPN feedback form */ -"vpn.feedback-form.button.submit" = "Submit"; +"vpn.feedback-form.button.submit" = "Potvrdiť"; /* Title for the Submitting state of the VPN feedback form */ -"vpn.feedback-form.button.submitting" = "Submitting…"; +"vpn.feedback-form.button.submitting" = "Odoslanie..."; /* Title for the browser crash/freeze category of the VPN feedback form */ -"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN causes browser to crash or freeze"; +"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN spôsobuje zlyhanie alebo zamrznutie prehliadača"; /* Title for the 'VPN fails to connect' category of the VPN feedback form */ -"vpn.feedback-form.category.fails-to-connect" = "VPN fails to connect"; +"vpn.feedback-form.category.fails-to-connect" = "Nedarí sa pripojiť k VPN."; /* Title for the 'VPN feature request' category of the VPN feedback form */ -"vpn.feedback-form.category.feature-request" = "VPN feature request"; +"vpn.feedback-form.category.feature-request" = "Požiadavka na funkciu VPN"; /* Title for the category 'VPN causes issues with other apps or websites' category of the VPN feedback form */ -"vpn.feedback-form.category.issues-with-apps" = "VPN causes issues with other apps or websites"; +"vpn.feedback-form.category.issues-with-apps" = "VPN spôsobuje problémy s inými aplikáciami alebo webovými stránkami."; /* Title for the local device connectivity category of the VPN feedback form */ -"vpn.feedback-form.category.local-device-connectivity" = "VPN won't let me connect to local device"; +"vpn.feedback-form.category.local-device-connectivity" = "VPN mi nedovolí pripojiť sa k lokálnemu zariadeniu"; /* Title for the 'other VPN feedback' category of the VPN feedback form */ -"vpn.feedback-form.category.other" = "Other VPN feedback"; +"vpn.feedback-form.category.other" = "Ďalšie pripomienky k sieti VPN"; /* Title for the category selection state of the VPN feedback form */ -"vpn.feedback-form.category.select-category" = "Select a category"; +"vpn.feedback-form.category.select-category" = "VYBERTE KATEGÓRIU"; /* Title for the 'VPN is too slow' category of the VPN feedback form */ -"vpn.feedback-form.category.too-slow" = "VPN connection is too slow"; +"vpn.feedback-form.category.too-slow" = "VPN pripojenie je príliš pomalé"; /* Title for the 'unable to install' category of the VPN feedback form */ -"vpn.feedback-form.category.unable-to-install" = "Unable to install VPN"; +"vpn.feedback-form.category.unable-to-install" = "Nie je možné nainštalovať VPN."; /* Title for the feedback sent view description of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.description" = "Your feedback will help us improve the\nDuckDuckGo VPN."; +"vpn.feedback-form.sending-confirmation.description" = "Tvoja spätná väzba nám pomôže zlepšiť DuckDuckGo VPN."; /* Title for the feedback sending error text of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.error" = "We couldn't send your feedback right now, please try again."; +"vpn.feedback-form.sending-confirmation.error" = "Vašu spätnú väzbu sme momentálne nemohli odoslať, skús to znova."; /* Title for the feedback sent view title of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.title" = "Thank you!"; +"vpn.feedback-form.sending-confirmation.title" = "Ďakujeme!"; /* Toast message when the VPN feedback form is submitted successfully */ -"vpn.feedback-form.submitted.message" = "Thank You! Feedback submitted."; +"vpn.feedback-form.submitted.message" = "Ďakujeme! Spätná väzba bola odoslaná"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-1" = "Please describe what's happening, what you expected to happen, and the steps that led to the issue:"; +"vpn.feedback-form.text-1" = "Opíš, čo sa deje, čo si očakával/-a, že sa stane, a kroky, ktoré viedli k problému:"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-2" = "In addition to the details entered into this form, your app issue report will contain:"; +"vpn.feedback-form.text-2" = "Okrem podrobností zadaných do tohto formulára bude tvoja správa o probléme s aplikáciou obsahovať:"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-3" = "• Whether specific DuckDuckGo features are enabled"; +"vpn.feedback-form.text-3" = "• či sú zapnuté špecifické funkcie DuckDuckGo"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-4" = "• Aggregate DuckDuckGo app diagnostics"; +"vpn.feedback-form.text-4" = "• agregované diagnostické údaje aplikácie DuckDuckGo"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-5" = "By tapping \"Submit\" I agree that DuckDuckGo may use the information in this report for purposes of improving the app's features."; +"vpn.feedback-form.text-5" = "Kliknutím na tlačidlo „Odoslať“ súhlasím s tým, že DuckDuckGo môže použiť informácie v tejto správe na účely zlepšovania funkcií aplikácie."; /* Title for each screen of the VPN feedback form */ -"vpn.feedback-form.title" = "Help Improve the DuckDuckGo VPN"; +"vpn.feedback-form.title" = "Pomôž zlepšiť DuckDuckGo VPN"; /* VPN settings screen cell text for adding the VPN widget to the home screen */ "vpn.settings.add.widget" = "Pridať miniaplikáciu VPN na domovskú obrazovku"; /* Disclaimer for the DNS Server section on the DNS Server screen */ -"vpn.settings.dns.section-disclaimer" = "Using a custom DNS server can impact browsing speeds and expose your activity to third parties if the server isn't secure or reliable."; +"vpn.settings.dns.section-disclaimer" = "Používanie vlastného servera DNS môže ovplyvniť rýchlosť prehliadania a vystaviť tvoju aktivitu tretím stranám, ak server nie je zabezpečený alebo spoľahlivý."; /* Header text for the DNS section on the VPN Settings screen */ "vpn.settings.dns.section-header" = "DNS"; /* Title for the Apply button on the DNS Server setting screen */ -"vpn.settings.dns.server.apply.button.title" = "Apply"; +"vpn.settings.dns.server.apply.button.title" = "Použiť"; /* Default value for the DNS Server row on the VPN Settings screen */ "vpn.settings.dns.server.default.value" = "DuckDuckGo"; /* Title for the IPv4 Address setting */ -"vpn.settings.dns.server.ipv4.title" = "IPv4 Address"; +"vpn.settings.dns.server.ipv4.title" = "Adresa IPv4"; /* Custom option for the DNS Server setting */ -"vpn.settings.dns.server.option.custom" = "Custom"; +"vpn.settings.dns.server.option.custom" = "Vlastné"; /* Recommended option for the DNS Server setting */ -"vpn.settings.dns.server.option.default" = "DuckDuckGo (Recommended)"; +"vpn.settings.dns.server.option.default" = "DuckDuckGo (odporúčané)"; /* Title for the DNS Server setting screen */ -"vpn.settings.dns.server.screen.title" = "DNS Server"; +"vpn.settings.dns.server.screen.title" = "DNS server"; /* Title for the DNS Server row on the VPN Settings screen */ -"vpn.settings.dns.server.title" = "DNS Server"; +"vpn.settings.dns.server.title" = "DNS server"; /* Title for the button to enable push notifications in system settings */ "waitlist.allow-notifications" = "Povoliť oznámenia"; diff --git a/DuckDuckGo/sk.lproj/OmniBar.strings b/DuckDuckGo/sk.lproj/OmniBar.strings index 3681378d74..563d66aab2 100644 --- a/DuckDuckGo/sk.lproj/OmniBar.strings +++ b/DuckDuckGo/sk.lproj/OmniBar.strings @@ -7,6 +7,9 @@ /* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "Dt8-We-5tV"; */ "Dt8-We-5tV.accessibilityLabel" = "Vymazať text"; +/* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "gb5-wL-4OW"; */ +"gb5-wL-4OW.accessibilityLabel" = "Vymazať text"; + /* Class = "UIButton"; accessibilityLabel = "Share"; ObjectID = "hor-Jd-3kw"; */ "hor-Jd-3kw.accessibilityLabel" = "Zdieľať"; diff --git a/DuckDuckGo/sk.lproj/Settings.strings b/DuckDuckGo/sk.lproj/Settings.strings index 625a14ec00..9ce828b1c2 100644 --- a/DuckDuckGo/sk.lproj/Settings.strings +++ b/DuckDuckGo/sk.lproj/Settings.strings @@ -10,9 +10,6 @@ /* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "5YD-Od-uDU"; */ "5YD-Od-uDU.text" = "Automaticky vymazať údaje"; -/* Class = "UIButton"; normalTitle = "Settings"; ObjectID = "8YG-7Q-Czd"; */ -"8YG-7Q-Czd.normalTitle" = "Nastavenia"; - /* Class = "UILabel"; text = "App Exit, Inactive for 1 Hour"; ObjectID = "9EC-lx-77p"; */ "9EC-lx-77p.text" = "Aplikácia bola zatvorená po 1 hodine neaktívnosti"; @@ -76,9 +73,6 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Nezabezpečené webové stránky"; -/* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ -"ssa-zd-L3T.title" = "Veľkosť textu"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Zobraziť klávesnicu v"; diff --git a/DuckDuckGo/sl.lproj/DaxOnboarding.strings b/DuckDuckGo/sl.lproj/DaxOnboarding.strings index 07aa05578e..e846f16968 100644 --- a/DuckDuckGo/sl.lproj/DaxOnboarding.strings +++ b/DuckDuckGo/sl.lproj/DaxOnboarding.strings @@ -1,12 +1,6 @@ /* Class = "UIButton"; normalTitle = "Hide"; ObjectID = "hgy-75-OvJ"; */ "hgy-75-OvJ.normalTitle" = "Skrij se"; -/* Class = "UILabel"; text = "Welcome to\nDuckDuckGo!"; ObjectID = "Lvy-9G-3gr"; */ -"Lvy-9G-3gr.text" = "Dobrodošli v aplikaciji\nDuckDuckGo!"; - -/* Class = "UIButton"; normalTitle = "Let’s Do It!"; ObjectID = "oAv-Zt-uIt"; */ -"oAv-Zt-uIt.normalTitle" = "Pa začnimo!"; - /* Class = "UIButton"; normalTitle = "Button"; ObjectID = "zJt-F9-L48"; */ "zJt-F9-L48.normalTitle" = "Gumb"; diff --git a/DuckDuckGo/sl.lproj/Localizable.strings b/DuckDuckGo/sl.lproj/Localizable.strings index 6a279a7f42..eb3046fb6b 100644 --- a/DuckDuckGo/sl.lproj/Localizable.strings +++ b/DuckDuckGo/sl.lproj/Localizable.strings @@ -20,13 +20,13 @@ "action.ok" = "V REDU"; /* Title for text zoom menu item. %d%% is replaced with percent, e.g. 56% so do not change that please. */ -"action.text-zoom-menu-item" = "Povečava (%d%%)"; +"action.text-zoom-menu-item" = "Povečava (%d %%)"; /* Text zoom menu item */ "action.text-zoom-sheet-menu-item" = "Povečava"; /* Title for text zoom sheet view. %d%% is replaced with percent, e.g. 56% so do not change that please. */ -"action.text-zoom-sheet-title" = "Povečava besedila (%d%%)"; +"action.text-zoom-sheet-title" = "Povečava besedila (%d %%)"; /* Add action - button shown in alert */ "action.title.add" = "Dodaj"; @@ -341,7 +341,7 @@ "autofill.keep-enabled.alert.disable" = "Onemogoči"; /* A link that takes the user to the DuckDuckGo help pages explaining password managers */ -"autofill.learn.more.link.title" = "Learn More"; +"autofill.learn.more.link.title" = "Več"; /* Button displayed after saving/updating an autofill login that takes the user to the saved login */ "autofill.login-save-action-button.toast" = "Oglejte si"; @@ -977,9 +977,6 @@ /* ad = advertisment */ "dax.onboarding.home.subsequent" = "Uspelo ti bo!\n\nNe pozabite: vsakič, ko brskate z mano, grozljiv oglas izgubi krila. 👍"; -/* No comment provided by engineer. */ -"dax.onboarding.message" = "Internet je lahko grozljiv.\n\nNe skrbi! Iskanje in brskanje na zaseben način je lažje, kot si misliš."; - /* No comment provided by engineer. */ "Debug" = "Odpravljanje napak"; @@ -1080,7 +1077,7 @@ "duckPlayer.never.label" = "Nikoli"; /* Body text for the modal feature explanation */ -"duckplayer.presentation.modal.body" = "Predvajalnik Duck Player vam s pomočjo DuckDuckGo omogoča kinematografsko izkušnjo gledanja YouTuba brez ciljanih oglasov, kar gledate pa ne bo vplivalo na vaša priporočila."; +"duckplayer.presentation.modal.body" = "Predvajalnik Duck Player vam s pomočjo DuckDuckGo omogoča gledanje YouTuba brez ciljanih oglasov, kar gledate pa ne bo vplivalo na vaša priporočila."; /* Button that will dismiss the modal */ "duckplayer.presentation.modal.dismiss-button" = "Razumem!"; @@ -1088,17 +1085,23 @@ /* Two line title (separated by \n) for the feature explanation */ "duckplayer.presentation.modal.title" = "Se utapljate v oglasih na YouTubu? Preizkusite Duck Player!"; +/* Two line title (separated by \n) for the feature explanation */ +"duckplayer.presentation.modal.title.youtube" = "Se utapljate v oglasih na YouTubu? S predvajalnikom Duck Player bo to preteklost!"; + /* Footer label in the settings screen for Duck Player */ "duckplayer.settings.footer" = "DuckDuckGo zagotavlja vse bistvene elemente zasebnosti, ki jih potrebujete, da se zaščitite med brskanjem po spletu."; /* Text explaining what Duck Player is in the settings screen. */ -"duckplayer.settings.info-text" = "Predvajalnik Duck Player zagotavlja čisto izkušnjo gledanja brez prilagojenih oglasov in preprečuje, da bi dejavnost gledanja vplivala na vaša priporočila v YouTubu."; +"duckplayer.settings.info-text" = "Predvajalnik Duck Player vam s pomočjo DuckDuckGo omogoča gledanje YouTuba brez ciljanih oglasov, kar gledate pa ne bo vplivalo na vaša priporočila."; /* Button that takes the user to learn more about Duck Player. */ "duckplayer.settings.learn-more" = "Več"; +/* Settings screen cell text for DuckPlayer settings to open in new tab */ +"duckplayer.settings.open-new-tab-label" = "Odpri Duck Player v novem zavihku"; + /* Settings screen cell text for DuckPlayer settings */ -"duckplayer.settings.open-videos-in" = "Odpri videoposnetke s predvajalnikom Duck Player"; +"duckplayer.settings.open-videos-in" = "Videoposnetke v YouTubu odpri s predvajalnikom Duck Player"; /* Settings screen cell text for DuckPlayer settings */ "duckplayer.settings.title" = "Duck Player"; @@ -1169,9 +1172,6 @@ /* Title for prompt to sign up for email protection */ "email.signup-prompt.title" = "Skrijte svojo e-pošto in \nblokirajte sledilnike"; -/* No comment provided by engineer. */ -"empty!" = "prazno!"; - /* Empty list state placholder */ "empty.bookmarks" = "Zaznamki še niso dodani"; @@ -1322,6 +1322,39 @@ /* Confirmation button */ "feedback.form.submit" = "Potrdi"; +/* Title of the feedback form */ +"feedback.form.title" = "Pomagajte izboljšati Privacy Pro"; + +/* Category for Identity Theft Restoration feedback */ +"feedback.general.category.itr" = "Identity Theft Restoration (Obnovitev v primeru kraje identitete)"; + +/* Category for Personal Info Removal feedback */ +"feedback.general.category.pir" = "Odstranitev osebnih podatkov"; + +/* Category for subscription and payments feedback */ +"feedback.general.category.ppro" = "Naročnine in plačila"; + +/* Prompt to select a category for general feedback */ +"feedback.general.category.select" = "IZBERITE KATEGORIJO"; + +/* Category for VPN feedback */ +"feedback.general.category.vpn" = "VPN"; + +/* Category for access code issues */ +"feedback.itr.category.access-code" = "Težava s kodo za dostop"; + +/* Category for issues contacting an advisor */ +"feedback.itr.category.cant-contact-advisor" = "Ne morem vzpostaviti stika s svetovalcem"; + +/* Prompt to select a category for Identity Theft Restoration feedback */ +"feedback.itr.category.select" = "IZBERITE KATEGORIJO"; + +/* Category for other Identity Theft Restoration issues */ +"feedback.itr.category.something-else" = "Nekaj drugega"; + +/* Category for unhelpful advisor calls */ +"feedback.itr.category.unhelpful" = "Klic s svetovalcem ni bil koristen"; + /* No comment provided by engineer. */ "feedback.negative.form.genericPlaceholder" = "Bodite čim bolj natančni"; @@ -1361,6 +1394,24 @@ /* No comment provided by engineer. */ "feedback.performance.slowLoading" = "Spletne strani ali rezultati iskanja se nalagajo počasi"; +/* Category for when scan finds incorrect records */ +"feedback.pir.category.not-me" = "Pri pregledu so bili najdeni zapisi, ki niso moji"; + +/* Category for when scan doesn't find info on a specific site */ +"feedback.pir.category.nothing-on-site" = "Pregled mojih podatkov ni našel na določenem spletnem mestu"; + +/* Category for other Personal Info Removal issues */ +"feedback.pir.category.other" = "Nekaj drugega"; + +/* Category for when the removal process is stuck */ +"feedback.pir.category.removal-stuck" = "Postopek odstranitve je zmrznil"; + +/* Category for when the scan is stuck */ +"feedback.pir.category.scan-stuck" = "Skeniranje zapisov je zmrznilo"; + +/* Prompt to select a category for Personal Info Removal feedback */ +"feedback.pir.category.select" = "IZBERITE KATEGORIJO"; + /* Header above input field */ "feedback.positive.form.header" = "Delite podrobnosti"; @@ -1379,6 +1430,15 @@ /* Button encouraging uses to share details aboout their feedback */ "feedback.positive.submit" = "Delite podrobnosti"; +/* Category for other Privacy Pro issues */ +"feedback.ppro.category.other" = "Nekaj drugega"; + +/* Category for one-time password issues */ +"feedback.ppro.category.otp" = "Težava z enkratnim geslom"; + +/* Prompt to select a category for Privacy Pro feedback */ +"feedback.ppro.category.select" = "IZBERITE KATEGORIJO"; + /* No comment provided by engineer. */ "feedback.start.footer" = "Vaše anonimne povratne informacije so nam pomembne."; @@ -1529,12 +1589,6 @@ /* No comment provided by engineer. */ "keyCommandShowAllTabs" = "Prikaži vse zavihke"; -/* Please preserve newline character */ -"launchscreenWelcomeMessage" = "Dobrodošli v aplikaciji\nDuckDuckGo!"; - -/* No comment provided by engineer. */ -"LOREM IPSUM" = "LOREM IPSUM"; - /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "DuckDuckGo za računalnike Mac zagotavlja hitrost, ki jo potrebujete, in funkcije brskanja, ki jih pričakujete, ter je opremljen z našimi ključnimi funkcijami zasebnosti, ki so najboljše v tem razredu."; @@ -1586,62 +1640,83 @@ /* Edit button */ "navigation.title.edit" = "Uredi"; -/* String indicating NetP is connected when viewed from the settings screen */ -"netP.cell.connected" = "Connected"; - -/* String indicating NetP is disconnected when viewed from the settings screen */ -"netP.cell.disconnected" = "Not connected"; - -/* Title for the DuckDuckGo VPN feature in settings */ -"netP.settings.title" = "VPN"; +/* String indicating NetP is snoozing when viewed from the settings screen */ +"netP.cell.snoozing" = "Dremež"; /* Title for the DuckDuckGo VPN feature */ "netP.title" = "VPN DuckDuckGo"; +/* Button title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.action" = "Dodaj gradnik"; + +/* Message for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.message" = "Vklopite in izklopite VPN kar na začetnem zaslonu."; + +/* Title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.title" = "Dodajte pripomoček VPN"; + /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN je prekinjen zaradi potekle naročnine. Naročite se na Privacy Pro, da znova povežete DuckDuckGo VPN."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Zaščita omrežja se ni uspela povezati. Poskusite znova pozneje."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "DuckDuckGo VPN se ni mogel povezati. Poskusite znova pozneje."; /* Title for the VPN FAQ screen. */ "network.protection.faq.title" = "Pogosta vprašanja o omrežju VPN DuckDuckGo"; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Zaščita omrežja je bila prekinjena. Poskušam se znova povezati zdaj ..."; +/* Message for tooltip about geoswitching */ +"network.protection.geoswitching.tip.message" = "Svojo lokacijo VPN lahko prilagodite tako, da se povežete s katerim koli od naših strežnikov po vsem svetu."; + +/* Title for tooltip about geoswitching */ +"network.protection.geoswitching.tip.title" = "Spremenite svojo lokacijo"; + +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "DuckDuckGo VPN je bil prekinjen. Poskušam se znova povezati zdaj ..."; /* Message for the network protection invite dialog */ -"network.protection.invite.dialog.message" = "Enter your invite code to get started."; +"network.protection.invite.dialog.message" = "Za začetek vnesite kodo povabila."; /* Title for the network protection invite screen */ "network.protection.invite.dialog.title" = "Vabimo vas, da preizkusite omrežje VPN DuckDuckGo"; /* Prompt for the network protection invite code text field */ -"network.protection.invite.field.prompt" = "Invite Code"; +"network.protection.invite.field.prompt" = "Koda povabila"; /* Message for the network protection invite success view */ -"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; +"network.protection.invite.success.message" = "Skrijte svojo lokacijo pred spletnimi mesti in prikrijte svoje spletne aktivnosti pred ponudniki interneta in drugimi v omrežju."; /* Title for the network protection invite success view */ -"network.protection.invite.success.title" = "Success! You’re in."; +"network.protection.invite.success.title" = "Uspelo je! Vključeni ste."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; /* Title text for an iOS quick action that opens VPN settings */ "network.protection.quick-action.open-vpn" = "Odpri VPN"; +/* Button title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.action" = "Več"; + +/* Message for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.message" = "Uporabljate lahko spletna mesta ali aplikacije, ki blokirajo promet VPN tako, da preložijo povezavo VPN."; + +/* Title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.title" = "Izogibanje konfliktom v omrežju VPN"; + +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN je preložen za %@"; + /* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ "network.protection.status.connected.format" = "Povezano · %@"; /* The label for the NetP VPN when connecting */ -"network.protection.status.connecting" = "Connecting..."; +"network.protection.status.connecting" = "Povezovanje ..."; /* The label for the NetP VPN when disconnected */ -"network.protection.status.disconnected" = "Not connected"; +"network.protection.status.disconnected" = "Brez povezave"; /* The label for the NetP VPN when disconnecting */ -"network.protection.status.disconnecting" = "Disconnecting..."; +"network.protection.status.disconnecting" = "Prekinjanje povezave ..."; /* Message label text for the status view when VPN is disconnected */ "network.protection.status.header.message.off" = "Povežite se, da zaščitite ves internetni promet\nv svoji napravi."; @@ -1655,26 +1730,41 @@ /* Header title label text for the status view when VPN is connected */ "network.protection.status.header.title.on" = "Omrežje VPN DuckDuckGo je vklopljeno"; +/* Header title label text for the status view when VPN is snoozing */ +"network.protection.status.header.title.snoozed" = "Omrežje VPN DuckDuckGo je preloženo"; + /* The status view 'Share Feedback' button which is shown inline on the status view after the temporary free use footer text */ -"network.protection.status.menu.share.feedback" = "Share Feedback"; +"network.protection.status.menu.share.feedback" = "Deli povratne informacije"; + +/* The label for the NetP VPN when paused */ +"network.protection.status.paused" = "Začasno ustavljeno"; + +/* The label for when NetP VPN is snoozing plus the length of time remaining formatted as '0:00' */ +"network.protection.status.snoozing.format" = "Dremež, %@ preostalo"; + +/* Snooze button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.snooze" = "Preloženo za 20 minut"; + +/* Wake Up button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.wake-up" = "Prebudi"; /* Connection details label shown in NetworkProtection's status view. */ -"network.protection.status.view.connection.details" = "Connection Details"; +"network.protection.status.view.connection.details" = "Podrobnosti povezave"; /* Custom DNS label shown in NetworkProtection's status view. */ -"network.protection.status.view.custom.dns" = "DNS Server"; +"network.protection.status.view.custom.dns" = "Strežnik DNS"; /* Generic connection failed error message shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.message" = "Please try again later."; +"network.protection.status.view.error.connection.failed.message" = "Poskusite znova pozneje."; /* Generic connection failed error title shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; +"network.protection.status.view.error.connection.failed.title" = "Ni uspelo vzpostaviti povezave."; /* IP Address label shown in NetworkProtection's status view. */ -"network.protection.status.view.ip.address" = "IP Address"; +"network.protection.status.view.ip.address" = "Naslov IP"; /* Location label shown in NetworkProtection's status view. */ -"network.protection.status.view.location" = "Location"; +"network.protection.status.view.location" = "Lokacija"; /* Label shown on the title of the settings section in NetworkProtection's status view. */ "network.protection.status.view.settings.section.title" = "Upravljanje"; @@ -1682,12 +1772,15 @@ /* Title label text for the status view when netP is disconnected */ "network.protection.status.view.title" = "VPN"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Zaščita omrežja je vklopljena. Vaša lokacija in spletna dejavnost sta zaščiteni."; +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "Omrežje VPN DuckDuckGo je vklopljeno. Vaša lokacija in spletna dejavnost sta zaščiteni."; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Usmerjanje prometa naprave prek kraja %@."; +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "Dremež za VPN je končan. Usmerjanje prometa naprave prek kraja %@."; + /* Title for the button to link to the iOS app settings and enable notifications app-wide. */ "network.protection.turn.on.notifications.button.title" = "Vključite obvestila"; @@ -1698,13 +1791,13 @@ "network.protection.vpn.about" = "Več o"; /* Section header for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.section.header" = "Notifications"; +"network.protection.vpn.alerts.section.header" = "Obvestila"; /* List section footer for the toggle for VPN alerts. */ "network.protection.vpn.alerts.toggle.section.footer" = "Prejmite obvestilo o prekinitvi povezave ali spremembi stanja VPN."; /* Title for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.toggle.title" = "VPN Notifications"; +"network.protection.vpn.alerts.toggle.title" = "Obvestila VPN"; /* Title for the data volume section in the VPN status screen */ "network.protection.vpn.data-volume" = "Podatkovni nosilec"; @@ -1713,13 +1806,13 @@ "network.protection.vpn.exclude.local.networks.setting.footer" = "Omogočite, da lahko lokalni promet zaobide omrežje VPN in se poveže z napravami v lokalnem omrežju, na primer s tiskalnikom."; /* Header text for the Exclude Local Networks setting item. */ -"network.protection.vpn.exclude.local.networks.setting.header" = "General"; +"network.protection.vpn.exclude.local.networks.setting.header" = "Splošno"; /* Title for the Exclude Local Networks setting item. */ "network.protection.vpn.exclude.local.networks.setting.title" = "Izključitev lokalnih omrežij"; /* Title for the VPN Location screen's All Countries section. */ -"network.protection.vpn.location.all.countries.section.title" = "All Countries"; +"network.protection.vpn.location.all.countries.section.title" = "Vse države"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.connected" = "Povezana lokacija"; @@ -1734,7 +1827,7 @@ "network.protection.vpn.location.recommended.section.footer" = "Samodejno se poveže z najbližjim strežnikom, ki ga najdemo."; /* Title for the VPN Location screen's Recommended section. */ -"network.protection.vpn.location.recommended.section.title" = "Recommended"; +"network.protection.vpn.location.recommended.section.title" = "Priporočeno"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.selected" = "Izbrana lokacija"; @@ -1752,13 +1845,13 @@ "network.protection.vpn.preferred.location.nearest" = "Najbližja lokacija"; /* Title for the Preferred Location VPN Settings item. */ -"network.protection.vpn.preferred.location.title" = "Preferred Location"; +"network.protection.vpn.preferred.location.title" = "Prednostna lokacija"; /* Footer text for the DNS server setting item. */ -"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo routes DNS queries through our DNS servers so your internet provider can't see what websites you visit."; +"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo usmerja poizvedbe DNS prek naših strežnikov DNS, da vaš ponudnik interneta ne more videti, katere spletne strani obiskujete."; /* Title for the FAQ row in the VPN status screen. */ -"network.protection.vpn.settings.faq" = "FAQs and Support"; +"network.protection.vpn.settings.faq" = "Pogosta vprašanja in podpora"; /* Title for the feedback row in the VPN status screen. */ "network.protection.vpn.settings.share-feedback" = "Deljenje povratnih informacij o omrežju VPN"; @@ -1940,6 +2033,66 @@ /* Deactivate button */ "pm.deactivate" = "Deaktiviraj"; +/* Label for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.label" = "E-naslov (neobvezno)"; + +/* Placeholder for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.placeholder" = "ime@email.com"; + +/* Placeholder for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.placeholder" = "Pošljite nam povratne informacije ..."; + +/* Title for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.title" = "Splošne povratne informacije"; + +/* Title for the ITR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-itr-problem.title" = "Identity Theft Restoration (Obnovitev v primeru kraje identitete)"; + +/* Title for the PIR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-pir-problem.title" = "Personal Information Removal (Odstranitev osebnih podatkov)"; + +/* Title for the Subscriptions and Payments category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-ppro-problem.title" = "Naročnine in plačila"; + +/* Placeholder for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.placeholder" = "Povejte nam, kaj se dogaja ..."; + +/* Title for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.title" = "Prijava težave"; + +/* Title for the VPN category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-vpn-problem.title" = "VPN"; + +/* Placeholder for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.placeholder" = "Katero funkcijo bi radi videli?"; + +/* Title for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.title" = "Zahteva za funkcijo"; + +/* Title for the category selection section in the Privacy Pro feedback form */ +"ppro.feedback-form.select-category.title" = "IZBERITE KATEGORIJO"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-1" = "Ste našli težavo, ki ni obravnavana v našem [središču za pomoč](duck://)? Vsekakor želimo vedeti o tem.\n\nNavedite e-poštni naslov, če želite, da vas kontaktiramo glede te težave (morda ne bomo mogli odgovoriti na vse težave):"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-2" = "Poleg zgoraj vnesenih podatkov pošljemo nekaj anonimnih informacij z vašimi povratnimi informacijami:"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-3" = "• ali so nekatere funkcije brskalnika aktivne,"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-4" = "• zbiranje diagnostike aplikacij (npr. kode napak)."; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-5" = "Z dotikom možnosti »Pošlji« se strinjate, da lahko DuckDuckGo uporabi poslane informacije za izboljšanje aplikacije."; + +/* Title for the text box in the Privacy Pro feedback form */ +"ppro.feedback-form.text-box.title" = "Povratne informacije"; + +/* Title for the Privacy Pro feedback form */ +"ppro.feedback-form.title" = "Pošljite povratne informacije"; + /* Button title for sync bookmarks limits exceeded warning to go to manage bookmarks */ "prefrences.sync.bookmarks-limit-exceeded-action" = "Upravljanje zaznamkov"; @@ -2029,6 +2182,18 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Priljubljeni"; +/* Name of the option the user can chose to give general browser feedback */ +"send.browser.feedback.general-feedback" = "Splošne povratne informacije"; + +/* Name of the option the user can chose to give browser feedback about a problem they enountered */ +"send.browser.feedback.report-problem" = "Prijava težave"; + +/* Name of the option the user can chose to give browser feedback about a feature they would like */ +"send.browser.feedback.request-feature" = "Zahtevajte funkcijo"; + +/* Title of the picker where the user can chose the category of the feedback they want ot send. */ +"send.browser.feedback.select-category" = "IZBERITE KATEGORIJO"; + /* Settings cell for About DDG */ "settings.about.ddg" = "O DuckDuckGo"; @@ -2084,6 +2249,9 @@ /* Section footer Autolock description */ "settings.autolock.description" = "Če je omogočena identifikacija s prepoznavanjem prstnega odtisa ali obraza ali s sistemsko kodo za dostop, boste pri odpiranju aplikacije pozvani, da jo odklenete."; +/* Settings cell for Browser Feedback */ +"settings.browser.feedback" = "Povratne informacije o brskalniku"; + /* Settings screen cell text for Automatically Clearing Data */ "settings.clear.data" = "Samodejno počisti podatke"; @@ -2182,7 +2350,7 @@ "settings.ppro" = "Privacy Pro"; /* Title for Link in the Footer of Privacy Pro section */ -"settings.ppro.footer" = "Privacy Policy and Terms of Service"; +"settings.ppro.footer" = "Pravilnik o zasebnosti in pogoji storitve"; /* Settings screen cell for long press previews */ "settings.previews" = "Pregledi z dolgim pritiskom"; @@ -2208,23 +2376,20 @@ /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Nastavitev položaja naslovne vrstice"; -/* Subscription activation pending description */ -"settings.subscription.activation.pending.description" = "This is taking longer than usual, please check back later."; +/* Privacy pro description subtitle in settings when the is activating */ +"settings.subscription.activating" = "Aktiviranje"; -/* Subscription activation pending title */ -"settings.subscription.activation.pending.title" = "Vaša naročnina je v postopku aktivacije"; +/* Subscription activation pending description */ +"settings.subscription.activation.pending.description" = "To traja dlje kot običajno, preverite pozneje."; /* Data Broker protection cell subtitle for privacy pro */ -"settings.subscription.DBP.subtitle" = "Remove your info from sites that sell it"; +"settings.subscription.DBP.subtitle" = "Odstranite svoje podatke s spletnih mest, ki jih prodajajo."; /* Data Broker protection cell title for privacy pro */ -"settings.subscription.DBP.title" = "Personal Information Removal"; - -/* Privacy pro description subtext */ -"settings.subscription.description" = "Več brezhibne zasebnosti s tremi novimi zaščitami:"; +"settings.subscription.DBP.title" = "Personal Information Removal (Odstranitev osebnih podatkov)"; /* I have a Subscription button text for privacy pro */ -"settings.subscription.existing.subscription" = "I Have a Subscription"; +"settings.subscription.existing.subscription" = "Imam naročnino"; /* Subscription expired description */ "settings.subscription.expired.comment" = "Znova se naročite, če želite še naprej uporabljati Privacy Pro"; @@ -2232,23 +2397,26 @@ /* Subscription expired tittle message */ "settings.subscription.expired.title" = "Vaša naročnina na Privacy Pro je potekla"; -/* Privacy pro features list */ -"settings.subscription.features" = " • VPN\n • Personal Information Removal\n • Identity Theft Restoration"; - /* Identity theft restoration cell subtitle for privacy pro */ -"settings.subscription.ITR.subtitle" = "If your identity is stolen, we'll help restore it"; +"settings.subscription.ITR.subtitle" = "Če vam ukradejo identiteto, vam jo bomo pomagali povrniti."; /* Identity theft restoration cell title for privacy pro */ -"settings.subscription.ITR.title" = "Identity Theft Restoration"; +"settings.subscription.ITR.title" = "Identity Theft Restoration (Obnovitev v primeru kraje identitete)"; /* Get Privacy Pro button text for privacy pro */ -"settings.subscription.learn.more" = "Get Privacy Pro"; +"settings.subscription.learn.more" = "Pridobite Privacy Pro"; /* Subscription Settings button text for privacy pro */ -"settings.subscription.manage" = "Subscription Settings"; +"settings.subscription.manage" = "Nastavitve naročnine"; + +/* Privacy Pro description subtitle in settings */ +"settings.subscription.row.description" = "Vključuje naš VPN in Identity Theft Restoration."; -/* Call to action title for Privacy Pro */ -"settings.subscription.subscribe" = "Subscribe to Privacy Pro"; +/* Call to action title for Privacy Pro settings */ +"settings.subscription.subscribe" = "Zaščite svojo povezavo in identiteto s Privacy Pro"; + +/* Privacy pro description subtitle in settings */ +"settings.subscription.us.description" = "Vključuje naše storitve VPN, Personal Information Removal in Identity Theft Restoration."; /* VPN cell title for privacy pro */ "settings.subscription.VPN.title" = "VPN"; @@ -2317,67 +2485,64 @@ "siteFeedback.urlPlaceholder" = "Katera spletna stran je poškodovana?"; /* Subscription Activation Info */ -"subscription.activate..header.description" = "Access your Privacy Pro subscription on this device via Apple ID or an email address."; +"subscription.activate..header.description" = "Dostopajte do svoje naročnine Privacy Pro v tej napravi prek storitve Apple ID ali e-poštnega naslova."; /* Button for adding email address to subscription */ -"subscription.activate.add.email.button" = "Add Email"; +"subscription.activate.add.email.button" = "Dodajte e-naslov"; /* Apple ID option for activation */ "subscription.activate.appleid" = "Apple ID"; /* Button text for restoring purchase via Apple ID */ -"subscription.activate.appleid.button" = "Restore Purchase"; +"subscription.activate.appleid.button" = "Obnovitev nakupa"; /* Description for Apple ID activation */ -"subscription.activate.appleid.description" = "Restore your purchase to activate your subscription on this device."; +"subscription.activate.appleid.description" = "Obnovite nakup, da aktivirate naročnino v tej napravi."; /* Subscription Activation Info */ -"subscription.activate.description" = "Your subscription is automatically available in DuckDuckGo on any device signed in to your Apple ID."; +"subscription.activate.description" = "Vaša naročnina je samodejno na voljo v DuckDuckGo v kateri koli napravi, kjer ste prijavljeni v svoj račun Apple ID."; /* Button for editing email address added to subscription */ -"subscription.activate.edit.email.button" = "Edit Email"; +"subscription.activate.edit.email.button" = "Urejanje e-naslova"; /* View Title for editing your email account */ -"subscription.activate.edit.email.title" = "Edit Email"; +"subscription.activate.edit.email.title" = "Urejanje e-naslova"; /* Email option for activation */ -"subscription.activate.email" = "Email"; +"subscription.activate.email" = "E-naslov"; /* Restore button title for Email */ -"subscription.activate.email.button" = "Enter Email"; +"subscription.activate.email.button" = "Vnesite e-naslov"; /* Description for Email activation */ -"subscription.activate.email.description" = "Use your email to activate your subscription on this device."; +"subscription.activate.email.description" = "Uporabite svoj e-naslov, da aktivirate naročnino v tej napravi."; /* Activate subscription title */ "subscription.activate.email.title" = "Aktiviranje naročnine"; /* Button title for cancelling email deletion */ -"subscription.activate.manage.email.cancel" = "Cancel"; +"subscription.activate.manage.email.cancel" = "Prekliči"; /* Button title for confirming email deletion */ -"subscription.activate.manage.email.OK" = "OK"; +"subscription.activate.manage.email.OK" = "V REDU"; /* Restore button title for AppleID */ -"subscription.activate.restore.apple" = "Restore Purchase"; +"subscription.activate.restore.apple" = "Obnovitev nakupa"; /* Subscription Activation Title */ -"subscription.activate.title" = "Activate your subscription on this device"; +"subscription.activate.title" = "Aktivirajte svojo naročnino v tej napravi"; /* Resend activation instructions button */ -"subscription.add.device.resend.instructions" = "Resend Instructions"; +"subscription.add.device.resend.instructions" = "Znova pošlji navodila"; /* Add email to an existing subscription */ -"subscription.add.email" = "Add an email address to activate your subscription on your other devices. We’ll only use this address to verify your subscription."; +"subscription.add.email" = "Dodajte e-poštni naslov za aktivacijo naročnine v drugih napravah. Ta naslov bomo uporabili le za preverjanje vaše naročnine."; /* View title for adding email to subscription */ -"subscription.add.email.title" = "Add Email"; - -/* Title for Alert messages */ -"subscription.alert.title" = "subscription.alert.title"; +"subscription.add.email.title" = "Dodajte e-naslov"; /* Subscription availability message on Apple devices */ -"subscription.available.apple" = "Privacy Pro is available on any device signed in to the same Apple ID."; +"subscription.available.apple" = "Privacy Pro je na voljo v vseh napravah, v katerih ste prijavljeni v isti Apple ID."; /* Text for the manage billing page */ "subscription.billing.google.text" = "Vaša naročnina je bila kupljena v trgovini Google Play. Če želite podaljšati naročnino, odprite nastavitve naročnine v trgovini Google Play v napravi, v kateri ste prijavljeni v isti račun Google, kot ste ga uporabili za prvotni nakup naročnine."; @@ -2385,89 +2550,86 @@ /* Title for the manage billing page */ "subscription.billing.google.title" = "Naročniški paketi"; -/* Subscription annual billing period type */ -"subscription.billing.period.annual" = "annual"; - -/* Subscription monthly billing period type */ -"subscription.billing.period.monthly" = "monthly"; - /* Subscription Removal confirmation message */ -"subscription.cancel.message" = "Your subscription has been removed from this device."; +"subscription.cancel.message" = "Vaša naročnina je bila odstranjena iz te naprave."; /* Change plan or cancel title */ -"subscription.change.plan" = "Update Plan or Cancel"; +"subscription.change.plan" = "Posodobitev paketa ali preklic"; /* Navigation Button for closing subscription view */ -"subscription.close" = "Close"; +"subscription.close" = "Zapri"; /* Title for Confirm messages */ -"subscription.confirm.title" = "Are you sure?"; +"subscription.confirm.title" = "Ste prepričani?"; /* Header for section for activating subscription on other devices */ -"subscription.devices.header" = "Activate on Other Devices"; +"subscription.devices.header" = "Aktiviranje v drugih napravah"; /* Footer for section for activating subscription on other devices when email was not yet added */ -"subscription.devices.no.email.footer" = "Add an optional email to your subscription or use your Apple ID to access Privacy Pro on other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.no.email.footer" = "V naročnino dodajte neobvezen e-naslov, da boste lahko do naročnine Privacy Pro dostopali v drugih napravah. **[Več o tem](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Footer for section for activating subscription on other devices when email is added */ -"subscription.devices.with.email.footer" = "Use this email to activate your subscription in Settings > Privacy Pro in the DuckDuckGo app on your other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.with.email.footer" = "Ta e-naslov uporabite za aktivacijo naročnine v aplikaciji DuckDuckGo v drugih napravah, in sicer tako, da odprete Nastavitve > Privacy Pro. **[Več o tem](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Alert content for not found subscription */ -"subscription.email.inactive.alert.message" = "The subscription associated with this email is no longer active."; +"subscription.email.inactive.alert.message" = "Naročnina, povezana s tem e-poštnim naslovom, ni več aktivna."; /* Alert title for not found subscription */ -"subscription.email.inactive.alert.title" = "Subscription Not Found"; +"subscription.email.inactive.alert.title" = "Naročnina ni najdena"; /* Alert content for not found subscription */ -"subscription.expired.alert.message" = "The subscription associated with this Apple ID is no longer active."; +"subscription.expired.alert.message" = "Naročnina, povezana s tem Apple ID-jem, ni več aktivna."; /* FAQ Button */ -"subscription.faq" = "FAQs and Support"; +"subscription.faq" = "Pogosta vprašanja in podpora"; /* FAQ Description */ -"subscription.faq.description" = "Get answers to frequently asked questions or contact Privacy Pro support from our help pages."; +"subscription.faq.description" = "Poiščite odgovore na pogosta vprašanja ali stopite v stik s podporo za naročnino Privacy Pro na naših straneh za pomoč."; + +/* Send Feedback Button */ +"subscription.feedback" = "Pošljite povratne informacije"; /* Cancel action for the existing subscription dialog */ -"subscription.found.cancel" = "Cancel"; +"subscription.found.cancel" = "Prekliči"; /* Restore action for the existing subscription dialog */ -"subscription.found.restore" = "Restore"; +"subscription.found.restore" = "Obnovitev"; /* Message for the existing subscription dialog */ -"subscription.found.text" = "We found a subscription associated with this Apple ID."; +"subscription.found.text" = "Našli smo naročnino, povezano s tem Apple ID-jem."; /* Title for the existing subscription dialog */ -"subscription.found.title" = "Subscription Found"; +"subscription.found.title" = "Najdena je bila naročnina"; /* Help and support Section header */ -"subscription.help" = "Help and support"; +"subscription.help" = "Pomoč in podpora"; /* Manage Plan header */ -"subscription.manage.plan" = "Manage Plan"; +"subscription.manage.plan" = "Upravljanje načrta"; /* Header for the subscription section */ -"subscription.manage.title" = "Subscription"; +"subscription.manage.title" = "Naročnina"; /* Alert content for not found subscription */ -"subscription.notFound.alert.message" = "There is no subscription associated with this Apple ID."; +"subscription.notFound.alert.message" = "S tem Apple ID-jem ni povezana nobena naročnina."; /* Alert title for not found subscription */ -"subscription.notFound.alert.title" = "Subscription Not Found"; +"subscription.notFound.alert.title" = "Naročnina ni najdena"; /* View plans button text */ -"subscription.notFound.view.plans" = "View Plans"; +"subscription.notFound.view.plans" = "Oglejte si načrte"; /* Hero Text for Personal information removal */ -"subscription.pir.hero" = "Activate Privacy Pro on desktop to set up Personal Information Removal"; +"subscription.pir.hero" = "Aktivirajte Privacy Pro na namizju, da nastavite Personal Information Removal"; /* Description on how to use Personal information removal in desktop. The first placeholder references a location in the Desktop application. Privacy Pro>, and the second, the menu entry. i.e. */ -"subscription.pir.heroText" = "In the DuckDuckGo browser for desktop, go to %1$@ and click %2$@ to get started."; +"subscription.pir.heroText" = "V brskalniku DuckDuckGo za namizje pojdite na %1$@ in kliknite %2$@, da začnete."; /* Settings references a menu in the Desktop app, Privacy Pro, references our product name */ -"subscription.pir.heroTextLocation" = "Settings > Privacy Pro"; +"subscription.pir.heroTextLocation" = "Nastavitve > Privacy Pro"; /* Menu item for enabling Personal Information Removal on Desktop */ -"subscription.pir.heroTextMenyEntry" = "I have a subscription"; +"subscription.pir.heroTextMenyEntry" = "Imam naročnino"; /* Text for the 'macOS' button */ "subscription.pir.macos" = "Mac"; @@ -2476,28 +2638,28 @@ "subscription.pir.windows" = "Windows"; /* Progress view title when completing the purchase */ -"subscription.progress.view.completing.purchase" = "Completing purchase..."; +"subscription.progress.view.completing.purchase" = "Dokončujem nakup ..."; /* Progress view title when starting the purchase */ -"subscription.progress.view.purchasing.subscription" = "Purchase in progress..."; +"subscription.progress.view.purchasing.subscription" = "Nakup je v teku ..."; /* Progress view title when restoring past subscription purchase */ -"subscription.progress.view.restoring.subscription" = "Restoring subscription..."; +"subscription.progress.view.restoring.subscription" = "Obnavljanje naročnine ..."; /* Remove from this device button */ -"subscription.remove.from.device.button" = "Remove From This Device"; +"subscription.remove.from.device.button" = "Odstrani iz te naprave"; /* Remove from device confirmation dialog text */ -"subscription.remove.from.device.text" = "You will no longer be able to access your Privacy Pro subscription on this device. This will not cancel your subscription, and it will remain active on your other devices."; +"subscription.remove.from.device.text" = "V tej napravi ne boste mogli več dostopati do naročnine Privacy Pro. S tem ne boste preklicali naročnine, ki bo ostala aktivna v drugih napravah."; /* Remove from device confirmation dialog title */ -"subscription.remove.from.device.title" = "Remove from this device?"; +"subscription.remove.from.device.title" = "Želite odstraniti iz te naprave?"; /* Remove subscription button text */ -"subscription.remove.subscription" = "Remove Subscription"; +"subscription.remove.subscription" = "Odstranitev naročnine"; /* Remove subscription cancel button text */ -"subscription.remove.subscription.cancel" = "Cancel"; +"subscription.remove.subscription.cancel" = "Prekliči"; /* Button text for general error message */ "subscription.restore.backend.error.button" = "Nazaj na nastavitve"; @@ -2509,28 +2671,43 @@ "subscription.restore.backend.error.title" = "Nekaj je šlo narobe"; /* Alert for general error message */ -"subscription.restore.general.error.message" = "The App Store was unable to process your purchase. Please try again later."; +"subscription.restore.general.error.message" = "App Store ni mogel obdelati vašega nakupa. Poskusite znova pozneje."; /* Alert for general error title */ -"subscription.restore.general.error.title" = "Something Went Wrong"; +"subscription.restore.general.error.title" = "Nekaj je šlo narobe"; /* Alert button text for restored purchase alert */ -"subscription.restore.success.alert.button" = "OK"; +"subscription.restore.success.alert.button" = "V REDU"; /* Alert message for restored purchase */ -"subscription.restore.success.alert.message" = "Your purchases have been restored."; +"subscription.restore.success.alert.message" = "Vaši nakupi so bili obnovljeni."; /* Alert title for restored purchase */ -"subscription.restore.success.alert.title" = "You’re all set."; +"subscription.restore.success.alert.title" = "Vse je pripravljeno."; + +/* Subtitle in header when subscribed */ +"subscription.subscribed" = "Naročeni"; /* Subscription Expired Data. This reads as 'Your subscription expired on (date)' */ "subscription.subscription.expired.caption" = "Vaša naročnina se je iztekla dne %@"; -/* Subscription expiration info. This reads as 'Your (monthly or annual) subscription expires on (date)' */ -"subscription.subscription.expiring.caption" = "Your %1$@ subscription expires on %2$@."; +/* Monthly subscription expiration info where parameter is expiration date. This reads as 'Your monthly subscription expires on (date)' */ +"subscription.subscription.expiring.monthly.caption" = "Vaša mesečna naročnina poteče dne %@."; + +/* Unknown period subscription expiration info where parameter is expiration date. This reads as 'Your subscription expires on (date)' */ +"subscription.subscription.expiring.unknown.caption" = "Vaša naročnina poteče dne %@."; + +/* Annual subscription expiration info where parameter is expiration date. This reads as 'Your annual subscription expires on (date)' */ +"subscription.subscription.expiring.yearly.caption" = "Vaša letna naročnina poteče dne %@."; + +/* Monthly subscription renewal info where parameter is renewal date. This reads as 'Your monthly subscription renews on (date)' */ +"subscription.subscription.renewing.monthly.caption" = "Vaša mesečna naročnina se bo obnovila dne %@."; + +/* Unknown period subscription renewal info where parameter is renewal date. This reads as 'Your subscription renews on (date)' */ +"subscription.subscription.renewing.unknown.caption" = "Vaša naročnina se bo obnovila dne %@."; -/* Subscription renewal info. This reads as 'Your (monthly or annual) subscription renews on (date)' */ -"subscription.subscription.renewing.caption" = "Your %1$@ subscription renews on %2$@."; +/* Annual subscription renewal info where parameter is renewal date. This reads as 'Your annual subscription renews on (date)' */ +"subscription.subscription.renewing.yearly.caption" = "Vaša letna naročnina se bo obnovila dne %@."; /* Navigation bar Title for subscriptions */ "subscription.title" = "Privacy Pro"; @@ -2677,118 +2854,118 @@ "voiceSearch.footer.note.old" = "Zvok bo obdelan v napravi. Zvok ne bo shranjen in ne bo deljen z drugimi, niti z brskalnikom DuckDuckGo."; /* Cancel action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.cancel" = "Dismiss"; +"vpn.access-revoked.alert.action.cancel" = "Opusti"; /* Primary action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.subscribe" = "Subscribe"; +"vpn.access-revoked.alert.action.subscribe" = "Naročite se"; /* Alert message for the alert when the Privacy Pro subscription expiress */ -"vpn.access-revoked.alert.message" = "Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"vpn.access-revoked.alert.message" = "Naročite se na Privacy Pro, da znova povežete DuckDuckGo VPN."; /* Alert title for the alert when the Privacy Pro subscription expires */ -"vpn.access-revoked.alert.title" = "VPN disconnected due to expired subscription"; +"vpn.access-revoked.alert.title" = "VPN je prekinjen zaradi potekle naročnine."; /* Title for the VPN widget onboarding screen */ "vpn.addWidget.settings.title" = "Poiščite in izberite DuckDuckGo. Nato povlecite do VPN in izberite Dodaj pripomoček."; /* Title for the Cancel button of the VPN feedback form */ -"vpn.feedback-form.button.cancel" = "Cancel"; +"vpn.feedback-form.button.cancel" = "Prekliči"; /* Title for the Done button of the VPN feedback form */ -"vpn.feedback-form.button.done" = "Done"; +"vpn.feedback-form.button.done" = "Končano"; /* Title for the Submit button of the VPN feedback form */ -"vpn.feedback-form.button.submit" = "Submit"; +"vpn.feedback-form.button.submit" = "Potrdi"; /* Title for the Submitting state of the VPN feedback form */ -"vpn.feedback-form.button.submitting" = "Submitting…"; +"vpn.feedback-form.button.submitting" = "Pošiljanje ..."; /* Title for the browser crash/freeze category of the VPN feedback form */ -"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN causes browser to crash or freeze"; +"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN povzroči, da se brskalnik sesuje ali zamrzne"; /* Title for the 'VPN fails to connect' category of the VPN feedback form */ -"vpn.feedback-form.category.fails-to-connect" = "VPN fails to connect"; +"vpn.feedback-form.category.fails-to-connect" = "VPN se ne poveže"; /* Title for the 'VPN feature request' category of the VPN feedback form */ -"vpn.feedback-form.category.feature-request" = "VPN feature request"; +"vpn.feedback-form.category.feature-request" = "Zahteva za funkcijo VPN"; /* Title for the category 'VPN causes issues with other apps or websites' category of the VPN feedback form */ -"vpn.feedback-form.category.issues-with-apps" = "VPN causes issues with other apps or websites"; +"vpn.feedback-form.category.issues-with-apps" = "VPN povzroča težave z drugimi aplikacijami ali spletnimi mesti"; /* Title for the local device connectivity category of the VPN feedback form */ -"vpn.feedback-form.category.local-device-connectivity" = "VPN won't let me connect to local device"; +"vpn.feedback-form.category.local-device-connectivity" = "VPN mi ne omogoča povezave z lokalno napravo"; /* Title for the 'other VPN feedback' category of the VPN feedback form */ -"vpn.feedback-form.category.other" = "Other VPN feedback"; +"vpn.feedback-form.category.other" = "Druge povratne informacije o omrežju VPN"; /* Title for the category selection state of the VPN feedback form */ -"vpn.feedback-form.category.select-category" = "Select a category"; +"vpn.feedback-form.category.select-category" = "IZBERITE KATEGORIJO"; /* Title for the 'VPN is too slow' category of the VPN feedback form */ -"vpn.feedback-form.category.too-slow" = "VPN connection is too slow"; +"vpn.feedback-form.category.too-slow" = "Povezava VPN je prepočasna"; /* Title for the 'unable to install' category of the VPN feedback form */ -"vpn.feedback-form.category.unable-to-install" = "Unable to install VPN"; +"vpn.feedback-form.category.unable-to-install" = "Ni mogoče namestiti VPN"; /* Title for the feedback sent view description of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.description" = "Your feedback will help us improve the\nDuckDuckGo VPN."; +"vpn.feedback-form.sending-confirmation.description" = "Vaše povratne informacije nam bodo pomagale izboljšati\nDuckDuckGo VPN."; /* Title for the feedback sending error text of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.error" = "We couldn't send your feedback right now, please try again."; +"vpn.feedback-form.sending-confirmation.error" = "Trenutno ne moremo poslati vaših povratnih informacij, poskusite znova."; /* Title for the feedback sent view title of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.title" = "Thank you!"; +"vpn.feedback-form.sending-confirmation.title" = "Hvala!"; /* Toast message when the VPN feedback form is submitted successfully */ -"vpn.feedback-form.submitted.message" = "Thank You! Feedback submitted."; +"vpn.feedback-form.submitted.message" = "Hvala! Povratne informacije so poslane."; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-1" = "Please describe what's happening, what you expected to happen, and the steps that led to the issue:"; +"vpn.feedback-form.text-1" = "Opišite, kaj se dogaja, kaj ste pričakovali, da se bo zgodilo, in ukrepe, ki so pripeljali do težave:"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-2" = "In addition to the details entered into this form, your app issue report will contain:"; +"vpn.feedback-form.text-2" = "Poleg podrobnosti, vnesenih v ta obrazec, bo poročilo o težavi z aplikacijo vsebovalo tudi naslednje:"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-3" = "• Whether specific DuckDuckGo features are enabled"; +"vpn.feedback-form.text-3" = "• ali so omogočene specifične funkcije DuckDuckGo,"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-4" = "• Aggregate DuckDuckGo app diagnostics"; +"vpn.feedback-form.text-4" = "• zbiranje diagnostike aplikacije DuckDuckGo."; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-5" = "By tapping \"Submit\" I agree that DuckDuckGo may use the information in this report for purposes of improving the app's features."; +"vpn.feedback-form.text-5" = "Z dotikom možnosti »Pošlji« se strinjam, da lahko DuckDuckGo podatke iz tega poročila uporabi za namene izboljšanja funkcij aplikacije."; /* Title for each screen of the VPN feedback form */ -"vpn.feedback-form.title" = "Help Improve the DuckDuckGo VPN"; +"vpn.feedback-form.title" = "Pomagajte izboljšati DuckDuckGo VPN"; /* VPN settings screen cell text for adding the VPN widget to the home screen */ "vpn.settings.add.widget" = "Dodajte pripomoček VPN na začetni zaslon"; /* Disclaimer for the DNS Server section on the DNS Server screen */ -"vpn.settings.dns.section-disclaimer" = "Using a custom DNS server can impact browsing speeds and expose your activity to third parties if the server isn't secure or reliable."; +"vpn.settings.dns.section-disclaimer" = "Uporaba strežnika DNS po meri lahko vpliva na hitrost brskanja, če strežnik ni varen ali zanesljiv, pa lahko vaše dejavnosti razkrije tretjim osebam."; /* Header text for the DNS section on the VPN Settings screen */ "vpn.settings.dns.section-header" = "DNS"; /* Title for the Apply button on the DNS Server setting screen */ -"vpn.settings.dns.server.apply.button.title" = "Apply"; +"vpn.settings.dns.server.apply.button.title" = "Uporabi"; /* Default value for the DNS Server row on the VPN Settings screen */ "vpn.settings.dns.server.default.value" = "DuckDuckGo"; /* Title for the IPv4 Address setting */ -"vpn.settings.dns.server.ipv4.title" = "IPv4 Address"; +"vpn.settings.dns.server.ipv4.title" = "Naslov IPv4"; /* Custom option for the DNS Server setting */ -"vpn.settings.dns.server.option.custom" = "Custom"; +"vpn.settings.dns.server.option.custom" = "Druga"; /* Recommended option for the DNS Server setting */ -"vpn.settings.dns.server.option.default" = "DuckDuckGo (Recommended)"; +"vpn.settings.dns.server.option.default" = "DuckDuckGo (priporočeno)"; /* Title for the DNS Server setting screen */ -"vpn.settings.dns.server.screen.title" = "DNS Server"; +"vpn.settings.dns.server.screen.title" = "Strežnik DNS"; /* Title for the DNS Server row on the VPN Settings screen */ -"vpn.settings.dns.server.title" = "DNS Server"; +"vpn.settings.dns.server.title" = "Strežnik DNS"; /* Title for the button to enable push notifications in system settings */ "waitlist.allow-notifications" = "Dovoli obvestila"; diff --git a/DuckDuckGo/sl.lproj/OmniBar.strings b/DuckDuckGo/sl.lproj/OmniBar.strings index 796c5ce2e6..b978bbeb88 100644 --- a/DuckDuckGo/sl.lproj/OmniBar.strings +++ b/DuckDuckGo/sl.lproj/OmniBar.strings @@ -7,6 +7,9 @@ /* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "Dt8-We-5tV"; */ "Dt8-We-5tV.accessibilityLabel" = "Počisti besedilo"; +/* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "gb5-wL-4OW"; */ +"gb5-wL-4OW.accessibilityLabel" = "Počisti besedilo"; + /* Class = "UIButton"; accessibilityLabel = "Share"; ObjectID = "hor-Jd-3kw"; */ "hor-Jd-3kw.accessibilityLabel" = "Deli"; diff --git a/DuckDuckGo/sl.lproj/Settings.strings b/DuckDuckGo/sl.lproj/Settings.strings index c4ef486b97..b9742d2f47 100644 --- a/DuckDuckGo/sl.lproj/Settings.strings +++ b/DuckDuckGo/sl.lproj/Settings.strings @@ -10,9 +10,6 @@ /* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "5YD-Od-uDU"; */ "5YD-Od-uDU.text" = "Samodejno počisti podatke"; -/* Class = "UIButton"; normalTitle = "Settings"; ObjectID = "8YG-7Q-Czd"; */ -"8YG-7Q-Czd.normalTitle" = "Nastavitve"; - /* Class = "UILabel"; text = "App Exit, Inactive for 1 Hour"; ObjectID = "9EC-lx-77p"; */ "9EC-lx-77p.text" = "Izhod iz aplikacije, neaktivnost 1 ura"; @@ -76,9 +73,6 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Nezaščitene strani"; -/* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ -"ssa-zd-L3T.title" = "Velikost besedila"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Pokaži tipkovnico v/pri"; diff --git a/DuckDuckGo/sv.lproj/DaxOnboarding.strings b/DuckDuckGo/sv.lproj/DaxOnboarding.strings index 22d8e2f74b..15d11f56f1 100644 --- a/DuckDuckGo/sv.lproj/DaxOnboarding.strings +++ b/DuckDuckGo/sv.lproj/DaxOnboarding.strings @@ -1,12 +1,6 @@ /* Class = "UIButton"; normalTitle = "Hide"; ObjectID = "hgy-75-OvJ"; */ "hgy-75-OvJ.normalTitle" = "Dölj"; -/* Class = "UILabel"; text = "Welcome to\nDuckDuckGo!"; ObjectID = "Lvy-9G-3gr"; */ -"Lvy-9G-3gr.text" = "Välkommen till\nDuckDuckGo!"; - -/* Class = "UIButton"; normalTitle = "Let’s Do It!"; ObjectID = "oAv-Zt-uIt"; */ -"oAv-Zt-uIt.normalTitle" = "Vi kör!"; - /* Class = "UIButton"; normalTitle = "Button"; ObjectID = "zJt-F9-L48"; */ "zJt-F9-L48.normalTitle" = "Knapp"; diff --git a/DuckDuckGo/sv.lproj/Localizable.strings b/DuckDuckGo/sv.lproj/Localizable.strings index 0f5c7a6eb4..77e833357c 100644 --- a/DuckDuckGo/sv.lproj/Localizable.strings +++ b/DuckDuckGo/sv.lproj/Localizable.strings @@ -341,7 +341,7 @@ "autofill.keep-enabled.alert.disable" = "Inaktivera"; /* A link that takes the user to the DuckDuckGo help pages explaining password managers */ -"autofill.learn.more.link.title" = "Learn More"; +"autofill.learn.more.link.title" = "Läs mer"; /* Button displayed after saving/updating an autofill login that takes the user to the saved login */ "autofill.login-save-action-button.toast" = "Visa"; @@ -977,9 +977,6 @@ /* ad = advertisment */ "dax.onboarding.home.subsequent" = "Du klarar det här!\n\nKom ihåg: varje gång du surfar med mig förlorar en läskig annons sina vingar. 👍"; -/* No comment provided by engineer. */ -"dax.onboarding.message" = "Internet kan vara lite läskigt.\n\nOroa dig inte! Att söka och surfa privat är lättare än du tror."; - /* No comment provided by engineer. */ "Debug" = "Felsökning"; @@ -1080,7 +1077,7 @@ "duckPlayer.never.label" = "Aldrig"; /* Body text for the modal feature explanation */ -"duckplayer.presentation.modal.body" = "Med Duck Player kan du titta på YouTube utan riktade annonser i en teaterliknande upplevelse i DuckDuckGo, och dina rekommendationer påverkas inte av vad du tittar på."; +"duckplayer.presentation.modal.body" = "Med Duck Player kan du titta på YouTube utan riktade annonser i DuckDuckGo, och dina rekommendationer påverkas inte av vad du tittar på."; /* Button that will dismiss the modal */ "duckplayer.presentation.modal.dismiss-button" = "Jag förstår!"; @@ -1088,17 +1085,23 @@ /* Two line title (separated by \n) for the feature explanation */ "duckplayer.presentation.modal.title" = "Drunknar du i annonser på YouTube? Prova Duck Player!"; +/* Two line title (separated by \n) for the feature explanation */ +"duckplayer.presentation.modal.title.youtube" = "Drunknar du i annonser på YouTube? Inte med Duck Player!"; + /* Footer label in the settings screen for Duck Player */ "duckplayer.settings.footer" = "DuckDuckGo tillhandahåller allt du behöver för att skydda dig själv när du surfar på webben."; /* Text explaining what Duck Player is in the settings screen. */ -"duckplayer.settings.info-text" = "Duck Player ger en störningsfri visningsupplevelse utan personliga annonser och förhindrar att din tittaraktivitet påverkar YouTube-rekommendationer."; +"duckplayer.settings.info-text" = "Med Duck Player kan du titta på YouTube utan riktade annonser i DuckDuckGo, och dina rekommendationer påverkas inte av vad du tittar på."; /* Button that takes the user to learn more about Duck Player. */ "duckplayer.settings.learn-more" = "Läs mer"; +/* Settings screen cell text for DuckPlayer settings to open in new tab */ +"duckplayer.settings.open-new-tab-label" = "Öppna Duck Player på en ny flik"; + /* Settings screen cell text for DuckPlayer settings */ -"duckplayer.settings.open-videos-in" = "Öppna videor i Duck Player"; +"duckplayer.settings.open-videos-in" = "Öppna YouTube-videor i Duck Player"; /* Settings screen cell text for DuckPlayer settings */ "duckplayer.settings.title" = "Duck Player"; @@ -1169,9 +1172,6 @@ /* Title for prompt to sign up for email protection */ "email.signup-prompt.title" = "Dölj din e-postadress och\nblockera spårare"; -/* No comment provided by engineer. */ -"empty!" = "tom!"; - /* Empty list state placholder */ "empty.bookmarks" = "Inga bokmärken har lagts till ännu"; @@ -1322,6 +1322,39 @@ /* Confirmation button */ "feedback.form.submit" = "Skicka"; +/* Title of the feedback form */ +"feedback.form.title" = "Hjälp till att förbättra Privacy Pro"; + +/* Category for Identity Theft Restoration feedback */ +"feedback.general.category.itr" = "Identity Theft Restoration"; + +/* Category for Personal Info Removal feedback */ +"feedback.general.category.pir" = "Personal Info Removal"; + +/* Category for subscription and payments feedback */ +"feedback.general.category.ppro" = "Abonnemang och betalningar"; + +/* Prompt to select a category for general feedback */ +"feedback.general.category.select" = "VÄLJ EN KATEGORI"; + +/* Category for VPN feedback */ +"feedback.general.category.vpn" = "VPN"; + +/* Category for access code issues */ +"feedback.itr.category.access-code" = "Problem med åtkomstkod"; + +/* Category for issues contacting an advisor */ +"feedback.itr.category.cant-contact-advisor" = "Det går inte att kontakta rådgivaren"; + +/* Prompt to select a category for Identity Theft Restoration feedback */ +"feedback.itr.category.select" = "VÄLJ EN KATEGORI"; + +/* Category for other Identity Theft Restoration issues */ +"feedback.itr.category.something-else" = "Övrigt"; + +/* Category for unhelpful advisor calls */ +"feedback.itr.category.unhelpful" = "Samtal till rådgivare var inte hjälpsamt"; + /* No comment provided by engineer. */ "feedback.negative.form.genericPlaceholder" = "Beskriv så noggrant som möjligt"; @@ -1361,6 +1394,24 @@ /* No comment provided by engineer. */ "feedback.performance.slowLoading" = "Webbsidor eller sökresultat laddas långsamt"; +/* Category for when scan finds incorrect records */ +"feedback.pir.category.not-me" = "Skanningen hittade poster som inte är mina"; + +/* Category for when scan doesn't find info on a specific site */ +"feedback.pir.category.nothing-on-site" = "Skanningen hittade inte mina uppgifter på en specifik webbplats"; + +/* Category for other Personal Info Removal issues */ +"feedback.pir.category.other" = "Övrigt"; + +/* Category for when the removal process is stuck */ +"feedback.pir.category.removal-stuck" = "Borttagningsprocessen har fastnat"; + +/* Category for when the scan is stuck */ +"feedback.pir.category.scan-stuck" = "Skanningen efter poster har fastnat"; + +/* Prompt to select a category for Personal Info Removal feedback */ +"feedback.pir.category.select" = "VÄLJ EN KATEGORI"; + /* Header above input field */ "feedback.positive.form.header" = "Berätta mer om dina upplevelser"; @@ -1379,6 +1430,15 @@ /* Button encouraging uses to share details aboout their feedback */ "feedback.positive.submit" = "Berätta mer om dina upplevelser"; +/* Category for other Privacy Pro issues */ +"feedback.ppro.category.other" = "Övrigt"; + +/* Category for one-time password issues */ +"feedback.ppro.category.otp" = "Problem med engångslösenord"; + +/* Prompt to select a category for Privacy Pro feedback */ +"feedback.ppro.category.select" = "VÄLJ EN KATEGORI"; + /* No comment provided by engineer. */ "feedback.start.footer" = "Din anonyma återkoppling är viktig för oss."; @@ -1529,12 +1589,6 @@ /* No comment provided by engineer. */ "keyCommandShowAllTabs" = "Visa alla flikar"; -/* Please preserve newline character */ -"launchscreenWelcomeMessage" = "Välkommen till\nDuckDuckGo!"; - -/* No comment provided by engineer. */ -"LOREM IPSUM" = "LOREM IPSUM"; - /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "DuckDuckGo för Mac har hastigheten du behöver, webbläsarfunktionerna du förväntar dig och våra många förstklassiga integritetsfunktioner."; @@ -1586,62 +1640,83 @@ /* Edit button */ "navigation.title.edit" = "Redigera"; -/* String indicating NetP is connected when viewed from the settings screen */ -"netP.cell.connected" = "Connected"; - -/* String indicating NetP is disconnected when viewed from the settings screen */ -"netP.cell.disconnected" = "Not connected"; - -/* Title for the DuckDuckGo VPN feature in settings */ -"netP.settings.title" = "VPN"; +/* String indicating NetP is snoozing when viewed from the settings screen */ +"netP.cell.snoozing" = "Snoozar"; /* Title for the DuckDuckGo VPN feature */ "netP.title" = "DuckDuckGo VPN"; +/* Button title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.action" = "Lägg till widget"; + +/* Message for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.message" = "Slå på och av VPN direkt från startskärmen."; + +/* Title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.title" = "Lägg till VPN-widget"; + /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN har kopplats från på grund av att abonnemanget har gått ut. Prenumerera på Privacy Pro för att återansluta DuckDuckGo VPN."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Nätverksskyddet kunde inte ansluta. Försök igen senare."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "DuckDuckGo VPN kunde inte ansluta. Försök igen senare."; /* Title for the VPN FAQ screen. */ "network.protection.faq.title" = "Vanliga frågor om DuckDuckGo VPN"; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Nätverksskyddet avbröts. Försöker återuppta kontakten nu..."; +/* Message for tooltip about geoswitching */ +"network.protection.geoswitching.tip.message" = "Du kan anpassa din VPN-plats genom att ansluta till någon av våra servrar som finns över hela världen."; + +/* Title for tooltip about geoswitching */ +"network.protection.geoswitching.tip.title" = "Ändra din plats"; + +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "DuckDuckGo VPN avbröts. Försöker återuppta kontakten nu ..."; /* Message for the network protection invite dialog */ -"network.protection.invite.dialog.message" = "Enter your invite code to get started."; +"network.protection.invite.dialog.message" = "Ange din inbjudningskod för att komma igång."; /* Title for the network protection invite screen */ "network.protection.invite.dialog.title" = "Du är inbjuden att prova DuckDuckGo VPN"; /* Prompt for the network protection invite code text field */ -"network.protection.invite.field.prompt" = "Invite Code"; +"network.protection.invite.field.prompt" = "Inbjudningskod"; /* Message for the network protection invite success view */ -"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; +"network.protection.invite.success.message" = "Dölj din plats från webbtjänster och dölj din onlineaktivitet från internetleverantörer och andra som är på samma nätverk."; /* Title for the network protection invite success view */ -"network.protection.invite.success.title" = "Success! You’re in."; +"network.protection.invite.success.title" = "Klart! Du är inne."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; /* Title text for an iOS quick action that opens VPN settings */ "network.protection.quick-action.open-vpn" = "Öppna VPN"; +/* Button title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.action" = "Läs mer"; + +/* Message for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.message" = "Du pausa VPN-anslutningen för att använda webbplatser eller appar som blockerar VPN-trafik."; + +/* Title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.title" = "Undvik VPN-konflikter"; + +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN har pausats i %@"; + /* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ "network.protection.status.connected.format" = "Ansluten · %@"; /* The label for the NetP VPN when connecting */ -"network.protection.status.connecting" = "Connecting..."; +"network.protection.status.connecting" = "Ansluter …"; /* The label for the NetP VPN when disconnected */ -"network.protection.status.disconnected" = "Not connected"; +"network.protection.status.disconnected" = "Ej ansluten"; /* The label for the NetP VPN when disconnecting */ -"network.protection.status.disconnecting" = "Disconnecting..."; +"network.protection.status.disconnecting" = "Kopplar från ..."; /* Message label text for the status view when VPN is disconnected */ "network.protection.status.header.message.off" = "Anslut för att säkra all internettrafik\nför din enhet."; @@ -1655,26 +1730,41 @@ /* Header title label text for the status view when VPN is connected */ "network.protection.status.header.title.on" = "DuckDuckGo VPN är På"; +/* Header title label text for the status view when VPN is snoozing */ +"network.protection.status.header.title.snoozed" = "DuckDuckGo VPN har pausats"; + /* The status view 'Share Feedback' button which is shown inline on the status view after the temporary free use footer text */ -"network.protection.status.menu.share.feedback" = "Share Feedback"; +"network.protection.status.menu.share.feedback" = "Berätta vad du tycker"; + +/* The label for the NetP VPN when paused */ +"network.protection.status.paused" = "Pausad"; + +/* The label for when NetP VPN is snoozing plus the length of time remaining formatted as '0:00' */ +"network.protection.status.snoozing.format" = "Snoozar, %@ kvar"; + +/* Snooze button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.snooze" = "Snooza i 20 minuter"; + +/* Wake Up button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.wake-up" = "Väck"; /* Connection details label shown in NetworkProtection's status view. */ -"network.protection.status.view.connection.details" = "Connection Details"; +"network.protection.status.view.connection.details" = "Anslutningsinformation"; /* Custom DNS label shown in NetworkProtection's status view. */ -"network.protection.status.view.custom.dns" = "DNS Server"; +"network.protection.status.view.custom.dns" = "DNS-server"; /* Generic connection failed error message shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.message" = "Please try again later."; +"network.protection.status.view.error.connection.failed.message" = "Försök igen senare."; /* Generic connection failed error title shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; +"network.protection.status.view.error.connection.failed.title" = "Det gick inte att ansluta."; /* IP Address label shown in NetworkProtection's status view. */ -"network.protection.status.view.ip.address" = "IP Address"; +"network.protection.status.view.ip.address" = "IP-adress"; /* Location label shown in NetworkProtection's status view. */ -"network.protection.status.view.location" = "Location"; +"network.protection.status.view.location" = "Plats"; /* Label shown on the title of the settings section in NetworkProtection's status view. */ "network.protection.status.view.settings.section.title" = "Hantera"; @@ -1682,12 +1772,15 @@ /* Title label text for the status view when netP is disconnected */ "network.protection.status.view.title" = "VPN"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Nätverksskydd är På. Din plats och onlineaktivitet är skyddad."; +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "DuckDuckGo VPN är på. Din plats och onlineaktivitet är skyddad."; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Dirigera enhetstrafik genom %@."; +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "VPN-pausen har avslutats. Dirigerar enhetstrafik via %@."; + /* Title for the button to link to the iOS app settings and enable notifications app-wide. */ "network.protection.turn.on.notifications.button.title" = "Aktivera aviseringar"; @@ -1698,13 +1791,13 @@ "network.protection.vpn.about" = "Om"; /* Section header for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.section.header" = "Notifications"; +"network.protection.vpn.alerts.section.header" = "Meddelanden"; /* List section footer for the toggle for VPN alerts. */ "network.protection.vpn.alerts.toggle.section.footer" = "Få en avisering om din anslutning bryts eller om VPN-statusen ändras."; /* Title for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.toggle.title" = "VPN Notifications"; +"network.protection.vpn.alerts.toggle.title" = "VPN-aviseringar"; /* Title for the data volume section in the VPN status screen */ "network.protection.vpn.data-volume" = "Datavolym"; @@ -1713,13 +1806,13 @@ "network.protection.vpn.exclude.local.networks.setting.footer" = "Låt lokal trafik kringgå VPN och ansluta till enheter i ditt lokala nätverk, t.ex. skrivare."; /* Header text for the Exclude Local Networks setting item. */ -"network.protection.vpn.exclude.local.networks.setting.header" = "General"; +"network.protection.vpn.exclude.local.networks.setting.header" = "Allmänt"; /* Title for the Exclude Local Networks setting item. */ "network.protection.vpn.exclude.local.networks.setting.title" = "Uteslut lokala nätverk"; /* Title for the VPN Location screen's All Countries section. */ -"network.protection.vpn.location.all.countries.section.title" = "All Countries"; +"network.protection.vpn.location.all.countries.section.title" = "Alla länder"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.connected" = "Ansluten plats"; @@ -1734,7 +1827,7 @@ "network.protection.vpn.location.recommended.section.footer" = "Anslut automatiskt till den närmaste servern vi kan hitta."; /* Title for the VPN Location screen's Recommended section. */ -"network.protection.vpn.location.recommended.section.title" = "Recommended"; +"network.protection.vpn.location.recommended.section.title" = "Rekommenderas"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.selected" = "Vald plats"; @@ -1752,13 +1845,13 @@ "network.protection.vpn.preferred.location.nearest" = "Närmaste plats"; /* Title for the Preferred Location VPN Settings item. */ -"network.protection.vpn.preferred.location.title" = "Preferred Location"; +"network.protection.vpn.preferred.location.title" = "Önskad plats"; /* Footer text for the DNS server setting item. */ -"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo routes DNS queries through our DNS servers so your internet provider can't see what websites you visit."; +"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo dirigerar DNS-förfrågningar via våra DNS-servrar så att din internetleverantör inte kan se vilka webbplatser du besöker."; /* Title for the FAQ row in the VPN status screen. */ -"network.protection.vpn.settings.faq" = "FAQs and Support"; +"network.protection.vpn.settings.faq" = "Vanliga frågor och support"; /* Title for the feedback row in the VPN status screen. */ "network.protection.vpn.settings.share-feedback" = "Dela VPN-feedback"; @@ -1940,6 +2033,66 @@ /* Deactivate button */ "pm.deactivate" = "Inaktivera"; +/* Label for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.label" = "E-postadress (valfritt)"; + +/* Placeholder for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.placeholder" = "namn@epost.com"; + +/* Placeholder for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.placeholder" = "Ge din feedback ..."; + +/* Title for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.title" = "Allmän feedback"; + +/* Title for the ITR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-itr-problem.title" = "Identity Theft Restoration"; + +/* Title for the PIR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-pir-problem.title" = "Personal Information Removal"; + +/* Title for the Subscriptions and Payments category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-ppro-problem.title" = "Abonnemang och betalningar"; + +/* Placeholder for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.placeholder" = "Berätta för oss vad som händer …"; + +/* Title for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.title" = "Rapportera ett problem"; + +/* Title for the VPN category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-vpn-problem.title" = "VPN"; + +/* Placeholder for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.placeholder" = "Vilken funktion vill du ha?"; + +/* Title for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.title" = "Utvecklingsförfrågan"; + +/* Title for the category selection section in the Privacy Pro feedback form */ +"ppro.feedback-form.select-category.title" = "VÄLJ EN KATEGORI"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-1" = "Har du hittat ett problem som inte täcks av vårt [hjälpcenter](duck://)? Vi vill definitivt veta mer om det.\n\nAnge en e-postadress om du vill att vi kontaktar dig angående problemet (vi kanske inte kan svara på alla problem):"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-2" = "Förutom de uppgifter som anges ovan skickar vi viss anonymiserad information med din feedback:"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-3" = "• Om vilka webbläsarfunktioner som är aktiva"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-4" = "• Sammanställd appdiagnostik (t.ex. felkoder)"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-5" = "Genom att trycka på ”Skicka” godkänner du att DuckDuckGo får använda den inskickade informationen för att förbättra appen."; + +/* Title for the text box in the Privacy Pro feedback form */ +"ppro.feedback-form.text-box.title" = "Feedback"; + +/* Title for the Privacy Pro feedback form */ +"ppro.feedback-form.title" = "Skicka återkoppling"; + /* Button title for sync bookmarks limits exceeded warning to go to manage bookmarks */ "prefrences.sync.bookmarks-limit-exceeded-action" = "Hantera bokmärken"; @@ -2029,6 +2182,18 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Favoriter"; +/* Name of the option the user can chose to give general browser feedback */ +"send.browser.feedback.general-feedback" = "Allmän feedback"; + +/* Name of the option the user can chose to give browser feedback about a problem they enountered */ +"send.browser.feedback.report-problem" = "Rapportera ett problem"; + +/* Name of the option the user can chose to give browser feedback about a feature they would like */ +"send.browser.feedback.request-feature" = "Begär en funktion"; + +/* Title of the picker where the user can chose the category of the feedback they want ot send. */ +"send.browser.feedback.select-category" = "VÄLJ EN KATEGORI"; + /* Settings cell for About DDG */ "settings.about.ddg" = "Om DuckDuckGo"; @@ -2084,6 +2249,9 @@ /* Section footer Autolock description */ "settings.autolock.description" = "Om Touch ID, Face ID eller ett systemlösenord har aktiverats blir du ombedd att låsa upp appen när du öppnar den."; +/* Settings cell for Browser Feedback */ +"settings.browser.feedback" = "Webbläsarfeedback"; + /* Settings screen cell text for Automatically Clearing Data */ "settings.clear.data" = "Rensa data automatiskt"; @@ -2182,7 +2350,7 @@ "settings.ppro" = "Privacy Pro"; /* Title for Link in the Footer of Privacy Pro section */ -"settings.ppro.footer" = "Privacy Policy and Terms of Service"; +"settings.ppro.footer" = "Integritetspolicy och användarvillkor"; /* Settings screen cell for long press previews */ "settings.previews" = "Förhandsvisning vid nedhållning"; @@ -2208,23 +2376,20 @@ /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Ange din adressfältsposition"; -/* Subscription activation pending description */ -"settings.subscription.activation.pending.description" = "This is taking longer than usual, please check back later."; +/* Privacy pro description subtitle in settings when the is activating */ +"settings.subscription.activating" = "Aktiverar"; -/* Subscription activation pending title */ -"settings.subscription.activation.pending.title" = "Din prenumeration håller på att aktiveras"; +/* Subscription activation pending description */ +"settings.subscription.activation.pending.description" = "Det här tar längre tid än vanligt, kom tillbaka senare."; /* Data Broker protection cell subtitle for privacy pro */ -"settings.subscription.DBP.subtitle" = "Remove your info from sites that sell it"; +"settings.subscription.DBP.subtitle" = "Ta bort dina uppgifter från webbplatser som säljer dem"; /* Data Broker protection cell title for privacy pro */ "settings.subscription.DBP.title" = "Personal Information Removal"; -/* Privacy pro description subtext */ -"settings.subscription.description" = "Mer sömlös integritet med tre nya skydd:"; - /* I have a Subscription button text for privacy pro */ -"settings.subscription.existing.subscription" = "I Have a Subscription"; +"settings.subscription.existing.subscription" = "Jag har ett abonnemang"; /* Subscription expired description */ "settings.subscription.expired.comment" = "Prenumerera igen för att fortsätta använda Privacy Pro"; @@ -2232,23 +2397,26 @@ /* Subscription expired tittle message */ "settings.subscription.expired.title" = "Din prenumeration på Privacy Pro har gått ut"; -/* Privacy pro features list */ -"settings.subscription.features" = " • VPN\n • Personal Information Removal\n • Identity Theft Restoration"; - /* Identity theft restoration cell subtitle for privacy pro */ -"settings.subscription.ITR.subtitle" = "If your identity is stolen, we'll help restore it"; +"settings.subscription.ITR.subtitle" = "Om din identitet blir stulen hjälper vi dig att återställa den"; /* Identity theft restoration cell title for privacy pro */ "settings.subscription.ITR.title" = "Identity Theft Restoration"; /* Get Privacy Pro button text for privacy pro */ -"settings.subscription.learn.more" = "Get Privacy Pro"; +"settings.subscription.learn.more" = "Skaffa Privacy Pro"; /* Subscription Settings button text for privacy pro */ -"settings.subscription.manage" = "Subscription Settings"; +"settings.subscription.manage" = "Abonnemangsinställningar"; + +/* Privacy Pro description subtitle in settings */ +"settings.subscription.row.description" = "Inkluderar vårt VPN och Identity Theft Restoration."; -/* Call to action title for Privacy Pro */ -"settings.subscription.subscribe" = "Subscribe to Privacy Pro"; +/* Call to action title for Privacy Pro settings */ +"settings.subscription.subscribe" = "Skydda din anslutning och identitet med Privacy Pro"; + +/* Privacy pro description subtitle in settings */ +"settings.subscription.us.description" = "Det inkluderar vårt VPN, Personal Information Removal och Identity Theft Restoration."; /* VPN cell title for privacy pro */ "settings.subscription.VPN.title" = "VPN"; @@ -2317,67 +2485,64 @@ "siteFeedback.urlPlaceholder" = "Vilken webbplats är skadad?"; /* Subscription Activation Info */ -"subscription.activate..header.description" = "Access your Privacy Pro subscription on this device via Apple ID or an email address."; +"subscription.activate..header.description" = "Kom åt ditt Privacy Pro-abonnemang på den här enheten via Apple-ID eller en e-postadress."; /* Button for adding email address to subscription */ -"subscription.activate.add.email.button" = "Add Email"; +"subscription.activate.add.email.button" = "Lägg till e-postadress"; /* Apple ID option for activation */ -"subscription.activate.appleid" = "Apple ID"; +"subscription.activate.appleid" = "Apple-ID"; /* Button text for restoring purchase via Apple ID */ -"subscription.activate.appleid.button" = "Restore Purchase"; +"subscription.activate.appleid.button" = "Återställ köp"; /* Description for Apple ID activation */ -"subscription.activate.appleid.description" = "Restore your purchase to activate your subscription on this device."; +"subscription.activate.appleid.description" = "Återställ ditt köp för att aktivera abonnemanget på den här enheten."; /* Subscription Activation Info */ -"subscription.activate.description" = "Your subscription is automatically available in DuckDuckGo on any device signed in to your Apple ID."; +"subscription.activate.description" = "Ditt abonnemang är automatiskt tillgängligt i DuckDuckGo på alla enheter som är inloggade på ditt Apple-ID."; /* Button for editing email address added to subscription */ -"subscription.activate.edit.email.button" = "Edit Email"; +"subscription.activate.edit.email.button" = "Redigera e-postadress"; /* View Title for editing your email account */ -"subscription.activate.edit.email.title" = "Edit Email"; +"subscription.activate.edit.email.title" = "Redigera e-postadress"; /* Email option for activation */ -"subscription.activate.email" = "Email"; +"subscription.activate.email" = "E-post"; /* Restore button title for Email */ -"subscription.activate.email.button" = "Enter Email"; +"subscription.activate.email.button" = "Ange e-postadress"; /* Description for Email activation */ -"subscription.activate.email.description" = "Use your email to activate your subscription on this device."; +"subscription.activate.email.description" = "Använd din e-postadress för att aktivera abonnemanget på den här enheten."; /* Activate subscription title */ "subscription.activate.email.title" = "Aktivera abonnemang"; /* Button title for cancelling email deletion */ -"subscription.activate.manage.email.cancel" = "Cancel"; +"subscription.activate.manage.email.cancel" = "Avbryt"; /* Button title for confirming email deletion */ "subscription.activate.manage.email.OK" = "OK"; /* Restore button title for AppleID */ -"subscription.activate.restore.apple" = "Restore Purchase"; +"subscription.activate.restore.apple" = "Återställ köp"; /* Subscription Activation Title */ -"subscription.activate.title" = "Activate your subscription on this device"; +"subscription.activate.title" = "Aktivera ditt abonnemang på den här enheten"; /* Resend activation instructions button */ -"subscription.add.device.resend.instructions" = "Resend Instructions"; +"subscription.add.device.resend.instructions" = "Skicka instruktionerna igen"; /* Add email to an existing subscription */ -"subscription.add.email" = "Add an email address to activate your subscription on your other devices. We’ll only use this address to verify your subscription."; +"subscription.add.email" = "Lägg till en e-postadress för att aktivera abonnemanget på dina andra enheter. Vi använder enbart adressen för att bekräfta abonnemanget."; /* View title for adding email to subscription */ -"subscription.add.email.title" = "Add Email"; - -/* Title for Alert messages */ -"subscription.alert.title" = "subscription.alert.title"; +"subscription.add.email.title" = "Lägg till e-postadress"; /* Subscription availability message on Apple devices */ -"subscription.available.apple" = "Privacy Pro is available on any device signed in to the same Apple ID."; +"subscription.available.apple" = "Privacy Pro är tillgängligt på alla enheter som är inloggade med samma Apple-ID."; /* Text for the manage billing page */ "subscription.billing.google.text" = "Ditt abonnemang köptes genom Google Play-butiken. För att förnya abonnemanget öppnar du abonnemangsinställningarna för Google Play-butiken på en enhet som är inloggad på samma Google-konto som användes för att köpa ditt abonnemang."; @@ -2385,89 +2550,86 @@ /* Title for the manage billing page */ "subscription.billing.google.title" = "Abonnemangstyper"; -/* Subscription annual billing period type */ -"subscription.billing.period.annual" = "annual"; - -/* Subscription monthly billing period type */ -"subscription.billing.period.monthly" = "monthly"; - /* Subscription Removal confirmation message */ -"subscription.cancel.message" = "Your subscription has been removed from this device."; +"subscription.cancel.message" = "Ditt abonnemang har tagits bort från den här enheten."; /* Change plan or cancel title */ -"subscription.change.plan" = "Update Plan or Cancel"; +"subscription.change.plan" = "Uppdatera abonnemang eller avbryt"; /* Navigation Button for closing subscription view */ -"subscription.close" = "Close"; +"subscription.close" = "Stäng"; /* Title for Confirm messages */ -"subscription.confirm.title" = "Are you sure?"; +"subscription.confirm.title" = "Är du säker?"; /* Header for section for activating subscription on other devices */ -"subscription.devices.header" = "Activate on Other Devices"; +"subscription.devices.header" = "Aktivera på andra enheter"; /* Footer for section for activating subscription on other devices when email was not yet added */ -"subscription.devices.no.email.footer" = "Add an optional email to your subscription or use your Apple ID to access Privacy Pro on other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.no.email.footer" = "Lägg till en valfri e-postadress till ditt abonnemang för att få tillgång till Privacy Pro på andra enheter. **[Läs mer](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Footer for section for activating subscription on other devices when email is added */ -"subscription.devices.with.email.footer" = "Use this email to activate your subscription in Settings > Privacy Pro in the DuckDuckGo app on your other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.with.email.footer" = "Använd den här e-postadressen för att aktivera ditt abonnemang i Inställningar > Privacy Pro i DuckDuckGo-appen på dina andra enheter. **[Läs mer](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Alert content for not found subscription */ -"subscription.email.inactive.alert.message" = "The subscription associated with this email is no longer active."; +"subscription.email.inactive.alert.message" = "Abonnemanget som är kopplat till den här e-postadressen är inte längre aktivt."; /* Alert title for not found subscription */ -"subscription.email.inactive.alert.title" = "Subscription Not Found"; +"subscription.email.inactive.alert.title" = "Det gick inte att hitta abonnemanget"; /* Alert content for not found subscription */ -"subscription.expired.alert.message" = "The subscription associated with this Apple ID is no longer active."; +"subscription.expired.alert.message" = "Abonnemanget som är kopplat till detta Apple-ID är inte längre aktivt."; /* FAQ Button */ -"subscription.faq" = "FAQs and Support"; +"subscription.faq" = "Vanliga frågor och support"; /* FAQ Description */ -"subscription.faq.description" = "Get answers to frequently asked questions or contact Privacy Pro support from our help pages."; +"subscription.faq.description" = "Få svar på vanliga frågor eller kontakta supporten för Privacy Pro på våra hjälpsidor."; + +/* Send Feedback Button */ +"subscription.feedback" = "Skicka återkoppling"; /* Cancel action for the existing subscription dialog */ -"subscription.found.cancel" = "Cancel"; +"subscription.found.cancel" = "Avbryt"; /* Restore action for the existing subscription dialog */ -"subscription.found.restore" = "Restore"; +"subscription.found.restore" = "Återställ"; /* Message for the existing subscription dialog */ -"subscription.found.text" = "We found a subscription associated with this Apple ID."; +"subscription.found.text" = "Vi hittade ett abonnemang som är kopplat till detta Apple-ID."; /* Title for the existing subscription dialog */ -"subscription.found.title" = "Subscription Found"; +"subscription.found.title" = "Abonnemang hittades"; /* Help and support Section header */ -"subscription.help" = "Help and support"; +"subscription.help" = "Hjälp och support"; /* Manage Plan header */ -"subscription.manage.plan" = "Manage Plan"; +"subscription.manage.plan" = "Hantera abonnemang"; /* Header for the subscription section */ -"subscription.manage.title" = "Subscription"; +"subscription.manage.title" = "Abonnemang"; /* Alert content for not found subscription */ -"subscription.notFound.alert.message" = "There is no subscription associated with this Apple ID."; +"subscription.notFound.alert.message" = "Det finns inget abonnemang som är kopplat till detta Apple-ID."; /* Alert title for not found subscription */ -"subscription.notFound.alert.title" = "Subscription Not Found"; +"subscription.notFound.alert.title" = "Det gick inte att hitta abonnemanget"; /* View plans button text */ -"subscription.notFound.view.plans" = "View Plans"; +"subscription.notFound.view.plans" = "Visa abonnemang"; /* Hero Text for Personal information removal */ -"subscription.pir.hero" = "Activate Privacy Pro on desktop to set up Personal Information Removal"; +"subscription.pir.hero" = "Aktivera Privacy Pro på en dator för att konfigurera Personal Information Removal"; /* Description on how to use Personal information removal in desktop. The first placeholder references a location in the Desktop application. Privacy Pro>, and the second, the menu entry. i.e. */ -"subscription.pir.heroText" = "In the DuckDuckGo browser for desktop, go to %1$@ and click %2$@ to get started."; +"subscription.pir.heroText" = "I DuckDuckGo-webbläsaren för datorn går du till %1$@ och klickar på %2$@ för att komma igång."; /* Settings references a menu in the Desktop app, Privacy Pro, references our product name */ -"subscription.pir.heroTextLocation" = "Settings > Privacy Pro"; +"subscription.pir.heroTextLocation" = "Inställningar > Privacy Pro"; /* Menu item for enabling Personal Information Removal on Desktop */ -"subscription.pir.heroTextMenyEntry" = "I have a subscription"; +"subscription.pir.heroTextMenyEntry" = "Jag har ett abonnemang"; /* Text for the 'macOS' button */ "subscription.pir.macos" = "Mac"; @@ -2476,28 +2638,28 @@ "subscription.pir.windows" = "Windows"; /* Progress view title when completing the purchase */ -"subscription.progress.view.completing.purchase" = "Completing purchase..."; +"subscription.progress.view.completing.purchase" = "Slutför köpet ..."; /* Progress view title when starting the purchase */ -"subscription.progress.view.purchasing.subscription" = "Purchase in progress..."; +"subscription.progress.view.purchasing.subscription" = "Köp pågår ..."; /* Progress view title when restoring past subscription purchase */ -"subscription.progress.view.restoring.subscription" = "Restoring subscription..."; +"subscription.progress.view.restoring.subscription" = "Återställer abonnemang ..."; /* Remove from this device button */ -"subscription.remove.from.device.button" = "Remove From This Device"; +"subscription.remove.from.device.button" = "Ta bort från den här enheten"; /* Remove from device confirmation dialog text */ -"subscription.remove.from.device.text" = "You will no longer be able to access your Privacy Pro subscription on this device. This will not cancel your subscription, and it will remain active on your other devices."; +"subscription.remove.from.device.text" = "Du kommer inte längre att kunna komma åt ditt Privacy Pro-abonnemang på den här enheten. Det här kommer inte att avsluta ditt abonnemang utan det förblir aktivt på dina andra enheter."; /* Remove from device confirmation dialog title */ -"subscription.remove.from.device.title" = "Remove from this device?"; +"subscription.remove.from.device.title" = "Ta bort från den här enheten?"; /* Remove subscription button text */ -"subscription.remove.subscription" = "Remove Subscription"; +"subscription.remove.subscription" = "Ta bort abonnemang"; /* Remove subscription cancel button text */ -"subscription.remove.subscription.cancel" = "Cancel"; +"subscription.remove.subscription.cancel" = "Avbryt"; /* Button text for general error message */ "subscription.restore.backend.error.button" = "Tillbaka till Inställningar"; @@ -2509,28 +2671,43 @@ "subscription.restore.backend.error.title" = "Något gick fel"; /* Alert for general error message */ -"subscription.restore.general.error.message" = "The App Store was unable to process your purchase. Please try again later."; +"subscription.restore.general.error.message" = "App Store kunde inte behandla ditt köp. Försök igen senare."; /* Alert for general error title */ -"subscription.restore.general.error.title" = "Something Went Wrong"; +"subscription.restore.general.error.title" = "Något gick fel"; /* Alert button text for restored purchase alert */ "subscription.restore.success.alert.button" = "OK"; /* Alert message for restored purchase */ -"subscription.restore.success.alert.message" = "Your purchases have been restored."; +"subscription.restore.success.alert.message" = "Dina köp har återställts."; /* Alert title for restored purchase */ -"subscription.restore.success.alert.title" = "You’re all set."; +"subscription.restore.success.alert.title" = "Nu är du redo."; + +/* Subtitle in header when subscribed */ +"subscription.subscribed" = "Prenumererad"; /* Subscription Expired Data. This reads as 'Your subscription expired on (date)' */ "subscription.subscription.expired.caption" = "Din prenumeration gick ut den %@"; -/* Subscription expiration info. This reads as 'Your (monthly or annual) subscription expires on (date)' */ -"subscription.subscription.expiring.caption" = "Your %1$@ subscription expires on %2$@."; +/* Monthly subscription expiration info where parameter is expiration date. This reads as 'Your monthly subscription expires on (date)' */ +"subscription.subscription.expiring.monthly.caption" = "Ditt månadsabonnemang löper ut den %@."; + +/* Unknown period subscription expiration info where parameter is expiration date. This reads as 'Your subscription expires on (date)' */ +"subscription.subscription.expiring.unknown.caption" = "Ditt abonnemang löper ut den %@."; + +/* Annual subscription expiration info where parameter is expiration date. This reads as 'Your annual subscription expires on (date)' */ +"subscription.subscription.expiring.yearly.caption" = "Ditt årsabonnemang löper ut den %@."; + +/* Monthly subscription renewal info where parameter is renewal date. This reads as 'Your monthly subscription renews on (date)' */ +"subscription.subscription.renewing.monthly.caption" = "Ditt månadsabonnemang förnyas den %@."; + +/* Unknown period subscription renewal info where parameter is renewal date. This reads as 'Your subscription renews on (date)' */ +"subscription.subscription.renewing.unknown.caption" = "Ditt abonnemang förnyas den %@."; -/* Subscription renewal info. This reads as 'Your (monthly or annual) subscription renews on (date)' */ -"subscription.subscription.renewing.caption" = "Your %1$@ subscription renews on %2$@."; +/* Annual subscription renewal info where parameter is renewal date. This reads as 'Your annual subscription renews on (date)' */ +"subscription.subscription.renewing.yearly.caption" = "Ditt årsabonnemang förnyas den %@."; /* Navigation bar Title for subscriptions */ "subscription.title" = "Privacy Pro"; @@ -2632,7 +2809,7 @@ "theme.name.light" = "Ljus"; /* No comment provided by engineer. */ -"This list contains messages that have been shown plus at most 1 message that is scheduled for showing. There may be more messages in the config that will be presented, but they haven't been processed yet." = "This list contains messages that have been shown plus at most 1 message that is scheduled for showing. There may be more messages in the config that will be presented, but they haven't been processed yet."; +"This list contains messages that have been shown plus at most 1 message that is scheduled for showing. There may be more messages in the config that will be presented, but they haven't been processed yet." = "Den här listan innehåller meddelanden som har visats, samt högst ett meddelande som är schemalagt att visas. Det kan finnas fler meddelanden i konfigurationen som kommer att visas, men de har inte bearbetats ännu."; /* Confirmation of an action - populated with a domain name */ "toast.protection.disabled" = "Integritetsskydd inaktiverat för %@"; @@ -2677,118 +2854,118 @@ "voiceSearch.footer.note.old" = "Ljudet bearbetas på enheten. Det lagras inte och delas inte med någon, inte ens DuckDuckGo."; /* Cancel action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.cancel" = "Dismiss"; +"vpn.access-revoked.alert.action.cancel" = "Avvisa"; /* Primary action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.subscribe" = "Subscribe"; +"vpn.access-revoked.alert.action.subscribe" = "Prenumerera"; /* Alert message for the alert when the Privacy Pro subscription expiress */ -"vpn.access-revoked.alert.message" = "Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"vpn.access-revoked.alert.message" = "Prenumerera på Privacy Pro för att återansluta DuckDuckGo VPN."; /* Alert title for the alert when the Privacy Pro subscription expires */ -"vpn.access-revoked.alert.title" = "VPN disconnected due to expired subscription"; +"vpn.access-revoked.alert.title" = "VPN har kopplats från på grund av att abonnemanget har gått ut"; /* Title for the VPN widget onboarding screen */ "vpn.addWidget.settings.title" = "Hitta och välj DuckDuckGo. Svep sedan till VPN och välj Lägg till widget."; /* Title for the Cancel button of the VPN feedback form */ -"vpn.feedback-form.button.cancel" = "Cancel"; +"vpn.feedback-form.button.cancel" = "Avbryt"; /* Title for the Done button of the VPN feedback form */ -"vpn.feedback-form.button.done" = "Done"; +"vpn.feedback-form.button.done" = "Klart"; /* Title for the Submit button of the VPN feedback form */ -"vpn.feedback-form.button.submit" = "Submit"; +"vpn.feedback-form.button.submit" = "Skicka"; /* Title for the Submitting state of the VPN feedback form */ -"vpn.feedback-form.button.submitting" = "Submitting…"; +"vpn.feedback-form.button.submitting" = "Skickar ..."; /* Title for the browser crash/freeze category of the VPN feedback form */ -"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN causes browser to crash or freeze"; +"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN får webbläsaren att krascha eller låsa sig"; /* Title for the 'VPN fails to connect' category of the VPN feedback form */ -"vpn.feedback-form.category.fails-to-connect" = "VPN fails to connect"; +"vpn.feedback-form.category.fails-to-connect" = "VPN kan inte ansluta"; /* Title for the 'VPN feature request' category of the VPN feedback form */ -"vpn.feedback-form.category.feature-request" = "VPN feature request"; +"vpn.feedback-form.category.feature-request" = "VPN-utvecklingsförfrågan"; /* Title for the category 'VPN causes issues with other apps or websites' category of the VPN feedback form */ -"vpn.feedback-form.category.issues-with-apps" = "VPN causes issues with other apps or websites"; +"vpn.feedback-form.category.issues-with-apps" = "VPN orsakar problem med andra appar eller webbplatser"; /* Title for the local device connectivity category of the VPN feedback form */ -"vpn.feedback-form.category.local-device-connectivity" = "VPN won't let me connect to local device"; +"vpn.feedback-form.category.local-device-connectivity" = "VPN hindrar mig från att ansluta till en lokal enhet"; /* Title for the 'other VPN feedback' category of the VPN feedback form */ -"vpn.feedback-form.category.other" = "Other VPN feedback"; +"vpn.feedback-form.category.other" = "Annan VPN-feedback"; /* Title for the category selection state of the VPN feedback form */ -"vpn.feedback-form.category.select-category" = "Select a category"; +"vpn.feedback-form.category.select-category" = "VÄLJ EN KATEGORI"; /* Title for the 'VPN is too slow' category of the VPN feedback form */ -"vpn.feedback-form.category.too-slow" = "VPN connection is too slow"; +"vpn.feedback-form.category.too-slow" = "VPN-anslutningen är för långsam"; /* Title for the 'unable to install' category of the VPN feedback form */ -"vpn.feedback-form.category.unable-to-install" = "Unable to install VPN"; +"vpn.feedback-form.category.unable-to-install" = "Det går inte att installera VPN"; /* Title for the feedback sent view description of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.description" = "Your feedback will help us improve the\nDuckDuckGo VPN."; +"vpn.feedback-form.sending-confirmation.description" = "Din feedback hjälper oss att förbättra\nDuckDuckGo VPN."; /* Title for the feedback sending error text of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.error" = "We couldn't send your feedback right now, please try again."; +"vpn.feedback-form.sending-confirmation.error" = "Vi kunde inte skicka din feedback just nu, försök igen."; /* Title for the feedback sent view title of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.title" = "Thank you!"; +"vpn.feedback-form.sending-confirmation.title" = "Tack!"; /* Toast message when the VPN feedback form is submitted successfully */ -"vpn.feedback-form.submitted.message" = "Thank You! Feedback submitted."; +"vpn.feedback-form.submitted.message" = "Tack! Återkopplingen har skickats."; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-1" = "Please describe what's happening, what you expected to happen, and the steps that led to the issue:"; +"vpn.feedback-form.text-1" = "Beskriv vad som händer, vad du väntade dig skulle hända och vilka steg som ledde till problemet:"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-2" = "In addition to the details entered into this form, your app issue report will contain:"; +"vpn.feedback-form.text-2" = "Förutom de uppgifter som anges i det här formuläret kommer din anmälan om problem med appen att innehålla:"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-3" = "• Whether specific DuckDuckGo features are enabled"; +"vpn.feedback-form.text-3" = "• Om specifika DuckDuckGo-funktioner är aktiverade eller inte"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-4" = "• Aggregate DuckDuckGo app diagnostics"; +"vpn.feedback-form.text-4" = "• Sammanställd DuckDuckGo-appdiagnostik"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-5" = "By tapping \"Submit\" I agree that DuckDuckGo may use the information in this report for purposes of improving the app's features."; +"vpn.feedback-form.text-5" = "Genom att trycka på Skicka godkänner jag att DuckDuckGo använder informationen i den här rapporten i syfte att förbättra appens funktioner."; /* Title for each screen of the VPN feedback form */ -"vpn.feedback-form.title" = "Help Improve the DuckDuckGo VPN"; +"vpn.feedback-form.title" = "Hjälp till att förbättra DuckDuckGo VPN"; /* VPN settings screen cell text for adding the VPN widget to the home screen */ "vpn.settings.add.widget" = "Lägg till VPN-widget på startsidan"; /* Disclaimer for the DNS Server section on the DNS Server screen */ -"vpn.settings.dns.section-disclaimer" = "Using a custom DNS server can impact browsing speeds and expose your activity to third parties if the server isn't secure or reliable."; +"vpn.settings.dns.section-disclaimer" = "När du använder en anpassad DNS-server kan det påverka surfhastigheten och exponera din aktivitet för tredje part om servern inte är säker eller tillförlitlig."; /* Header text for the DNS section on the VPN Settings screen */ "vpn.settings.dns.section-header" = "DNS"; /* Title for the Apply button on the DNS Server setting screen */ -"vpn.settings.dns.server.apply.button.title" = "Apply"; +"vpn.settings.dns.server.apply.button.title" = "Tillämpa"; /* Default value for the DNS Server row on the VPN Settings screen */ "vpn.settings.dns.server.default.value" = "DuckDuckGo"; /* Title for the IPv4 Address setting */ -"vpn.settings.dns.server.ipv4.title" = "IPv4 Address"; +"vpn.settings.dns.server.ipv4.title" = "IPv4-adress"; /* Custom option for the DNS Server setting */ -"vpn.settings.dns.server.option.custom" = "Custom"; +"vpn.settings.dns.server.option.custom" = "Anpassat"; /* Recommended option for the DNS Server setting */ -"vpn.settings.dns.server.option.default" = "DuckDuckGo (Recommended)"; +"vpn.settings.dns.server.option.default" = "DuckDuckGo (rekommenderas)"; /* Title for the DNS Server setting screen */ -"vpn.settings.dns.server.screen.title" = "DNS Server"; +"vpn.settings.dns.server.screen.title" = "DNS-server"; /* Title for the DNS Server row on the VPN Settings screen */ -"vpn.settings.dns.server.title" = "DNS Server"; +"vpn.settings.dns.server.title" = "DNS-server"; /* Title for the button to enable push notifications in system settings */ "waitlist.allow-notifications" = "Tillåt aviseringar"; diff --git a/DuckDuckGo/sv.lproj/OmniBar.strings b/DuckDuckGo/sv.lproj/OmniBar.strings index 546f2d48ce..e6cc7773e8 100644 --- a/DuckDuckGo/sv.lproj/OmniBar.strings +++ b/DuckDuckGo/sv.lproj/OmniBar.strings @@ -7,6 +7,9 @@ /* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "Dt8-We-5tV"; */ "Dt8-We-5tV.accessibilityLabel" = "Rensa text"; +/* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "gb5-wL-4OW"; */ +"gb5-wL-4OW.accessibilityLabel" = "Rensa text"; + /* Class = "UIButton"; accessibilityLabel = "Share"; ObjectID = "hor-Jd-3kw"; */ "hor-Jd-3kw.accessibilityLabel" = "Dela"; diff --git a/DuckDuckGo/sv.lproj/Settings.strings b/DuckDuckGo/sv.lproj/Settings.strings index 9d5a7cf207..c70e5a7475 100644 --- a/DuckDuckGo/sv.lproj/Settings.strings +++ b/DuckDuckGo/sv.lproj/Settings.strings @@ -10,9 +10,6 @@ /* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "5YD-Od-uDU"; */ "5YD-Od-uDU.text" = "Rensa data automatiskt"; -/* Class = "UIButton"; normalTitle = "Settings"; ObjectID = "8YG-7Q-Czd"; */ -"8YG-7Q-Czd.normalTitle" = "Inställningar"; - /* Class = "UILabel"; text = "App Exit, Inactive for 1 Hour"; ObjectID = "9EC-lx-77p"; */ "9EC-lx-77p.text" = "Avsluta app, inaktiv i 1 timme"; @@ -76,9 +73,6 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Oskyddade webbplatser"; -/* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ -"ssa-zd-L3T.title" = "Textstorlek"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Visa tangentbord på"; diff --git a/DuckDuckGo/tr.lproj/DaxOnboarding.strings b/DuckDuckGo/tr.lproj/DaxOnboarding.strings index 9061c8f5de..c9da5aa098 100644 --- a/DuckDuckGo/tr.lproj/DaxOnboarding.strings +++ b/DuckDuckGo/tr.lproj/DaxOnboarding.strings @@ -1,12 +1,6 @@ /* Class = "UIButton"; normalTitle = "Hide"; ObjectID = "hgy-75-OvJ"; */ "hgy-75-OvJ.normalTitle" = "Gizle"; -/* Class = "UILabel"; text = "Welcome to\nDuckDuckGo!"; ObjectID = "Lvy-9G-3gr"; */ -"Lvy-9G-3gr.text" = "DuckDuckGo'ya\nHoş Geldiniz!"; - -/* Class = "UIButton"; normalTitle = "Let’s Do It!"; ObjectID = "oAv-Zt-uIt"; */ -"oAv-Zt-uIt.normalTitle" = "Hadi Başlayalım!"; - /* Class = "UIButton"; normalTitle = "Button"; ObjectID = "zJt-F9-L48"; */ "zJt-F9-L48.normalTitle" = "Düğme"; diff --git a/DuckDuckGo/tr.lproj/Localizable.strings b/DuckDuckGo/tr.lproj/Localizable.strings index 7612f8efaf..d787a57ece 100644 --- a/DuckDuckGo/tr.lproj/Localizable.strings +++ b/DuckDuckGo/tr.lproj/Localizable.strings @@ -341,7 +341,7 @@ "autofill.keep-enabled.alert.disable" = "Devre dışı bırak"; /* A link that takes the user to the DuckDuckGo help pages explaining password managers */ -"autofill.learn.more.link.title" = "Learn More"; +"autofill.learn.more.link.title" = "Daha Fazla Bilgi"; /* Button displayed after saving/updating an autofill login that takes the user to the saved login */ "autofill.login-save-action-button.toast" = "Görüntüle"; @@ -977,9 +977,6 @@ /* ad = advertisment */ "dax.onboarding.home.subsequent" = "İşte bu kadar!\n\nUnutmayın: İnterneti benimle ne kadar çok gezerseniz rahatsız edici reklamları da o kadar az görürsünüz. 👍"; -/* No comment provided by engineer. */ -"dax.onboarding.message" = "İnternet bazen ürkütücü olabilir.\n\nEndişelenmeyin! İnternette kimsenin göremeyeceği şekilde arama yapmak ve gezinmek sandığınızdan çok daha kolay."; - /* No comment provided by engineer. */ "Debug" = "Hata Ayıklama"; @@ -1080,7 +1077,7 @@ "duckPlayer.never.label" = "Hiçbir zaman"; /* Body text for the modal feature explanation */ -"duckplayer.presentation.modal.body" = "Duck Player, DuckDuckGo'da sinema benzeri bir deneyimde hedefli reklamlar olmadan YouTube'u izlemenizi sağlar ve izlediğiniz şeyler önerilerinizi etkilemez."; +"duckplayer.presentation.modal.body" = "Duck Player, DuckDuckGo'da hedefli reklamlar olmadan YouTube'u izlemenizi sağlar ve izlediğiniz şeyler önerilerinizi etkilemez."; /* Button that will dismiss the modal */ "duckplayer.presentation.modal.dismiss-button" = "Anladım!"; @@ -1088,17 +1085,23 @@ /* Two line title (separated by \n) for the feature explanation */ "duckplayer.presentation.modal.title" = "YouTube'da reklama mı boğuluyorsunuz? Duck Player'ı deneyin!"; +/* Two line title (separated by \n) for the feature explanation */ +"duckplayer.presentation.modal.title.youtube" = "YouTube'da reklama mı boğuluyorsunuz? Duck Player ile bundan kurtulun!"; + /* Footer label in the settings screen for Duck Player */ "duckplayer.settings.footer" = "DuckDuckGo, web'de gezinirken kendinizi korumak için ihtiyacınız olan tüm Privacy Essentials özelliklerini sağlar."; /* Text explaining what Duck Player is in the settings screen. */ -"duckplayer.settings.info-text" = "Duck Player, kişiselleştirilmiş reklamlar olmadan temiz bir görüntüleme deneyimi sağlar ve görüntüleme etkinliğinin YouTube önerilerinizi etkilemesini önler."; +"duckplayer.settings.info-text" = "Duck Player, DuckDuckGo'da hedefli reklamlar olmadan YouTube'u izlemenizi sağlar ve izlediğiniz şeyler önerilerinizi etkilemez."; /* Button that takes the user to learn more about Duck Player. */ "duckplayer.settings.learn-more" = "Daha Fazla Bilgi"; +/* Settings screen cell text for DuckPlayer settings to open in new tab */ +"duckplayer.settings.open-new-tab-label" = "Duck Player'ı yeni bir sekmede aç"; + /* Settings screen cell text for DuckPlayer settings */ -"duckplayer.settings.open-videos-in" = "Videoları Duck Player'da Aç"; +"duckplayer.settings.open-videos-in" = "YouTube Videolarını Duck Player'da Aç"; /* Settings screen cell text for DuckPlayer settings */ "duckplayer.settings.title" = "Duck Player"; @@ -1169,9 +1172,6 @@ /* Title for prompt to sign up for email protection */ "email.signup-prompt.title" = "E-postanızı Gizleyin ve \n İzleyicileri Engelleyin"; -/* No comment provided by engineer. */ -"empty!" = "empty!"; - /* Empty list state placholder */ "empty.bookmarks" = "Henüz yer imi eklenmedi"; @@ -1322,6 +1322,39 @@ /* Confirmation button */ "feedback.form.submit" = "Gönder"; +/* Title of the feedback form */ +"feedback.form.title" = "Privacy Pro'nun İyileştirilmesine Yardımcı Olun"; + +/* Category for Identity Theft Restoration feedback */ +"feedback.general.category.itr" = "Identity Theft Restoration"; + +/* Category for Personal Info Removal feedback */ +"feedback.general.category.pir" = "Kişisel Bilgilerin Silinmesi"; + +/* Category for subscription and payments feedback */ +"feedback.general.category.ppro" = "Abonelik ve Ödemeler"; + +/* Prompt to select a category for general feedback */ +"feedback.general.category.select" = "BİR KATEGORİ SEÇİN"; + +/* Category for VPN feedback */ +"feedback.general.category.vpn" = "VPN"; + +/* Category for access code issues */ +"feedback.itr.category.access-code" = "Erişim koduyla ilgili sorun"; + +/* Category for issues contacting an advisor */ +"feedback.itr.category.cant-contact-advisor" = "Danışmanla iletişime geçilemiyor"; + +/* Prompt to select a category for Identity Theft Restoration feedback */ +"feedback.itr.category.select" = "BİR KATEGORİ SEÇİN"; + +/* Category for other Identity Theft Restoration issues */ +"feedback.itr.category.something-else" = "Başka bir şey"; + +/* Category for unhelpful advisor calls */ +"feedback.itr.category.unhelpful" = "Danışmanı aramak işe yaramadı"; + /* No comment provided by engineer. */ "feedback.negative.form.genericPlaceholder" = "Lütfen mümkün olduğunca spesifik olun"; @@ -1361,6 +1394,24 @@ /* No comment provided by engineer. */ "feedback.performance.slowLoading" = "Web sayfaları veya arama sonuçları yavaş yükleniyor"; +/* Category for when scan finds incorrect records */ +"feedback.pir.category.not-me" = "Tarama, beni temsil etmeyen kayıtlar buldu"; + +/* Category for when scan doesn't find info on a specific site */ +"feedback.pir.category.nothing-on-site" = "Tarama, belirli bir sitede bilgilerimi bulamadı"; + +/* Category for other Personal Info Removal issues */ +"feedback.pir.category.other" = "Başka bir şey"; + +/* Category for when the removal process is stuck */ +"feedback.pir.category.removal-stuck" = "Silme işlemi takıldı"; + +/* Category for when the scan is stuck */ +"feedback.pir.category.scan-stuck" = "Kayıt tarama işlemi takıldı"; + +/* Prompt to select a category for Personal Info Removal feedback */ +"feedback.pir.category.select" = "BİR KATEGORİ SEÇİN"; + /* Header above input field */ "feedback.positive.form.header" = "Ayrıntıları paylaşın"; @@ -1379,6 +1430,15 @@ /* Button encouraging uses to share details aboout their feedback */ "feedback.positive.submit" = "Ayrıntıları paylaşın"; +/* Category for other Privacy Pro issues */ +"feedback.ppro.category.other" = "Başka bir şey"; + +/* Category for one-time password issues */ +"feedback.ppro.category.otp" = "Tek kullanımlık şifre ile ilgili sorun"; + +/* Prompt to select a category for Privacy Pro feedback */ +"feedback.ppro.category.select" = "BİR KATEGORİ SEÇİN"; + /* No comment provided by engineer. */ "feedback.start.footer" = "İsimsiz geri bildiriminiz bizim için önemlidir."; @@ -1529,12 +1589,6 @@ /* No comment provided by engineer. */ "keyCommandShowAllTabs" = "Tüm Sekmeleri Göster"; -/* Please preserve newline character */ -"launchscreenWelcomeMessage" = "DuckDuckGo'ya\nHoş Geldiniz!"; - -/* No comment provided by engineer. */ -"LOREM IPSUM" = "LOREM IPSUM"; - /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "Mac için DuckDuckGo, ihtiyacınız olan hız ve beklediğiniz tarama özelliklerinin yanı sıra sınıfındaki en iyi gizlilik özelliklerini (Privacy Essentials) sunuyor."; @@ -1586,62 +1640,83 @@ /* Edit button */ "navigation.title.edit" = "Düzenle"; -/* String indicating NetP is connected when viewed from the settings screen */ -"netP.cell.connected" = "Connected"; - -/* String indicating NetP is disconnected when viewed from the settings screen */ -"netP.cell.disconnected" = "Not connected"; - -/* Title for the DuckDuckGo VPN feature in settings */ -"netP.settings.title" = "VPN"; +/* String indicating NetP is snoozing when viewed from the settings screen */ +"netP.cell.snoozing" = "Uyku Modunda"; /* Title for the DuckDuckGo VPN feature */ "netP.title" = "DuckDuckGo VPN"; +/* Button title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.action" = "Widget ekle"; + +/* Message for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.message" = "VPN'i doğrudan Ana Ekrandan açın ve kapatın."; + +/* Title for tooltip about adding VPN widget */ +"network.protection.addwidget.tip.title" = "VPN Widget'ı Ekleyin"; + /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "Abonelik süresi dolduğu için VPN bağlantısı kesildi. DuckDuckGo VPN'e yeniden bağlanmak için Privacy Pro'ya abone olun."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Ağ Koruması bağlanamadı. Lütfen daha sonra tekrar deneyin."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "DuckDuckGo VPN bağlanamadı. Lütfen daha sonra tekrar deneyin."; /* Title for the VPN FAQ screen. */ "network.protection.faq.title" = "DuckDuckGo VPN Sıkça Sorulan Sorular"; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Ağ Koruması kesintiye uğradı. Şimdi yeniden bağlanmaya çalışılıyor..."; +/* Message for tooltip about geoswitching */ +"network.protection.geoswitching.tip.message" = "Dünya genelindeki sunucularımızdan herhangi birine bağlanarak VPN konumunuzu özelleştirebilirsiniz."; + +/* Title for tooltip about geoswitching */ +"network.protection.geoswitching.tip.title" = "Konumunuzu Değiştirin"; + +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "DuckDuckGo VPN kesintiye uğradı. Şimdi yeniden bağlanmaya çalışılıyor..."; /* Message for the network protection invite dialog */ -"network.protection.invite.dialog.message" = "Enter your invite code to get started."; +"network.protection.invite.dialog.message" = "Başlamak için davet kodunuzu girin."; /* Title for the network protection invite screen */ "network.protection.invite.dialog.title" = "DuckDuckGo VPN'i denemeye davetlisiniz"; /* Prompt for the network protection invite code text field */ -"network.protection.invite.field.prompt" = "Invite Code"; +"network.protection.invite.field.prompt" = "Davet Kodu"; /* Message for the network protection invite success view */ -"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; +"network.protection.invite.success.message" = "Konumunuzu web sitelerinden gizleyin ve çevrimiçi etkinliğinizi İnternet sağlayıcılarından ve ağınızdaki diğer kişilerden gizli tutun."; /* Title for the network protection invite success view */ -"network.protection.invite.success.title" = "Success! You’re in."; +"network.protection.invite.success.title" = "Tebrikler! Katıldınız."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; /* Title text for an iOS quick action that opens VPN settings */ "network.protection.quick-action.open-vpn" = "VPN'i aç"; +/* Button title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.action" = "Daha fazla bilgi"; + +/* Message for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.message" = "VPN bağlantısını askıya alarak, VPN trafiğini engelleyen siteleri veya uygulamaları kullanabilirsiniz."; + +/* Title for tooltip about VPN snooze mode */ +"network.protection.snooze.tip.title" = "VPN Çakışmalarını Engelleyin"; + +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN %@ için uyku moduna alındı"; + /* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ "network.protection.status.connected.format" = "Bağlandı · %@"; /* The label for the NetP VPN when connecting */ -"network.protection.status.connecting" = "Connecting..."; +"network.protection.status.connecting" = "Bağlanıyor..."; /* The label for the NetP VPN when disconnected */ -"network.protection.status.disconnected" = "Not connected"; +"network.protection.status.disconnected" = "Bağlı değil"; /* The label for the NetP VPN when disconnecting */ -"network.protection.status.disconnecting" = "Disconnecting..."; +"network.protection.status.disconnecting" = "Bağlantı kesiliyor..."; /* Message label text for the status view when VPN is disconnected */ "network.protection.status.header.message.off" = "Tüm cihazlarınızın internet trafiğinde güvenliği sağlamak\niçin bağlanın."; @@ -1655,26 +1730,41 @@ /* Header title label text for the status view when VPN is connected */ "network.protection.status.header.title.on" = "DuckDuckGo VPN Açık"; +/* Header title label text for the status view when VPN is snoozing */ +"network.protection.status.header.title.snoozed" = "DuckDuckGo VPN Uyku Moduna Alındı"; + /* The status view 'Share Feedback' button which is shown inline on the status view after the temporary free use footer text */ -"network.protection.status.menu.share.feedback" = "Share Feedback"; +"network.protection.status.menu.share.feedback" = "Geri Bildirim Paylaş"; + +/* The label for the NetP VPN when paused */ +"network.protection.status.paused" = "Duraklatıldı"; + +/* The label for when NetP VPN is snoozing plus the length of time remaining formatted as '0:00' */ +"network.protection.status.snoozing.format" = "Uyku modunda, %@ kaldı"; + +/* Snooze button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.snooze" = "20 Dakikalığına Uyku Moduna Al"; + +/* Wake Up button title shown in NetworkProtection's status view. */ +"network.protection.status.view.action.wake-up" = "Uyandır"; /* Connection details label shown in NetworkProtection's status view. */ -"network.protection.status.view.connection.details" = "Connection Details"; +"network.protection.status.view.connection.details" = "Bağlantı Ayrıntıları"; /* Custom DNS label shown in NetworkProtection's status view. */ -"network.protection.status.view.custom.dns" = "DNS Server"; +"network.protection.status.view.custom.dns" = "DNS Sunucusu"; /* Generic connection failed error message shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.message" = "Please try again later."; +"network.protection.status.view.error.connection.failed.message" = "Lütfen daha sonra tekrar deneyin."; /* Generic connection failed error title shown in NetworkProtection's status view. */ -"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; +"network.protection.status.view.error.connection.failed.title" = "Bağlanılamadı."; /* IP Address label shown in NetworkProtection's status view. */ -"network.protection.status.view.ip.address" = "IP Address"; +"network.protection.status.view.ip.address" = "IP Adresi"; /* Location label shown in NetworkProtection's status view. */ -"network.protection.status.view.location" = "Location"; +"network.protection.status.view.location" = "Konum"; /* Label shown on the title of the settings section in NetworkProtection's status view. */ "network.protection.status.view.settings.section.title" = "Yönet"; @@ -1682,12 +1772,15 @@ /* Title label text for the status view when netP is disconnected */ "network.protection.status.view.title" = "VPN"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Ağ Koruması Açık. Konumunuz ve çevrim içi etkinliğiniz korunuyor."; +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "DuckDuckGo VPN Açık. Konumunuz ve çevrimiçi etkinliğiniz korunuyor."; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Cihaz trafiği %@ üzerinden yönlendiriliyor."; +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "VPN uyku modu sona erdi. Cihaz trafiği %@ üzerinden yönlendiriliyor."; + /* Title for the button to link to the iOS app settings and enable notifications app-wide. */ "network.protection.turn.on.notifications.button.title" = "Bildirimleri Açın"; @@ -1698,13 +1791,13 @@ "network.protection.vpn.about" = "Hakkında"; /* Section header for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.section.header" = "Notifications"; +"network.protection.vpn.alerts.section.header" = "Bildirimler"; /* List section footer for the toggle for VPN alerts. */ "network.protection.vpn.alerts.toggle.section.footer" = "Bağlantınız kesilirse veya VPN durumunuz değişirse bildirim alın."; /* Title for the toggle for VPN notifications. */ -"network.protection.vpn.alerts.toggle.title" = "VPN Notifications"; +"network.protection.vpn.alerts.toggle.title" = "VPN Bildirimleri"; /* Title for the data volume section in the VPN status screen */ "network.protection.vpn.data-volume" = "Veri Hacmi"; @@ -1713,13 +1806,13 @@ "network.protection.vpn.exclude.local.networks.setting.footer" = "Yerel trafiğin VPN'i atlamasını ve yerel ağınızdaki yazıcı gibi cihazlara bağlanmasını sağlayın."; /* Header text for the Exclude Local Networks setting item. */ -"network.protection.vpn.exclude.local.networks.setting.header" = "General"; +"network.protection.vpn.exclude.local.networks.setting.header" = "Genel"; /* Title for the Exclude Local Networks setting item. */ "network.protection.vpn.exclude.local.networks.setting.title" = "Yerel Ağları Hariç Tut"; /* Title for the VPN Location screen's All Countries section. */ -"network.protection.vpn.location.all.countries.section.title" = "All Countries"; +"network.protection.vpn.location.all.countries.section.title" = "Tüm Ülkeler"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.connected" = "Bağlı Konum"; @@ -1734,7 +1827,7 @@ "network.protection.vpn.location.recommended.section.footer" = "Bulabildiğimiz en yakın sunucuya otomatik olarak bağlanın."; /* Title for the VPN Location screen's Recommended section. */ -"network.protection.vpn.location.recommended.section.title" = "Recommended"; +"network.protection.vpn.location.recommended.section.title" = "Önerilen"; /* Description of the location type in the VPN status screen */ "network.protection.vpn.location.selected" = "Konum Seçildi"; @@ -1752,13 +1845,13 @@ "network.protection.vpn.preferred.location.nearest" = "En yakın konum"; /* Title for the Preferred Location VPN Settings item. */ -"network.protection.vpn.preferred.location.title" = "Preferred Location"; +"network.protection.vpn.preferred.location.title" = "Tercih Edilen Konum"; /* Footer text for the DNS server setting item. */ -"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo routes DNS queries through our DNS servers so your internet provider can't see what websites you visit."; +"network.protection.vpn.secure.dns.setting.footer" = "DuckDuckGo, DNS sorgularını DNS sunucularımız üzerinden yönlendirir. Böylece internet sağlayıcınız hangi web sitelerini ziyaret ettiğinizi göremez."; /* Title for the FAQ row in the VPN status screen. */ -"network.protection.vpn.settings.faq" = "FAQs and Support"; +"network.protection.vpn.settings.faq" = "SSS ve Destek"; /* Title for the feedback row in the VPN status screen. */ "network.protection.vpn.settings.share-feedback" = "VPN Geri Bildirimini Paylaş"; @@ -1940,6 +2033,66 @@ /* Deactivate button */ "pm.deactivate" = "Devre dışı bırak"; +/* Label for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.label" = "E-posta (isteğe bağlı)"; + +/* Placeholder for the email form in the Privacy Pro feedback form */ +"ppro.feedback-form.email.placeholder" = "name@email.com"; + +/* Placeholder for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.placeholder" = "Lütfen bize geri bildirimlerinizi iletin…"; + +/* Title for the General Feedback step in the Privacy Pro feedback form */ +"ppro.feedback-form.general-feedback.title" = "Genel geri bildirim"; + +/* Title for the ITR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-itr-problem.title" = "Identity Theft Restoration"; + +/* Title for the PIR category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-pir-problem.title" = "Personal Information Removal"; + +/* Title for the Subscriptions and Payments category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-ppro-problem.title" = "Abonelikler ve Ödemeler"; + +/* Placeholder for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.placeholder" = "Bize neler olduğunu anlatın..."; + +/* Title for the Report a Problem step in the Privacy Pro feedback form */ +"ppro.feedback-form.report-problem.title" = "Sorun bildir"; + +/* Title for the VPN category in the Privacy Pro feedback form */ +"ppro.feedback-form.report-vpn-problem.title" = "VPN"; + +/* Placeholder for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.placeholder" = "Hangi özelliği görmek istersiniz?"; + +/* Title for the Feature Request step in the Privacy Pro feedback form */ +"ppro.feedback-form.request-feature.title" = "Özellik isteği"; + +/* Title for the category selection section in the Privacy Pro feedback form */ +"ppro.feedback-form.select-category.title" = "BİR KATEGORİ SEÇİN"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-1" = "Yardım merkezimizde ([help center](duck://)) ele alınmayan bir sorun mu buldunuz? Ne olduğunu kesinlikle öğrenmek isteriz.\n\nBu sorunla ilgili olarak sizinle iletişime geçmemizi istiyorsanız bir e-posta adresi sağlayın (tüm sorunlara yanıt veremeyebiliriz):"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-2" = "Yukarıda girilen bilgilere ek olarak, geri bildiriminizle birlikte bazı anonimleştirilmiş bilgiler gönderiyoruz:"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-3" = "• Bazı tarayıcı özelliklerinin etkin olup olmadığı"; + +/* Bullet text for the body of the PPro feedback form */ +"ppro.feedback-form.text-4" = "• Toplu uygulama tanılamaları (ör. hata kodları)"; + +/* Text for the body of the PPro feedback form */ +"ppro.feedback-form.text-5" = "\"Gönder\"e dokunarak, DuckDuckGo'nun gönderilen bilgileri uygulamayı iyileştirmek için kullanabileceğini kabul ediyorsunuz."; + +/* Title for the text box in the Privacy Pro feedback form */ +"ppro.feedback-form.text-box.title" = "Geri Bildirim"; + +/* Title for the Privacy Pro feedback form */ +"ppro.feedback-form.title" = "Geri Bildirim gönder"; + /* Button title for sync bookmarks limits exceeded warning to go to manage bookmarks */ "prefrences.sync.bookmarks-limit-exceeded-action" = "Yer İmlerini Yönet"; @@ -2029,6 +2182,18 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Favoriler"; +/* Name of the option the user can chose to give general browser feedback */ +"send.browser.feedback.general-feedback" = "Genel geri bildirim"; + +/* Name of the option the user can chose to give browser feedback about a problem they enountered */ +"send.browser.feedback.report-problem" = "Sorun bildir"; + +/* Name of the option the user can chose to give browser feedback about a feature they would like */ +"send.browser.feedback.request-feature" = "Bir özellik isteyin"; + +/* Title of the picker where the user can chose the category of the feedback they want ot send. */ +"send.browser.feedback.select-category" = "BİR KATEGORİ SEÇİN"; + /* Settings cell for About DDG */ "settings.about.ddg" = "DuckDuckGo Hakkında"; @@ -2084,6 +2249,9 @@ /* Section footer Autolock description */ "settings.autolock.description" = "Touch ID, Face ID veya sistem parolası etkinleştirilmişse, uygulamayı açtığınızda kilidi açmanız istenir."; +/* Settings cell for Browser Feedback */ +"settings.browser.feedback" = "Tarayıcı Geri Bildirimi"; + /* Settings screen cell text for Automatically Clearing Data */ "settings.clear.data" = "Verileri Otomatik Olarak Temizle"; @@ -2182,7 +2350,7 @@ "settings.ppro" = "Privacy Pro"; /* Title for Link in the Footer of Privacy Pro section */ -"settings.ppro.footer" = "Privacy Policy and Terms of Service"; +"settings.ppro.footer" = "Gizlilik Politikası ve Hizmet Şartları"; /* Settings screen cell for long press previews */ "settings.previews" = "Uzun Basma Önizlemeleri"; @@ -2208,23 +2376,20 @@ /* Settings screen cell text for setting address bar position */ "settings.set.your.address.bar.position" = "Adres Çubuğu Konumunuzu Ayarlayın"; -/* Subscription activation pending description */ -"settings.subscription.activation.pending.description" = "This is taking longer than usual, please check back later."; +/* Privacy pro description subtitle in settings when the is activating */ +"settings.subscription.activating" = "Etkinleştiriliyor"; -/* Subscription activation pending title */ -"settings.subscription.activation.pending.title" = "Aboneliğiniz etkinleştiriliyor"; +/* Subscription activation pending description */ +"settings.subscription.activation.pending.description" = "Bu işlem normalden daha uzun sürüyor. Lütfen daha sonra tekrar kontrol edin."; /* Data Broker protection cell subtitle for privacy pro */ -"settings.subscription.DBP.subtitle" = "Remove your info from sites that sell it"; +"settings.subscription.DBP.subtitle" = "Bilgilerinizi, bunları satan sitelerden kaldırın"; /* Data Broker protection cell title for privacy pro */ "settings.subscription.DBP.title" = "Personal Information Removal"; -/* Privacy pro description subtext */ -"settings.subscription.description" = "Üç yeni koruma ile daha sorunsuz gizlilik:"; - /* I have a Subscription button text for privacy pro */ -"settings.subscription.existing.subscription" = "I Have a Subscription"; +"settings.subscription.existing.subscription" = "Aboneliğim Var"; /* Subscription expired description */ "settings.subscription.expired.comment" = "Privacy Pro'yu kullanmaya devam etmek için tekrar abone olun"; @@ -2232,23 +2397,26 @@ /* Subscription expired tittle message */ "settings.subscription.expired.title" = "Privacy Pro aboneliğiniz sona erdi"; -/* Privacy pro features list */ -"settings.subscription.features" = " • VPN\n • Personal Information Removal\n • Identity Theft Restoration"; - /* Identity theft restoration cell subtitle for privacy pro */ -"settings.subscription.ITR.subtitle" = "If your identity is stolen, we'll help restore it"; +"settings.subscription.ITR.subtitle" = "Kimliğiniz çalınırsa kurtarmanıza yardımcı olacağız"; /* Identity theft restoration cell title for privacy pro */ "settings.subscription.ITR.title" = "Identity Theft Restoration"; /* Get Privacy Pro button text for privacy pro */ -"settings.subscription.learn.more" = "Get Privacy Pro"; +"settings.subscription.learn.more" = "Privacy Pro'yu Al"; /* Subscription Settings button text for privacy pro */ -"settings.subscription.manage" = "Subscription Settings"; +"settings.subscription.manage" = "Abonelik Ayarları"; + +/* Privacy Pro description subtitle in settings */ +"settings.subscription.row.description" = "VPN ve Identity Theft Restoration hizmetlerimizi içerir."; -/* Call to action title for Privacy Pro */ -"settings.subscription.subscribe" = "Subscribe to Privacy Pro"; +/* Call to action title for Privacy Pro settings */ +"settings.subscription.subscribe" = "Privacy Pro ile bağlantınızı ve kimliğinizi koruyun"; + +/* Privacy pro description subtitle in settings */ +"settings.subscription.us.description" = "VPN, Personal Information Removal ve Identity Theft Restoration'ı içerir."; /* VPN cell title for privacy pro */ "settings.subscription.VPN.title" = "VPN"; @@ -2317,67 +2485,64 @@ "siteFeedback.urlPlaceholder" = "Hangi web sitesi hatalı?"; /* Subscription Activation Info */ -"subscription.activate..header.description" = "Access your Privacy Pro subscription on this device via Apple ID or an email address."; +"subscription.activate..header.description" = "Bu cihazda Privacy Pro aboneliğinize Apple ID veya bir e-posta adresi aracılığıyla erişin."; /* Button for adding email address to subscription */ -"subscription.activate.add.email.button" = "Add Email"; +"subscription.activate.add.email.button" = "E-posta Ekle"; /* Apple ID option for activation */ "subscription.activate.appleid" = "Apple ID"; /* Button text for restoring purchase via Apple ID */ -"subscription.activate.appleid.button" = "Restore Purchase"; +"subscription.activate.appleid.button" = "Satın Almayı Geri Yükle"; /* Description for Apple ID activation */ -"subscription.activate.appleid.description" = "Restore your purchase to activate your subscription on this device."; +"subscription.activate.appleid.description" = "Aboneliğinizi bu cihazda etkinleştirmek için satın alma işleminizi geri yükleyin."; /* Subscription Activation Info */ -"subscription.activate.description" = "Your subscription is automatically available in DuckDuckGo on any device signed in to your Apple ID."; +"subscription.activate.description" = "Aboneliğiniz, Apple ID'niz ile oturum açtığınız tüm cihazlarda DuckDuckGo'da otomatik olarak kullanılabilir."; /* Button for editing email address added to subscription */ -"subscription.activate.edit.email.button" = "Edit Email"; +"subscription.activate.edit.email.button" = "E-postayı Düzenle"; /* View Title for editing your email account */ -"subscription.activate.edit.email.title" = "Edit Email"; +"subscription.activate.edit.email.title" = "E-postayı Düzenle"; /* Email option for activation */ -"subscription.activate.email" = "Email"; +"subscription.activate.email" = "E-posta"; /* Restore button title for Email */ -"subscription.activate.email.button" = "Enter Email"; +"subscription.activate.email.button" = "E-posta Girin"; /* Description for Email activation */ -"subscription.activate.email.description" = "Use your email to activate your subscription on this device."; +"subscription.activate.email.description" = "Bu cihazda aboneliğinizi etkinleştirmek için e-postanızı kullanın."; /* Activate subscription title */ "subscription.activate.email.title" = "Aboneliği Etkinleştir"; /* Button title for cancelling email deletion */ -"subscription.activate.manage.email.cancel" = "Cancel"; +"subscription.activate.manage.email.cancel" = "İptal"; /* Button title for confirming email deletion */ -"subscription.activate.manage.email.OK" = "OK"; +"subscription.activate.manage.email.OK" = "Tamam"; /* Restore button title for AppleID */ -"subscription.activate.restore.apple" = "Restore Purchase"; +"subscription.activate.restore.apple" = "Satın Almayı Geri Yükle"; /* Subscription Activation Title */ -"subscription.activate.title" = "Activate your subscription on this device"; +"subscription.activate.title" = "Bu cihazda aboneliğinizi etkinleştirin"; /* Resend activation instructions button */ -"subscription.add.device.resend.instructions" = "Resend Instructions"; +"subscription.add.device.resend.instructions" = "Talimatları Tekrar Gönder"; /* Add email to an existing subscription */ -"subscription.add.email" = "Add an email address to activate your subscription on your other devices. We’ll only use this address to verify your subscription."; +"subscription.add.email" = "Aboneliğinizi diğer cihazlarınızda etkinleştirmek için bir e-posta adresi ekleyin. Bu adresi yalnızca aboneliğinizi doğrulamak için kullanacağız."; /* View title for adding email to subscription */ -"subscription.add.email.title" = "Add Email"; - -/* Title for Alert messages */ -"subscription.alert.title" = "subscription.alert.title"; +"subscription.add.email.title" = "E-posta Ekle"; /* Subscription availability message on Apple devices */ -"subscription.available.apple" = "Privacy Pro is available on any device signed in to the same Apple ID."; +"subscription.available.apple" = "Privacy Pro, aynı Apple ID ile oturum açılmış herhangi bir cihazda kullanılabilir."; /* Text for the manage billing page */ "subscription.billing.google.text" = "Aboneliğiniz Google Play Store üzerinden satın alınmış. Aboneliğinizi yenilemek için lütfen ilk olarak aboneliğinizi satın alırken kullandığınız Google Hesabında oturum açılmış bir cihazda Google Play Store abonelik ayarlarını açın."; @@ -2385,89 +2550,86 @@ /* Title for the manage billing page */ "subscription.billing.google.title" = "Abonelik Planları"; -/* Subscription annual billing period type */ -"subscription.billing.period.annual" = "annual"; - -/* Subscription monthly billing period type */ -"subscription.billing.period.monthly" = "monthly"; - /* Subscription Removal confirmation message */ -"subscription.cancel.message" = "Your subscription has been removed from this device."; +"subscription.cancel.message" = "Aboneliğiniz bu cihazdan kaldırıldı."; /* Change plan or cancel title */ -"subscription.change.plan" = "Update Plan or Cancel"; +"subscription.change.plan" = "Planı Güncelle veya İptal Et"; /* Navigation Button for closing subscription view */ -"subscription.close" = "Close"; +"subscription.close" = "Kapat"; /* Title for Confirm messages */ -"subscription.confirm.title" = "Are you sure?"; +"subscription.confirm.title" = "Emin misiniz?"; /* Header for section for activating subscription on other devices */ -"subscription.devices.header" = "Activate on Other Devices"; +"subscription.devices.header" = "Diğer Cihazlarda Etkinleştir"; /* Footer for section for activating subscription on other devices when email was not yet added */ -"subscription.devices.no.email.footer" = "Add an optional email to your subscription or use your Apple ID to access Privacy Pro on other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.no.email.footer" = "Privacy Pro'ya diğer cihazlardan erişmek için aboneliğinize isteğe bağlı bir e-posta ekleyin. **[Daha fazla bilgi edinin](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Footer for section for activating subscription on other devices when email is added */ -"subscription.devices.with.email.footer" = "Use this email to activate your subscription in Settings > Privacy Pro in the DuckDuckGo app on your other devices. **[Learn more](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; +"subscription.devices.with.email.footer" = "Bu e-postayı, diğer cihazlarınızdaki DuckDuckGo uygulamasında Ayarlar > Privacy Pro bölümünde aboneliğinizi etkinleştirmek için kullanın. **[Daha fazla bilgi edinin](https://duckduckgo.com/duckduckgo-help-pages/privacy-pro/adding-email/)**"; /* Alert content for not found subscription */ -"subscription.email.inactive.alert.message" = "The subscription associated with this email is no longer active."; +"subscription.email.inactive.alert.message" = "Bu e-postayla ilişkili abonelik artık aktif değil."; /* Alert title for not found subscription */ -"subscription.email.inactive.alert.title" = "Subscription Not Found"; +"subscription.email.inactive.alert.title" = "Abonelik Bulunamadı"; /* Alert content for not found subscription */ -"subscription.expired.alert.message" = "The subscription associated with this Apple ID is no longer active."; +"subscription.expired.alert.message" = "Bu Apple ID ile ilişkili abonelik artık aktif değil."; /* FAQ Button */ -"subscription.faq" = "FAQs and Support"; +"subscription.faq" = "SSS ve Destek"; /* FAQ Description */ -"subscription.faq.description" = "Get answers to frequently asked questions or contact Privacy Pro support from our help pages."; +"subscription.faq.description" = "Yardım sayfalarımızdan sıkça sorulan soruların yanıtlarını alabilir veya Privacy Pro destek ekibiyle iletişime geçebilirsiniz."; + +/* Send Feedback Button */ +"subscription.feedback" = "Geri Bildirim gönder"; /* Cancel action for the existing subscription dialog */ -"subscription.found.cancel" = "Cancel"; +"subscription.found.cancel" = "İptal"; /* Restore action for the existing subscription dialog */ -"subscription.found.restore" = "Restore"; +"subscription.found.restore" = "Geri Yükle"; /* Message for the existing subscription dialog */ -"subscription.found.text" = "We found a subscription associated with this Apple ID."; +"subscription.found.text" = "Bu Apple ID ile ilişkili bir abonelik bulduk."; /* Title for the existing subscription dialog */ -"subscription.found.title" = "Subscription Found"; +"subscription.found.title" = "Abonelik Bulundu"; /* Help and support Section header */ -"subscription.help" = "Help and support"; +"subscription.help" = "Yardım ve Destek"; /* Manage Plan header */ -"subscription.manage.plan" = "Manage Plan"; +"subscription.manage.plan" = "Planı Yönetin"; /* Header for the subscription section */ -"subscription.manage.title" = "Subscription"; +"subscription.manage.title" = "Abonelik"; /* Alert content for not found subscription */ -"subscription.notFound.alert.message" = "There is no subscription associated with this Apple ID."; +"subscription.notFound.alert.message" = "Bu Apple ID ile ilişkili bir abonelik yok."; /* Alert title for not found subscription */ -"subscription.notFound.alert.title" = "Subscription Not Found"; +"subscription.notFound.alert.title" = "Abonelik Bulunamadı"; /* View plans button text */ -"subscription.notFound.view.plans" = "View Plans"; +"subscription.notFound.view.plans" = "Planları Görüntüle"; /* Hero Text for Personal information removal */ -"subscription.pir.hero" = "Activate Privacy Pro on desktop to set up Personal Information Removal"; +"subscription.pir.hero" = "Personal Information Removal kurulumu için masaüstünde Privacy Pro'yu etkinleştirin"; /* Description on how to use Personal information removal in desktop. The first placeholder references a location in the Desktop application. Privacy Pro>, and the second, the menu entry. i.e. */ -"subscription.pir.heroText" = "In the DuckDuckGo browser for desktop, go to %1$@ and click %2$@ to get started."; +"subscription.pir.heroText" = "Masaüstü için DuckDuckGo tarayıcısında %1$@ adresine gidin ve başlamak için %2$@ seçeneğine tıklayın."; /* Settings references a menu in the Desktop app, Privacy Pro, references our product name */ -"subscription.pir.heroTextLocation" = "Settings > Privacy Pro"; +"subscription.pir.heroTextLocation" = "Ayarlar > Privacy Pro"; /* Menu item for enabling Personal Information Removal on Desktop */ -"subscription.pir.heroTextMenyEntry" = "I have a subscription"; +"subscription.pir.heroTextMenyEntry" = "Aboneliğim Var"; /* Text for the 'macOS' button */ "subscription.pir.macos" = "Mac"; @@ -2476,28 +2638,28 @@ "subscription.pir.windows" = "Windows"; /* Progress view title when completing the purchase */ -"subscription.progress.view.completing.purchase" = "Completing purchase..."; +"subscription.progress.view.completing.purchase" = "Satın alma tamamlanıyor..."; /* Progress view title when starting the purchase */ -"subscription.progress.view.purchasing.subscription" = "Purchase in progress..."; +"subscription.progress.view.purchasing.subscription" = "Satın alma devam ediyor..."; /* Progress view title when restoring past subscription purchase */ -"subscription.progress.view.restoring.subscription" = "Restoring subscription..."; +"subscription.progress.view.restoring.subscription" = "Abonelik geri yükleniyor..."; /* Remove from this device button */ -"subscription.remove.from.device.button" = "Remove From This Device"; +"subscription.remove.from.device.button" = "Bu Cihazdan Kaldır"; /* Remove from device confirmation dialog text */ -"subscription.remove.from.device.text" = "You will no longer be able to access your Privacy Pro subscription on this device. This will not cancel your subscription, and it will remain active on your other devices."; +"subscription.remove.from.device.text" = "Privacy Pro aboneliğinize artık bu cihaz üzerinden erişemeyeceksiniz. Bu, aboneliğinizi iptal etmez ve abonelik diğer cihazlarınızda aktif kalır."; /* Remove from device confirmation dialog title */ -"subscription.remove.from.device.title" = "Remove from this device?"; +"subscription.remove.from.device.title" = "Bu cihazdan kaldırılsın mı?"; /* Remove subscription button text */ -"subscription.remove.subscription" = "Remove Subscription"; +"subscription.remove.subscription" = "Aboneliği Kaldır"; /* Remove subscription cancel button text */ -"subscription.remove.subscription.cancel" = "Cancel"; +"subscription.remove.subscription.cancel" = "İptal"; /* Button text for general error message */ "subscription.restore.backend.error.button" = "Ayarlara Geri Dön"; @@ -2509,28 +2671,43 @@ "subscription.restore.backend.error.title" = "Bir hata oluştu"; /* Alert for general error message */ -"subscription.restore.general.error.message" = "The App Store was unable to process your purchase. Please try again later."; +"subscription.restore.general.error.message" = "App Store satın alma işleminizi gerçekleştiremedi. Lütfen daha sonra tekrar deneyin."; /* Alert for general error title */ -"subscription.restore.general.error.title" = "Something Went Wrong"; +"subscription.restore.general.error.title" = "Bir hata oluştu"; /* Alert button text for restored purchase alert */ -"subscription.restore.success.alert.button" = "OK"; +"subscription.restore.success.alert.button" = "Tamam"; /* Alert message for restored purchase */ -"subscription.restore.success.alert.message" = "Your purchases have been restored."; +"subscription.restore.success.alert.message" = "Satın aldıklarınız geri yüklendi."; /* Alert title for restored purchase */ -"subscription.restore.success.alert.title" = "You’re all set."; +"subscription.restore.success.alert.title" = "Artık hazırsınız."; + +/* Subtitle in header when subscribed */ +"subscription.subscribed" = "Abone olundu"; /* Subscription Expired Data. This reads as 'Your subscription expired on (date)' */ "subscription.subscription.expired.caption" = "Aboneliğinizin süresi %@ tarihinde doldu"; -/* Subscription expiration info. This reads as 'Your (monthly or annual) subscription expires on (date)' */ -"subscription.subscription.expiring.caption" = "Your %1$@ subscription expires on %2$@."; +/* Monthly subscription expiration info where parameter is expiration date. This reads as 'Your monthly subscription expires on (date)' */ +"subscription.subscription.expiring.monthly.caption" = "Aylık aboneliğiniz %@ tarihinde sona erecek."; + +/* Unknown period subscription expiration info where parameter is expiration date. This reads as 'Your subscription expires on (date)' */ +"subscription.subscription.expiring.unknown.caption" = "Aboneliğiniz %@ tarihinde sona erecek."; + +/* Annual subscription expiration info where parameter is expiration date. This reads as 'Your annual subscription expires on (date)' */ +"subscription.subscription.expiring.yearly.caption" = "Yıllık aboneliğiniz %@ tarihinde sona erecek."; + +/* Monthly subscription renewal info where parameter is renewal date. This reads as 'Your monthly subscription renews on (date)' */ +"subscription.subscription.renewing.monthly.caption" = "Aylık aboneliğiniz %@ tarihinde yenilenecek."; + +/* Unknown period subscription renewal info where parameter is renewal date. This reads as 'Your subscription renews on (date)' */ +"subscription.subscription.renewing.unknown.caption" = "Aboneliğiniz %@ tarihinde yenilenecek."; -/* Subscription renewal info. This reads as 'Your (monthly or annual) subscription renews on (date)' */ -"subscription.subscription.renewing.caption" = "Your %1$@ subscription renews on %2$@."; +/* Annual subscription renewal info where parameter is renewal date. This reads as 'Your annual subscription renews on (date)' */ +"subscription.subscription.renewing.yearly.caption" = "Yıllık aboneliğiniz %@ tarihinde yenilenecek."; /* Navigation bar Title for subscriptions */ "subscription.title" = "Privacy Pro"; @@ -2677,118 +2854,118 @@ "voiceSearch.footer.note.old" = "Ses cihazda işlenir. Saklanmaz ve DuckDuckGo da dâhil olmak üzere kimseyle paylaşılmaz."; /* Cancel action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.cancel" = "Dismiss"; +"vpn.access-revoked.alert.action.cancel" = "Reddet"; /* Primary action for the alert when the subscription expires */ -"vpn.access-revoked.alert.action.subscribe" = "Subscribe"; +"vpn.access-revoked.alert.action.subscribe" = "Abone ol"; /* Alert message for the alert when the Privacy Pro subscription expiress */ -"vpn.access-revoked.alert.message" = "Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"vpn.access-revoked.alert.message" = "DuckDuckGo VPN'e yeniden bağlanmak için Privacy Pro'ya abone olun."; /* Alert title for the alert when the Privacy Pro subscription expires */ -"vpn.access-revoked.alert.title" = "VPN disconnected due to expired subscription"; +"vpn.access-revoked.alert.title" = "Abonelik süresi dolduğu için VPN bağlantısı kesildi"; /* Title for the VPN widget onboarding screen */ "vpn.addWidget.settings.title" = "DuckDuckGo'yu bulun ve seçin. Ardından VPN'e kaydırın ve Widget Ekle'yi seçin."; /* Title for the Cancel button of the VPN feedback form */ -"vpn.feedback-form.button.cancel" = "Cancel"; +"vpn.feedback-form.button.cancel" = "İptal"; /* Title for the Done button of the VPN feedback form */ -"vpn.feedback-form.button.done" = "Done"; +"vpn.feedback-form.button.done" = "Bitti"; /* Title for the Submit button of the VPN feedback form */ -"vpn.feedback-form.button.submit" = "Submit"; +"vpn.feedback-form.button.submit" = "Gönder"; /* Title for the Submitting state of the VPN feedback form */ -"vpn.feedback-form.button.submitting" = "Submitting…"; +"vpn.feedback-form.button.submitting" = "Gönderiliyor…"; /* Title for the browser crash/freeze category of the VPN feedback form */ -"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN causes browser to crash or freeze"; +"vpn.feedback-form.category.browser-crash-or-freeze" = "VPN tarayıcının çökmesine veya donmasına sebep oluyor"; /* Title for the 'VPN fails to connect' category of the VPN feedback form */ -"vpn.feedback-form.category.fails-to-connect" = "VPN fails to connect"; +"vpn.feedback-form.category.fails-to-connect" = "VPN bağlanamıyor"; /* Title for the 'VPN feature request' category of the VPN feedback form */ -"vpn.feedback-form.category.feature-request" = "VPN feature request"; +"vpn.feedback-form.category.feature-request" = "VPN özellik talebi"; /* Title for the category 'VPN causes issues with other apps or websites' category of the VPN feedback form */ -"vpn.feedback-form.category.issues-with-apps" = "VPN causes issues with other apps or websites"; +"vpn.feedback-form.category.issues-with-apps" = "VPN, diğer uygulamalar veya web sitelerinde sorunlara yol açıyor"; /* Title for the local device connectivity category of the VPN feedback form */ -"vpn.feedback-form.category.local-device-connectivity" = "VPN won't let me connect to local device"; +"vpn.feedback-form.category.local-device-connectivity" = "VPN yerel cihaza bağlanmama izin vermiyor"; /* Title for the 'other VPN feedback' category of the VPN feedback form */ -"vpn.feedback-form.category.other" = "Other VPN feedback"; +"vpn.feedback-form.category.other" = "Diğer VPN geri bildirimi"; /* Title for the category selection state of the VPN feedback form */ -"vpn.feedback-form.category.select-category" = "Select a category"; +"vpn.feedback-form.category.select-category" = "BİR KATEGORİ SEÇİN"; /* Title for the 'VPN is too slow' category of the VPN feedback form */ -"vpn.feedback-form.category.too-slow" = "VPN connection is too slow"; +"vpn.feedback-form.category.too-slow" = "VPN bağlantısı çok yavaş"; /* Title for the 'unable to install' category of the VPN feedback form */ -"vpn.feedback-form.category.unable-to-install" = "Unable to install VPN"; +"vpn.feedback-form.category.unable-to-install" = "VPN yüklenemiyor"; /* Title for the feedback sent view description of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.description" = "Your feedback will help us improve the\nDuckDuckGo VPN."; +"vpn.feedback-form.sending-confirmation.description" = "Geri bildiriminiz, DuckDuckGo VPN'i\ngeliştirmemize yardımcı olacak."; /* Title for the feedback sending error text of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.error" = "We couldn't send your feedback right now, please try again."; +"vpn.feedback-form.sending-confirmation.error" = "Geri bildiriminizi şu anda gönderemedik, lütfen tekrar deneyin."; /* Title for the feedback sent view title of the VPN feedback form */ -"vpn.feedback-form.sending-confirmation.title" = "Thank you!"; +"vpn.feedback-form.sending-confirmation.title" = "Teşekkür ederiz!"; /* Toast message when the VPN feedback form is submitted successfully */ -"vpn.feedback-form.submitted.message" = "Thank You! Feedback submitted."; +"vpn.feedback-form.submitted.message" = "Teşekkür Ederiz! Geri bildirim gönderildi."; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-1" = "Please describe what's happening, what you expected to happen, and the steps that led to the issue:"; +"vpn.feedback-form.text-1" = "Lütfen neler olduğunu, ne olmasını beklediğinizi ve soruna yol açan adımları açıklayın:"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-2" = "In addition to the details entered into this form, your app issue report will contain:"; +"vpn.feedback-form.text-2" = "Bu forma girilen ayrıntılara ek olarak, uygulama sorun raporunuz şunları içerecek:"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-3" = "• Whether specific DuckDuckGo features are enabled"; +"vpn.feedback-form.text-3" = "• Belirli DuckDuckGo özelliklerinin etkin olup olmadığı"; /* Bullet text for the body of the VPN feedback form */ -"vpn.feedback-form.text-4" = "• Aggregate DuckDuckGo app diagnostics"; +"vpn.feedback-form.text-4" = "• Toplu DuckDuckGo uygulama tanılamaları"; /* Text for the body of the VPN feedback form */ -"vpn.feedback-form.text-5" = "By tapping \"Submit\" I agree that DuckDuckGo may use the information in this report for purposes of improving the app's features."; +"vpn.feedback-form.text-5" = "\"Gönder\"e dokunarak, DuckDuckGo'nun bu rapordaki bilgileri uygulamanın özelliklerini geliştirmek amacıyla kullanabileceğini kabul ediyorum."; /* Title for each screen of the VPN feedback form */ -"vpn.feedback-form.title" = "Help Improve the DuckDuckGo VPN"; +"vpn.feedback-form.title" = "DuckDuckGo VPN'i geliştirmeye yardımcı olun"; /* VPN settings screen cell text for adding the VPN widget to the home screen */ "vpn.settings.add.widget" = "VPN Widget'ını Ana Ekrana Ekleyin"; /* Disclaimer for the DNS Server section on the DNS Server screen */ -"vpn.settings.dns.section-disclaimer" = "Using a custom DNS server can impact browsing speeds and expose your activity to third parties if the server isn't secure or reliable."; +"vpn.settings.dns.section-disclaimer" = "Özel bir DNS sunucusu kullanmak, tarama hızlarını etkileyebilir ve sunucu güvenli veya güvenilir değilse etkinliğini üçüncü taraflara ifşa edebilir."; /* Header text for the DNS section on the VPN Settings screen */ "vpn.settings.dns.section-header" = "DNS"; /* Title for the Apply button on the DNS Server setting screen */ -"vpn.settings.dns.server.apply.button.title" = "Apply"; +"vpn.settings.dns.server.apply.button.title" = "Uygula"; /* Default value for the DNS Server row on the VPN Settings screen */ "vpn.settings.dns.server.default.value" = "DuckDuckGo"; /* Title for the IPv4 Address setting */ -"vpn.settings.dns.server.ipv4.title" = "IPv4 Address"; +"vpn.settings.dns.server.ipv4.title" = "IPv4 Adresi"; /* Custom option for the DNS Server setting */ -"vpn.settings.dns.server.option.custom" = "Custom"; +"vpn.settings.dns.server.option.custom" = "Özelleştirme"; /* Recommended option for the DNS Server setting */ -"vpn.settings.dns.server.option.default" = "DuckDuckGo (Recommended)"; +"vpn.settings.dns.server.option.default" = "DuckDuckGo (Önerilen)"; /* Title for the DNS Server setting screen */ -"vpn.settings.dns.server.screen.title" = "DNS Server"; +"vpn.settings.dns.server.screen.title" = "DNS Sunucusu"; /* Title for the DNS Server row on the VPN Settings screen */ -"vpn.settings.dns.server.title" = "DNS Server"; +"vpn.settings.dns.server.title" = "DNS Sunucusu"; /* Title for the button to enable push notifications in system settings */ "waitlist.allow-notifications" = "Bildirimlere İzin Ver"; diff --git a/DuckDuckGo/tr.lproj/OmniBar.strings b/DuckDuckGo/tr.lproj/OmniBar.strings index e3b342a777..bf54dc9b79 100644 --- a/DuckDuckGo/tr.lproj/OmniBar.strings +++ b/DuckDuckGo/tr.lproj/OmniBar.strings @@ -7,6 +7,9 @@ /* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "Dt8-We-5tV"; */ "Dt8-We-5tV.accessibilityLabel" = "Metni Temizle"; +/* Class = "UIButton"; accessibilityLabel = "Clear Text"; ObjectID = "gb5-wL-4OW"; */ +"gb5-wL-4OW.accessibilityLabel" = "Metni Temizle"; + /* Class = "UIButton"; accessibilityLabel = "Share"; ObjectID = "hor-Jd-3kw"; */ "hor-Jd-3kw.accessibilityLabel" = "Paylaş"; diff --git a/DuckDuckGo/tr.lproj/Settings.strings b/DuckDuckGo/tr.lproj/Settings.strings index cef3e23ead..6577fe51e9 100644 --- a/DuckDuckGo/tr.lproj/Settings.strings +++ b/DuckDuckGo/tr.lproj/Settings.strings @@ -10,9 +10,6 @@ /* Class = "UILabel"; text = "Automatically Clear Data"; ObjectID = "5YD-Od-uDU"; */ "5YD-Od-uDU.text" = "Verileri Otomatik Olarak Temizle"; -/* Class = "UIButton"; normalTitle = "Settings"; ObjectID = "8YG-7Q-Czd"; */ -"8YG-7Q-Czd.normalTitle" = "Ayarlar"; - /* Class = "UILabel"; text = "App Exit, Inactive for 1 Hour"; ObjectID = "9EC-lx-77p"; */ "9EC-lx-77p.text" = "1 Saat Boyunca Hareket Yoksa Uygulamadan Çık"; @@ -76,9 +73,6 @@ /* Class = "UINavigationItem"; title = "Unprotected Sites"; ObjectID = "OHV-qC-tL9"; */ "OHV-qC-tL9.title" = "Korumasız Siteler"; -/* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ -"ssa-zd-L3T.title" = "Metin Boyutu"; - /* Class = "UITableViewSection"; headerTitle = "Show keyboard on"; ObjectID = "tGh-di-rfq"; */ "tGh-di-rfq.headerTitle" = "Şu durumda klavyeyi göster:"; diff --git a/DuckDuckGoTests/AIChat/AIChatSettingsTests.swift b/DuckDuckGoTests/AIChat/AIChatSettingsTests.swift new file mode 100644 index 0000000000..f150f8f470 --- /dev/null +++ b/DuckDuckGoTests/AIChat/AIChatSettingsTests.swift @@ -0,0 +1,207 @@ +// +// AIChatSettingsTests.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import XCTest +@testable import Core +@testable import DuckDuckGo +import BrowserServicesKit +import Combine + +class AIChatSettingsTests: XCTestCase { + + private var mockPrivacyConfigurationManager: PrivacyConfigurationManagerMock! + private var mockInternalUserDecider: MockInternalUserDecider! + private var mockUserDefaults: UserDefaults! + private var mockNotificationCenter: NotificationCenter! + + override func setUp() { + super.setUp() + mockPrivacyConfigurationManager = PrivacyConfigurationManagerMock() + mockInternalUserDecider = MockInternalUserDecider() + mockUserDefaults = UserDefaults(suiteName: "TestDefaults") + mockNotificationCenter = NotificationCenter() + } + + override func tearDown() { + mockUserDefaults.removePersistentDomain(forName: "TestDefaults") + mockPrivacyConfigurationManager = nil + mockInternalUserDecider = nil + mockUserDefaults = nil + mockNotificationCenter = nil + super.tearDown() + } + + func testAIChatURLReturnsDefaultWhenRemoteSettingsMissing() { + let settings = AIChatSettings(privacyConfigurationManager: mockPrivacyConfigurationManager, + internalUserDecider: mockInternalUserDecider, + userDefaults: mockUserDefaults, + notificationCenter: mockNotificationCenter) + + (mockPrivacyConfigurationManager.privacyConfig as? PrivacyConfigurationMock)?.settings = [:] + + let expectedURL = URL(string: AIChatSettings.SettingsValue.aiChatURL.defaultValue)! + XCTAssertEqual(settings.aiChatURL, expectedURL) + } + + func testAIChatURLReturnsRemoteSettingWhenAvailable() { + let settings = AIChatSettings(privacyConfigurationManager: mockPrivacyConfigurationManager, + internalUserDecider: mockInternalUserDecider, + userDefaults: mockUserDefaults, + notificationCenter: mockNotificationCenter) + + let remoteURL = "https://example.com/ai-chat" + (mockPrivacyConfigurationManager.privacyConfig as? PrivacyConfigurationMock)?.settings = [ + .aiChat: [AIChatSettings.SettingsValue.aiChatURL.rawValue: remoteURL] + ] + + XCTAssertEqual(settings.aiChatURL, URL(string: remoteURL)) + } + + func testIsAIChatFeatureEnabledWhenFeatureIsEnabled() { + let settings = AIChatSettings(privacyConfigurationManager: mockPrivacyConfigurationManager, + internalUserDecider: mockInternalUserDecider, + userDefaults: mockUserDefaults, + notificationCenter: mockNotificationCenter) + + (mockPrivacyConfigurationManager.privacyConfig as? PrivacyConfigurationMock)?.enabledFeaturesForVersions = [ + .aiChat: [AppVersionProvider().appVersion() ?? ""] + ] + + XCTAssertTrue(settings.isAIChatFeatureEnabled) + } + + func testIsAIChatFeatureEnabledForInternalUser() { + let settings = AIChatSettings(privacyConfigurationManager: mockPrivacyConfigurationManager, + internalUserDecider: mockInternalUserDecider, + userDefaults: mockUserDefaults, + notificationCenter: mockNotificationCenter) + + mockInternalUserDecider.mockIsInternalUser = true + XCTAssertTrue(settings.isAIChatFeatureEnabled) + } + + func testEnableAIChatBrowsingMenuUserSettings() { + let settings = AIChatSettings(privacyConfigurationManager: mockPrivacyConfigurationManager, + internalUserDecider: mockInternalUserDecider, + userDefaults: mockUserDefaults, + notificationCenter: mockNotificationCenter) + + (mockPrivacyConfigurationManager.privacyConfig as? PrivacyConfigurationMock)?.enabledFeaturesForVersions = [ + .aiChat: [AppVersionProvider().appVersion() ?? ""] + ] + + (mockPrivacyConfigurationManager.privacyConfig as? PrivacyConfigurationMock)?.enabledSubfeaturesForVersions = [ + AIChatSubfeature.browsingToolbarShortcut.rawValue: [AppVersionProvider().appVersion() ?? ""] + ] + settings.enableAIChatBrowsingMenuUserSettings(enable: false) + XCTAssertFalse(settings.isAIChatBrowsingMenuUserSettingsEnabled) + + settings.enableAIChatBrowsingMenuUserSettings(enable: true) + XCTAssertTrue(settings.isAIChatBrowsingMenuUserSettingsEnabled) + } + + func testEnableAIChatAddressBarUserSettings() { + let settings = AIChatSettings(privacyConfigurationManager: mockPrivacyConfigurationManager, + internalUserDecider: mockInternalUserDecider, + userDefaults: mockUserDefaults, + notificationCenter: mockNotificationCenter) + + (mockPrivacyConfigurationManager.privacyConfig as? PrivacyConfigurationMock)?.enabledFeaturesForVersions = [ + .aiChat: [AppVersionProvider().appVersion() ?? ""] + ] + + (mockPrivacyConfigurationManager.privacyConfig as? PrivacyConfigurationMock)?.enabledSubfeaturesForVersions = [ + AIChatSubfeature.addressBarShortcut.rawValue: [AppVersionProvider().appVersion() ?? ""] + ] + + settings.enableAIChatAddressBarUserSettings(enable: false) + XCTAssertFalse(settings.isAIChatAddressBarUserSettingsEnabled) + + settings.enableAIChatAddressBarUserSettings(enable: true) + XCTAssertTrue(settings.isAIChatAddressBarUserSettingsEnabled) + } + + func testNotificationPostedWhenSettingsChange() { + let settings = AIChatSettings(privacyConfigurationManager: mockPrivacyConfigurationManager, + internalUserDecider: mockInternalUserDecider, + userDefaults: mockUserDefaults, + notificationCenter: mockNotificationCenter) + + let expectation = self.expectation(description: "Notification should be posted") + + let observer = mockNotificationCenter.addObserver(forName: .aiChatSettingsChanged, object: nil, queue: nil) { _ in + expectation.fulfill() + } + + settings.enableAIChatBrowsingMenuUserSettings(enable: false) + waitForExpectations(timeout: 1, handler: nil) + mockNotificationCenter.removeObserver(observer) + } + + func testAIChatBrowsingMenuUserSettingsDisabledWhenToolbarShortcutFeatureDisabled() { + let settings = AIChatSettings(privacyConfigurationManager: mockPrivacyConfigurationManager, + internalUserDecider: mockInternalUserDecider, + userDefaults: mockUserDefaults, + notificationCenter: mockNotificationCenter) + + (mockPrivacyConfigurationManager.privacyConfig as? PrivacyConfigurationMock)?.enabledSubfeaturesForVersions = [ + AIChatSubfeature.browsingToolbarShortcut.rawValue: [] + ] + + settings.enableAIChatBrowsingMenuUserSettings(enable: true) + + XCTAssertFalse(settings.isAIChatBrowsingMenuUserSettingsEnabled) + } + + func testAIChatAddressBarUserSettingsDisabledWhenAddressBarShortcutFeatureDisabled() { + let settings = AIChatSettings(privacyConfigurationManager: mockPrivacyConfigurationManager, + internalUserDecider: mockInternalUserDecider, + userDefaults: mockUserDefaults, + notificationCenter: mockNotificationCenter) + + (mockPrivacyConfigurationManager.privacyConfig as? PrivacyConfigurationMock)?.enabledSubfeaturesForVersions = [ + AIChatSubfeature.addressBarShortcut.rawValue: [] + ] + + settings.enableAIChatAddressBarUserSettings(enable: true) + + XCTAssertFalse(settings.isAIChatAddressBarUserSettingsEnabled) + } + +} + +final private class MockInternalUserDecider: InternalUserDecider { + var mockIsInternalUser: Bool = false + var mockIsInternalUserPublisher: AnyPublisher { + Just(mockIsInternalUser).eraseToAnyPublisher() + } + + var isInternalUser: Bool { + return mockIsInternalUser + } + + var isInternalUserPublisher: AnyPublisher { + return mockIsInternalUserPublisher + } + + @discardableResult + func markUserAsInternalIfNeeded(forUrl url: URL?, response: HTTPURLResponse?) -> Bool { + return mockIsInternalUser + } +} diff --git a/DuckDuckGoTests/AIChat/OmnibarAccessoryHandlerTests.swift b/DuckDuckGoTests/AIChat/OmnibarAccessoryHandlerTests.swift new file mode 100644 index 0000000000..b91f8d060d --- /dev/null +++ b/DuckDuckGoTests/AIChat/OmnibarAccessoryHandlerTests.swift @@ -0,0 +1,100 @@ +// +// OmnibarAccessoryHandlerTests.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import XCTest +import AIChat +@testable import DuckDuckGo + +class OmnibarAccessoryHandlerTests: XCTestCase { + + static let DDGSearchURL = URL(string: "https://duckduckgo.com?q=hello")! + static let DDGHomeURL = URL(string: "https://duckduckgo.com")! + static let randomURL = URL(string: "https://potato.com")! + + func testOmnibarAccessoryWhenAIChatFeatureDisabled() { + let settings = MockAIChatSettingsProvider() + settings.isAIChatFeatureEnabled = false + let handler = OmnibarAccessoryHandler(settings: settings) + + let accessoryType = handler.omnibarAccessory(for: OmnibarAccessoryHandlerTests.DDGSearchURL) + + XCTAssertEqual(accessoryType, OmniBar.AccessoryType.share) + } + + func testOmnibarAccessoryWhenAIChatFeatureEnabledAndUserSettingsDisabled() { + let settings = MockAIChatSettingsProvider() + settings.isAIChatFeatureEnabled = true + settings.isAIChatAddressBarUserSettingsEnabled = false + let handler = OmnibarAccessoryHandler(settings: settings) + + let accessoryType = handler.omnibarAccessory(for: OmnibarAccessoryHandlerTests.DDGSearchURL) + + XCTAssertEqual(accessoryType, OmniBar.AccessoryType.share) + } + + func testOmnibarAccessoryWhenAIChatFeatureAndUserSettingsEnabledWithDuckDuckGoURL() { + let settings = MockAIChatSettingsProvider() + settings.isAIChatFeatureEnabled = true + settings.isAIChatAddressBarUserSettingsEnabled = true + let handler = OmnibarAccessoryHandler(settings: settings) + + let accessoryType = handler.omnibarAccessory(for: OmnibarAccessoryHandlerTests.DDGSearchURL) + + XCTAssertEqual(accessoryType, OmniBar.AccessoryType.chat) + } + + func testOmnibarAccessoryWhenAIChatFeatureAndUserSettingsEnabledWithNonDuckDuckGoURL() { + let settings = MockAIChatSettingsProvider() + settings.isAIChatFeatureEnabled = true + settings.isAIChatAddressBarUserSettingsEnabled = true + let handler = OmnibarAccessoryHandler(settings: settings) + + let accessoryType = handler.omnibarAccessory(for: OmnibarAccessoryHandlerTests.randomURL) + + XCTAssertEqual(accessoryType, OmniBar.AccessoryType.share) + } + + func testOmnibarAccessoryWhenAIChatFeatureAndUserSettingsEnabledWithDuckDuckGoHomeURL() { + let settings = MockAIChatSettingsProvider() + settings.isAIChatFeatureEnabled = true + settings.isAIChatAddressBarUserSettingsEnabled = true + let handler = OmnibarAccessoryHandler(settings: settings) + + let accessoryType = handler.omnibarAccessory(for: OmnibarAccessoryHandlerTests.DDGHomeURL) + + XCTAssertEqual(accessoryType, OmniBar.AccessoryType.share) + } +} + +private final class MockAIChatSettingsProvider: AIChatSettingsProvider { + var aiChatURL: URL = URL(string: "https://example.com")! + var isAIChatAddressBarUserSettingsEnabled: Bool = false + var isAIChatBrowsingMenuUserSettingsEnabled: Bool = false + var isAIChatFeatureEnabled: Bool = false + var isAIChatBrowsingMenubarShortcutFeatureEnabled: Bool = false + var isAIChatAddressBarShortcutFeatureEnabled: Bool = false + + func enableAIChatBrowsingMenuUserSettings(enable: Bool) { + isAIChatBrowsingMenuUserSettingsEnabled = enable + } + + func enableAIChatAddressBarUserSettings(enable: Bool) { + isAIChatAddressBarUserSettingsEnabled = enable + } +} diff --git a/DuckDuckGoTests/AutofillInterfaceEmailTruncatorTests.swift b/DuckDuckGoTests/AutofillInterfaceEmailTruncatorTests.swift index c247ea0d97..a3d9340697 100644 --- a/DuckDuckGoTests/AutofillInterfaceEmailTruncatorTests.swift +++ b/DuckDuckGoTests/AutofillInterfaceEmailTruncatorTests.swift @@ -18,6 +18,7 @@ // import XCTest +import Core @testable import DuckDuckGo class AutofillInterfaceEmailTruncatorTests: XCTestCase { diff --git a/DuckDuckGoTests/AutofillLoginListViewModelTests.swift b/DuckDuckGoTests/AutofillLoginListViewModelTests.swift index 0ff5c5ac3a..cdc7c8dabd 100644 --- a/DuckDuckGoTests/AutofillLoginListViewModelTests.swift +++ b/DuckDuckGoTests/AutofillLoginListViewModelTests.swift @@ -644,9 +644,9 @@ class AutofillLoginListItemViewModelTests: XCTestCase { let testData = [SecureVaultModels.WebsiteAccount(title: nil, username: "c", domain: domain), SecureVaultModels.WebsiteAccount(title: nil, username: "ç", domain: domain), SecureVaultModels.WebsiteAccount(title: nil, username: "C", domain: domain)] - let result = testData.autofillLoginListItemViewModelsForAccountsGroupedByFirstLetter(tld: tld, - autofillDomainNameUrlMatcher: autofillUrlMatcher, - autofillDomainNameUrlSort: autofillDomainNameUrlSort) + let result = testData.groupedByFirstLetter(tld: tld, + autofillDomainNameUrlMatcher: autofillUrlMatcher, + autofillDomainNameUrlSort: autofillDomainNameUrlSort) // Diacritics should be grouped with the root letter (in most cases), and grouping should be case insensative XCTAssertEqual(result.count, 1) } @@ -662,9 +662,9 @@ class AutofillLoginListItemViewModelTests: XCTestCase { SecureVaultModels.WebsiteAccount(title: nil, username: "?????", domain: domain), SecureVaultModels.WebsiteAccount(title: nil, username: "&%$£$%", domain: domain), SecureVaultModels.WebsiteAccount(title: nil, username: "99999", domain: domain)] - let result = testData.autofillLoginListItemViewModelsForAccountsGroupedByFirstLetter(tld: tld, - autofillDomainNameUrlMatcher: autofillUrlMatcher, - autofillDomainNameUrlSort: autofillDomainNameUrlSort) + let result = testData.groupedByFirstLetter(tld: tld, + autofillDomainNameUrlMatcher: autofillUrlMatcher, + autofillDomainNameUrlSort: autofillDomainNameUrlSort) // All non letters should be grouped together XCTAssertEqual(result.count, 1) } @@ -672,32 +672,32 @@ class AutofillLoginListItemViewModelTests: XCTestCase { func testWhenCreatingSectionsThenTitlesWithinASectionAreSortedCorrectly() { let domain = "whateverNotImportantForThisTest" let testData = ["e": [ - AutofillLoginListItemViewModel(account: SecureVaultModels.WebsiteAccount(title: "elephant", username: "1", domain: domain), - tld: tld, - autofillDomainNameUrlMatcher: autofillUrlMatcher, - autofillDomainNameUrlSort: autofillDomainNameUrlSort), - AutofillLoginListItemViewModel(account: SecureVaultModels.WebsiteAccount(title: "elephants", username: "2", domain: domain), - tld: tld, - autofillDomainNameUrlMatcher: autofillUrlMatcher, - autofillDomainNameUrlSort: autofillDomainNameUrlSort), - AutofillLoginListItemViewModel(account: SecureVaultModels.WebsiteAccount(title: "Elephant", username: "3", domain: domain), - tld: tld, - autofillDomainNameUrlMatcher: autofillUrlMatcher, - autofillDomainNameUrlSort: autofillDomainNameUrlSort), - AutofillLoginListItemViewModel(account: SecureVaultModels.WebsiteAccount(title: "èlephant", username: "4", domain: domain), - tld: tld, - autofillDomainNameUrlMatcher: autofillUrlMatcher, - autofillDomainNameUrlSort: autofillDomainNameUrlSort), - AutofillLoginListItemViewModel(account: SecureVaultModels.WebsiteAccount(title: "è", username: "5", domain: domain), - tld: tld, - autofillDomainNameUrlMatcher: autofillUrlMatcher, - autofillDomainNameUrlSort: autofillDomainNameUrlSort), - AutofillLoginListItemViewModel(account: SecureVaultModels.WebsiteAccount(title: nil, username: "ezy", domain: domain), - tld: tld, - autofillDomainNameUrlMatcher: autofillUrlMatcher, - autofillDomainNameUrlSort: autofillDomainNameUrlSort)]] - let result = testData.autofillLoginListSectionsForViewModelsSortedByTitle(autofillDomainNameUrlSort, - tld: tld) + AutofillLoginItem(account: SecureVaultModels.WebsiteAccount(title: "elephant", username: "1", domain: domain), + tld: tld, + autofillDomainNameUrlMatcher: autofillUrlMatcher, + autofillDomainNameUrlSort: autofillDomainNameUrlSort), + AutofillLoginItem(account: SecureVaultModels.WebsiteAccount(title: "elephants", username: "2", domain: domain), + tld: tld, + autofillDomainNameUrlMatcher: autofillUrlMatcher, + autofillDomainNameUrlSort: autofillDomainNameUrlSort), + AutofillLoginItem(account: SecureVaultModels.WebsiteAccount(title: "Elephant", username: "3", domain: domain), + tld: tld, + autofillDomainNameUrlMatcher: autofillUrlMatcher, + autofillDomainNameUrlSort: autofillDomainNameUrlSort), + AutofillLoginItem(account: SecureVaultModels.WebsiteAccount(title: "èlephant", username: "4", domain: domain), + tld: tld, + autofillDomainNameUrlMatcher: autofillUrlMatcher, + autofillDomainNameUrlSort: autofillDomainNameUrlSort), + AutofillLoginItem(account: SecureVaultModels.WebsiteAccount(title: "è", username: "5", domain: domain), + tld: tld, + autofillDomainNameUrlMatcher: autofillUrlMatcher, + autofillDomainNameUrlSort: autofillDomainNameUrlSort), + AutofillLoginItem(account: SecureVaultModels.WebsiteAccount(title: nil, username: "ezy", domain: domain), + tld: tld, + autofillDomainNameUrlMatcher: autofillUrlMatcher, + autofillDomainNameUrlSort: autofillDomainNameUrlSort)]] + let result = testData.sortedIntoSections(autofillDomainNameUrlSort, + tld: tld) if case .credentials(_, let viewModels) = result[0] { XCTAssertEqual(viewModels[0].title, "è") XCTAssertEqual(viewModels[1].title, "elephant") @@ -719,9 +719,9 @@ class AutofillLoginListItemViewModelTests: XCTestCase { SecureVaultModels.WebsiteAccount(title: nil, username: "test", domain: "auth.test.example.com"), SecureVaultModels.WebsiteAccount(title: nil, username: "test", domain: "https://www.auth.example.com"), SecureVaultModels.WebsiteAccount(title: nil, username: "test", domain: "https://www.example.com")] - let result = testData.autofillLoginListItemViewModelsForAccountsGroupedByFirstLetter(tld: tld, - autofillDomainNameUrlMatcher: autofillUrlMatcher, - autofillDomainNameUrlSort: autofillDomainNameUrlSort) + let result = testData.groupedByFirstLetter(tld: tld, + autofillDomainNameUrlMatcher: autofillUrlMatcher, + autofillDomainNameUrlSort: autofillDomainNameUrlSort) // Diacritics should be grouped with the root letter (in most cases), and grouping should be case insensative XCTAssertEqual(result.count, 1) } diff --git a/DuckDuckGoTests/BrowserComparisonModelTests.swift b/DuckDuckGoTests/BrowserComparisonModelTests.swift index 846c986f20..5dcbc51631 100644 --- a/DuckDuckGoTests/BrowserComparisonModelTests.swift +++ b/DuckDuckGoTests/BrowserComparisonModelTests.swift @@ -21,126 +21,46 @@ import XCTest @testable import DuckDuckGo final class BrowserComparisonModelTests: XCTestCase { - private var onboardingManager: OnboardingManagerMock! - - override func setUpWithError() throws { - try super.setUpWithError() - onboardingManager = OnboardingManagerMock() - } - - override func tearDownWithError() throws { - onboardingManager = nil - try super.tearDownWithError() - } - - func testWhenIsNotHighlightsThenBrowserComparisonFeaturePrivateSearchIsCorrect() throws { - // GIVEN - try [false, true].forEach { isOnboardingHighlightsEnabled in - onboardingManager.isOnboardingHighlightsEnabled = isOnboardingHighlightsEnabled - BrowsersComparisonModel.PrivacyFeature.FeatureType.onboardingManager = onboardingManager - - // WHEN - let result = try XCTUnwrap(BrowsersComparisonModel.privacyFeatures.first(where: { $0.type == .privateSearch })?.type.title) - - // THEN - XCTAssertEqual(result, UserText.DaxOnboardingExperiment.BrowsersComparison.Features.privateSearch) - } - } - - func testWhenIsNotHighlightsThenBrowserComparisonFeatureBlockThirdPartyTrackersIsCorrect() throws { - // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = false - BrowsersComparisonModel.PrivacyFeature.FeatureType.onboardingManager = onboardingManager + func testBrowserComparisonFeaturePrivateSearchIsCorrect() throws { // WHEN - let result = try XCTUnwrap(BrowsersComparisonModel.privacyFeatures.first(where: { $0.type == .blockThirdPartyTrackers })?.type.title) + let result = try XCTUnwrap(BrowsersComparisonModel.privacyFeatures.first(where: { $0.type == .privateSearch })?.type.title) // THEN - XCTAssertEqual(result, UserText.DaxOnboardingExperiment.BrowsersComparison.Features.trackerBlockers) - } - - func testWhenIsHighlightsThenBrowserComparisonFeatureBlockThirdPartyTrackersIsCorrect() throws { - // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = true - BrowsersComparisonModel.PrivacyFeature.FeatureType.onboardingManager = onboardingManager + XCTAssertEqual(result, UserText.Onboarding.BrowsersComparison.Features.privateSearch) - // WHEN - let result = try XCTUnwrap(BrowsersComparisonModel.privacyFeatures.first(where: { $0.type == .blockThirdPartyTrackers })?.type.title) - - // THEN - XCTAssertEqual(result, UserText.HighlightsOnboardingExperiment.BrowsersComparison.Features.trackerBlockers) } - func testWhenIsNotHighlightsThenBrowserComparisonFeatureBlockCookiePopupsIsCorrect() throws { - // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = false - BrowsersComparisonModel.PrivacyFeature.FeatureType.onboardingManager = onboardingManager - + func testBrowserComparisonFeatureBlockThirdPartyTrackersIsCorrect() throws { // WHEN - let result = try XCTUnwrap(BrowsersComparisonModel.privacyFeatures.first(where: { $0.type == .blockCookiePopups })?.type.title) + let result = try XCTUnwrap(BrowsersComparisonModel.privacyFeatures.first(where: { $0.type == .blockThirdPartyTrackers })?.type.title) // THEN - XCTAssertEqual(result, UserText.DaxOnboardingExperiment.BrowsersComparison.Features.cookiePopups) + XCTAssertEqual(result, UserText.Onboarding.BrowsersComparison.Features.trackerBlockers) } - func testWhenIsHighlightsThenBrowserComparisonFeatureBlockCookiePopupsIsCorrect() throws { - // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = true - BrowsersComparisonModel.PrivacyFeature.FeatureType.onboardingManager = onboardingManager - + func testBrowserComparisonFeatureBlockCookiePopupsIsCorrect() throws { // WHEN let result = try XCTUnwrap(BrowsersComparisonModel.privacyFeatures.first(where: { $0.type == .blockCookiePopups })?.type.title) // THEN - XCTAssertEqual(result, UserText.HighlightsOnboardingExperiment.BrowsersComparison.Features.cookiePopups) - } - - func testWhenIsNotHighlightsThenBrowserComparisonFeatureBlockCreepyAdsIsCorrect() throws { - // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = false - BrowsersComparisonModel.PrivacyFeature.FeatureType.onboardingManager = onboardingManager - - // WHEN - let result = try XCTUnwrap(BrowsersComparisonModel.privacyFeatures.first(where: { $0.type == .blockCreepyAds })?.type.title) - - // THEN - XCTAssertEqual(result, UserText.DaxOnboardingExperiment.BrowsersComparison.Features.creepyAds) + XCTAssertEqual(result, UserText.Onboarding.BrowsersComparison.Features.cookiePopups) } - func testWhenIsHighlightsThenBrowserComparisonFeatureBlockCreepyAdsIsCorrect() throws { - // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = true - BrowsersComparisonModel.PrivacyFeature.FeatureType.onboardingManager = onboardingManager - + func testBrowserComparisonFeatureBlockCreepyAdsIsCorrect() throws { // WHEN let result = try XCTUnwrap(BrowsersComparisonModel.privacyFeatures.first(where: { $0.type == .blockCreepyAds })?.type.title) // THEN - XCTAssertEqual(result, UserText.HighlightsOnboardingExperiment.BrowsersComparison.Features.creepyAds) + XCTAssertEqual(result, UserText.Onboarding.BrowsersComparison.Features.creepyAds) } func testWhenIsNotHighlightsThenBrowserComparisonFeatureEraseBrowsingDataIsCorrect() throws { - // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = false - BrowsersComparisonModel.PrivacyFeature.FeatureType.onboardingManager = onboardingManager - - // WHEN - let result = try XCTUnwrap(BrowsersComparisonModel.privacyFeatures.first(where: { $0.type == .eraseBrowsingData })?.type.title) - - // THEN - XCTAssertEqual(result, UserText.DaxOnboardingExperiment.BrowsersComparison.Features.eraseBrowsingData) - } - - func testWhenIsHighlightsThenBrowserComparisonFeatureEraseBrowsingDataIsCorrect() throws { - // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = true - BrowsersComparisonModel.PrivacyFeature.FeatureType.onboardingManager = onboardingManager - // WHEN let result = try XCTUnwrap(BrowsersComparisonModel.privacyFeatures.first(where: { $0.type == .eraseBrowsingData })?.type.title) // THEN - XCTAssertEqual(result, UserText.HighlightsOnboardingExperiment.BrowsersComparison.Features.eraseBrowsingData) + XCTAssertEqual(result, UserText.Onboarding.BrowsersComparison.Features.eraseBrowsingData) } } diff --git a/DuckDuckGoTests/ContextualOnboardingPresenterTests.swift b/DuckDuckGoTests/ContextualOnboardingPresenterTests.swift index 5220b6dff9..c27f8caa2d 100644 --- a/DuckDuckGoTests/ContextualOnboardingPresenterTests.swift +++ b/DuckDuckGoTests/ContextualOnboardingPresenterTests.swift @@ -34,36 +34,9 @@ final class ContextualOnboardingPresenterTests: XCTestCase { try super.tearDownWithError() } - - func testWhenPresentContextualOnboardingAndVariantDoesNotSupportContextualDaxDialogsThenOldContextualOnboardingIsPresented() throws { + func testWhenPresentContextualOnboardingThenNewContextualOnboardingIsPresented() { // GIVEN - var variantManagerMock = MockVariantManager() - variantManagerMock.isSupportedBlock = { feature in - feature != .contextualDaxDialogs - } - let sut = ContextualOnboardingPresenter(variantManager: variantManagerMock, daxDialogsFactory: contextualDaxDialogsFactory) - let parent = TabViewControllerMock() - XCTAssertFalse(parent.didCallPerformSegue) - XCTAssertNil(parent.capturedSegueIdentifier) - XCTAssertNil(parent.capturedSender) - - // WHEN - sut.presentContextualOnboarding(for: .afterSearch, in: parent) - - // THEN - XCTAssertTrue(parent.didCallPerformSegue) - XCTAssertEqual(parent.capturedSegueIdentifier, "DaxDialog") - let sender = try XCTUnwrap(parent.capturedSender as? DaxDialogs.BrowsingSpec) - XCTAssertEqual(sender, DaxDialogs.BrowsingSpec.afterSearch) - } - - func testWhenPresentContextualOnboardingAndVariantSupportsContextualDaxDialogsThenThenNewContextualOnboardingIsPresented() { - // GIVEN - var variantManagerMock = MockVariantManager() - variantManagerMock.isSupportedBlock = { feature in - feature == .contextualDaxDialogs - } - let sut = ContextualOnboardingPresenter(variantManager: variantManagerMock, daxDialogsFactory: contextualDaxDialogsFactory) + let sut = ContextualOnboardingPresenter(variantManager: MockVariantManager(), daxDialogsFactory: contextualDaxDialogsFactory) let parent = TabViewControllerMock() XCTAssertFalse(parent.didCallAddChild) XCTAssertNil(parent.capturedChild) @@ -78,12 +51,8 @@ final class ContextualOnboardingPresenterTests: XCTestCase { func testWhenPresentContextualOnboardingForFireEducational_andBarAtTheTop_TheMessageHandPointsInTheRightDirection() throws { // GIVEN - var variantManagerMock = MockVariantManager() - variantManagerMock.isSupportedBlock = { feature in - feature == .contextualDaxDialogs - } let appSettings = AppSettingsMock() - let sut = ContextualOnboardingPresenter(variantManager: variantManagerMock, daxDialogsFactory: contextualDaxDialogsFactory, appSettings: appSettings) + let sut = ContextualOnboardingPresenter(variantManager: MockVariantManager(), daxDialogsFactory: contextualDaxDialogsFactory, appSettings: appSettings) let parent = TabViewControllerMock() // WHEN @@ -96,13 +65,9 @@ final class ContextualOnboardingPresenterTests: XCTestCase { func testWhenPresentContextualOnboardingForFireEducational_andBarAtTheBottom_TheMessageHandPointsInTheRightDirection() throws { // GIVEN - var variantManagerMock = MockVariantManager() - variantManagerMock.isSupportedBlock = { feature in - feature == .contextualDaxDialogs - } let appSettings = AppSettingsMock() appSettings.currentAddressBarPosition = .bottom - let sut = ContextualOnboardingPresenter(variantManager: variantManagerMock, daxDialogsFactory: contextualDaxDialogsFactory, appSettings: appSettings) + let sut = ContextualOnboardingPresenter(variantManager: MockVariantManager(), daxDialogsFactory: contextualDaxDialogsFactory, appSettings: appSettings) let parent = TabViewControllerMock() // WHEN @@ -113,14 +78,10 @@ final class ContextualOnboardingPresenterTests: XCTestCase { XCTAssertTrue(view.message.string.contains("👇")) } - func testWhenDismissContextualOnboardingAndVariantSupportsContextualDaxDialogsThenContextualOnboardingIsDismissed() { + func testWhenDismissContextualOnboardingThenContextualOnboardingIsDismissed() { // GIVEN let expectation = self.expectation(description: #function) - var variantManagerMock = MockVariantManager() - variantManagerMock.isSupportedBlock = { feature in - feature == .contextualDaxDialogs - } - let sut = ContextualOnboardingPresenter(variantManager: variantManagerMock, daxDialogsFactory: contextualDaxDialogsFactory) + let sut = ContextualOnboardingPresenter(variantManager: MockVariantManager(), daxDialogsFactory: contextualDaxDialogsFactory) let parent = TabViewControllerMock() let daxController = DaxContextualOnboardingControllerMock() daxController.removeFromParentExpectation = expectation @@ -140,29 +101,6 @@ final class ContextualOnboardingPresenterTests: XCTestCase { XCTAssertFalse(parent.daxDialogsStackView.arrangedSubviews.contains(daxController.view)) } - func testWhenDismissContextualOnboardingAndVariantDoesNotSupportsContextualDaxDialogsThenNothingHappens() { - // GIVEN - let expectation = self.expectation(description: #function) - expectation.isInverted = true - var variantManagerMock = MockVariantManager() - variantManagerMock.isSupportedBlock = { feature in - feature != .contextualDaxDialogs - } - let sut = ContextualOnboardingPresenter(variantManager: variantManagerMock, daxDialogsFactory: contextualDaxDialogsFactory) - let parent = TabViewControllerMock() - let daxController = DaxContextualOnboardingControllerMock() - daxController.removeFromParentExpectation = expectation - parent.daxContextualOnboardingController = daxController - XCTAssertFalse(daxController.didCallRemoveFromParent) - - // WHEN - sut.dismissContextualOnboardingIfNeeded(from: parent) - - // THEN - waitForExpectations(timeout: 0.4) - XCTAssertFalse(daxController.didCallRemoveFromParent) - } - } final class TabViewControllerMock: UIViewController, TabViewOnboardingDelegate { diff --git a/DuckDuckGoTests/DaxDialogTests.swift b/DuckDuckGoTests/DaxDialogTests.swift index 7a512f5cae..dab3924ac5 100644 --- a/DuckDuckGoTests/DaxDialogTests.swift +++ b/DuckDuckGoTests/DaxDialogTests.swift @@ -71,34 +71,17 @@ final class DaxDialog: XCTestCase { setupUserDefault(with: #file) entityProvider = MockEntityProvider() } - - func testWhenResumingRegularFlowThenNextHomeMessageIsBlankUntilBrowsingMessagesShown() { - mockVariantManager.isSupportedReturns = false - onboarding.enableAddFavoriteFlow() - onboarding.resumeRegularFlow() - XCTAssertNil(onboarding.nextHomeScreenMessage()) - XCTAssertEqual(settings.homeScreenMessagesSeen, 1) - XCTAssertNotNil(onboarding.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.google))) - XCTAssertEqual(onboarding.nextHomeScreenMessage(), .final) - XCTAssertEqual(settings.homeScreenMessagesSeen, 2) - } func testWhenStartingAddFavoriteFlowThenNextMessageIsAddFavorite() { + // WHEN onboarding.enableAddFavoriteFlow() - XCTAssertEqual(onboarding.nextHomeScreenMessage(), .addFavorite) - XCTAssertEqual(settings.homeScreenMessagesSeen, 1) - XCTAssertTrue(onboarding.isAddFavoriteFlow) - } - func testWhenStartingNextMessageAndAddFavoriteFlowThenNextHomeScreenMessagesSeenDoesNotIncrement() { - XCTAssertNotNil(onboarding.nextHomeScreenMessage()) - XCTAssertEqual(settings.homeScreenMessagesSeen, 1) - onboarding.enableAddFavoriteFlow() - XCTAssertEqual(onboarding.nextHomeScreenMessage(), .addFavorite) - XCTAssertEqual(settings.homeScreenMessagesSeen, 1) + // THEN + XCTAssertEqual(onboarding.nextHomeScreenMessageNew(), .addFavorite) + XCTAssertTrue(onboarding.isAddFavoriteFlow) } - func testWhenEachVersionOfTrackersMessageIsShownThenFormattedCorrectlyAndNotShownAgain() { + func testWhenEachVersionOfTrackersMessageIsShownThenFormattedCorrectly() { let testCases = [ (urls: [ URLs.google ], expected: DaxDialogs.BrowsingSpec.withOneTracker.format(args: "Google"), line: #line), (urls: [ URLs.google, URLs.amazon ], expected: DaxDialogs.BrowsingSpec.withMultipleTrackers.format(args: 0, "Google", "Amazon.com"), line: #line), @@ -124,172 +107,9 @@ final class DaxDialog: XCTestCase { // Assert the expected case XCTAssertEqual(testCase.expected, onboarding.nextBrowsingMessageIfShouldShow(for: privacyInfo), line: UInt(testCase.line)) - - // Also assert the we don't see the message on subsequent calls - XCTAssertTrue(onboarding.shouldShowFireButtonPulse) - XCTAssertNil(onboarding.nextBrowsingMessageIfShouldShow(for: privacyInfo), line: UInt(testCase.line)) } } - - func testWhenTrackersShownThenFireEducationShown() { - let privacyInfo = makePrivacyInfo(url: URLs.example) - privacyInfo.trackerInfo.addDetectedTracker(detectedTrackerFrom(URLs.google, pageUrl: URLs.example.absoluteString), - onPageWithURL: URLs.example) - XCTAssertFalse(onboarding.shouldShowFireButtonPulse) - XCTAssertNotNil(onboarding.nextBrowsingMessageIfShouldShow(for: privacyInfo)) - XCTAssertTrue(onboarding.shouldShowFireButtonPulse) - XCTAssertNil(onboarding.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.example))) - } - - func testWhenMajorTrackerShownThenFireEducationShown() { - let privacyInfo = makePrivacyInfo(url: URLs.google) - XCTAssertFalse(onboarding.shouldShowFireButtonPulse) - XCTAssertNotNil(onboarding.nextBrowsingMessageIfShouldShow(for: privacyInfo)) - XCTAssertTrue(onboarding.shouldShowFireButtonPulse) - XCTAssertNil(onboarding.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.example))) - } - - func testWhenSearchShownThenNoTrackersIsShown() { - XCTAssertFalse(onboarding.shouldShowFireButtonPulse) - XCTAssertNotNil(onboarding.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.ddg))) - XCTAssertNotNil(onboarding.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.example))) - XCTAssertTrue(onboarding.shouldShowFireButtonPulse) - XCTAssertNil(onboarding.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.example))) - } - - func testWhenMajorTrackerShownThenNoTrackersIsNotShown() { - XCTAssertFalse(onboarding.shouldShowFireButtonPulse) - XCTAssertNotNil(onboarding.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.facebook))) - XCTAssertTrue(onboarding.shouldShowFireButtonPulse) - XCTAssertNil(onboarding.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.example))) - } - - func testWhenTrackersShownThenNoTrackersIsNotShown() { - XCTAssertFalse(onboarding.shouldShowFireButtonPulse) - XCTAssertNotNil(onboarding.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.amazon))) - XCTAssertTrue(onboarding.shouldShowFireButtonPulse) - XCTAssertNil(onboarding.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.example))) - } - - func testWhenMajorTrackerShownThenOwnedByIsNotShown() { - XCTAssertFalse(onboarding.shouldShowFireButtonPulse) - XCTAssertNotNil(onboarding.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.facebook))) - XCTAssertTrue(onboarding.shouldShowFireButtonPulse) - XCTAssertNil(onboarding.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.ownedByFacebook))) - } - - func testWhenSecondTimeOnSiteThatIsOwnedByFacebookThenShowNothingAfterFireEducation() { - let privacyInfo = makePrivacyInfo(url: URLs.ownedByFacebook) - XCTAssertFalse(onboarding.shouldShowFireButtonPulse) - XCTAssertNotNil(onboarding.nextBrowsingMessageIfShouldShow(for: privacyInfo)) - XCTAssertTrue(onboarding.shouldShowFireButtonPulse) - XCTAssertNil(onboarding.nextBrowsingMessageIfShouldShow(for: privacyInfo)) - } - - func testWhenFirstTimeOnSiteThatIsOwnedByFacebookThenShowOwnedByMajorTrackingMessage() { - let privacyInfo = makePrivacyInfo(url: URLs.ownedByFacebook) - XCTAssertFalse(onboarding.shouldShowFireButtonPulse) - XCTAssertEqual(DaxDialogs.BrowsingSpec.siteOwnedByMajorTracker.format(args: "instagram.com", "Facebook", 39.0), - onboarding.nextBrowsingMessageIfShouldShow(for: privacyInfo)) - } - - func testWhenSecondTimeOnSiteThatIsMajorTrackerThenShowNothingAfterFireEducation() { - let privacyInfo = makePrivacyInfo(url: URLs.facebook) - XCTAssertFalse(onboarding.shouldShowFireButtonPulse) - XCTAssertNotNil(onboarding.nextBrowsingMessageIfShouldShow(for: privacyInfo)) - XCTAssertTrue(onboarding.shouldShowFireButtonPulse) - XCTAssertNil(onboarding.nextBrowsingMessageIfShouldShow(for: privacyInfo)) - } - - func testWhenFirstTimeOnFacebookThenShowMajorTrackingMessage() { - let privacyInfo = makePrivacyInfo(url: URLs.facebook) - XCTAssertFalse(onboarding.shouldShowFireButtonPulse) - XCTAssertEqual(DaxDialogs.BrowsingSpec.siteIsMajorTracker.format(args: "Facebook", URLs.facebook.host ?? ""), - onboarding.nextBrowsingMessageIfShouldShow(for: privacyInfo)) - } - - func testWhenFirstTimeOnGoogleThenShowMajorTrackingMessage() { - let privacyInfo = makePrivacyInfo(url: URLs.google) - XCTAssertFalse(onboarding.shouldShowFireButtonPulse) - XCTAssertEqual(DaxDialogs.BrowsingSpec.siteIsMajorTracker.format(args: "Google", URLs.google.host ?? ""), - onboarding.nextBrowsingMessageIfShouldShow(for: privacyInfo)) - } - - func testWhenSecondTimeOnPageWithNoTrackersThenTrackersThenShowFireEducation() { - let privacyInfo = makePrivacyInfo(url: URLs.example) - XCTAssertFalse(onboarding.shouldShowFireButtonPulse) - XCTAssertNotNil(onboarding.nextBrowsingMessageIfShouldShow(for: privacyInfo)) - XCTAssertTrue(onboarding.shouldShowFireButtonPulse) - XCTAssertNil(onboarding.nextBrowsingMessageIfShouldShow(for: privacyInfo)) - } - - func testWhenFirstTimeOnPageWithNoTrackersThenTrackersThenShowNoTrackersMessage() { - let privacyInfo = makePrivacyInfo(url: URLs.example) - XCTAssertFalse(onboarding.shouldShowFireButtonPulse) - XCTAssertEqual(DaxDialogs.BrowsingSpec.withoutTrackers, onboarding.nextBrowsingMessageIfShouldShow(for: privacyInfo)) - } - - func testWhenSecondTimeOnSearchPageThenShowNothing() { - XCTAssertNotNil(onboarding.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.ddg))) - XCTAssertNil(onboarding.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.ddg))) - } - - func testWhenFirstTimeOnSearchPageThenShowSearchPageMessage() { - XCTAssertFalse(onboarding.shouldShowFireButtonPulse) - XCTAssertEqual(DaxDialogs.BrowsingSpec.afterSearch, onboarding.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.ddg))) - } - - func testWhenOnSamePageAndPresenceOfTrackersChangesThenShowOnlyOneMessage() { - let privacyInfo = makePrivacyInfo(url: URLs.example) - XCTAssertEqual(DaxDialogs.BrowsingSpec.withoutTrackers, onboarding.nextBrowsingMessageIfShouldShow(for: privacyInfo)) - - let privacyInfoWithTrackers = makePrivacyInfo(url: URLs.google) - privacyInfo.trackerInfo = privacyInfoWithTrackers.trackerInfo - XCTAssertNil(onboarding.nextBrowsingMessageIfShouldShow(for: privacyInfo)) - } - - func testWhenDimissedThenShowNothing() { - onboarding.dismiss() - XCTAssertNil(onboarding.nextHomeScreenMessage()) - XCTAssertEqual(settings.homeScreenMessagesSeen, 0) - XCTAssertNil(onboarding.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.example))) - XCTAssertFalse(onboarding.shouldShowFireButtonPulse) - } - - func testWhenThirdTimeOnHomeScreenAndFireEducationSeenThenShowNothing() { - XCTAssertFalse(onboarding.shouldShowFireButtonPulse) - XCTAssertNotNil(onboarding.nextHomeScreenMessage()) - XCTAssertEqual(settings.homeScreenMessagesSeen, 1) - XCTAssertNotNil(onboarding.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.example))) - XCTAssertTrue(onboarding.shouldShowFireButtonPulse) - XCTAssertEqual(DaxDialogs.HomeScreenSpec.final, onboarding.nextHomeScreenMessage()) - XCTAssertEqual(settings.homeScreenMessagesSeen, 2) - XCTAssertNil(onboarding.nextHomeScreenMessage()) - XCTAssertEqual(settings.homeScreenMessagesSeen, 2) - } - - func testWhenSecondTimeOnHomeScreenAndFireEducationSeenThenShowSubsequentDialog() { - XCTAssertFalse(onboarding.shouldShowFireButtonPulse) - XCTAssertNotNil(onboarding.nextHomeScreenMessage()) - XCTAssertEqual(settings.homeScreenMessagesSeen, 1) - XCTAssertNotNil(onboarding.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.example))) - XCTAssertTrue(onboarding.shouldShowFireButtonPulse) - XCTAssertEqual(DaxDialogs.HomeScreenSpec.final, onboarding.nextHomeScreenMessage()) - XCTAssertEqual(settings.homeScreenMessagesSeen, 2) - } - - func testWhenSecondTimeOnHomeScreenAndNoOtherDialogsSeenThenShowNothing() { - XCTAssertNotNil(onboarding.nextHomeScreenMessage()) - XCTAssertEqual(settings.homeScreenMessagesSeen, 1) - XCTAssertNil(onboarding.nextHomeScreenMessage()) - XCTAssertEqual(settings.homeScreenMessagesSeen, 1) - } - - func testWhenFirstTimeOnHomeScreenThenShowFirstDialog() { - XCTAssertEqual(DaxDialogs.HomeScreenSpec.initial, onboarding.nextHomeScreenMessage()) - XCTAssertEqual(settings.homeScreenMessagesSeen, 1) - } func testWhenPrimingDaxDialogForUseThenDismissedIsFalse() { let settings = InMemoryDaxDialogsSettings() @@ -304,12 +124,9 @@ final class DaxDialog: XCTestCase { XCTAssertTrue(DefaultDaxDialogsSettings().isDismissed) } - // MARK: - Experiment - - func testWhenExperimentAndBrowsingSpecIsWithOneTrackerThenHighlightAddressBarIsFalse() throws { + func testWhenBrowsingSpecIsWithOneTrackerThenHighlightAddressBarIsFalse() throws { // GIVEN - mockVariantManager.isSupportedReturns = true - let sut = makeExperimentSUT(settings: InMemoryDaxDialogsSettings()) + let sut = makeSUT(settings: InMemoryDaxDialogsSettings()) let privacyInfo = makePrivacyInfo(url: URLs.example) let detectedTracker = detectedTrackerFrom(URLs.google, pageUrl: URLs.example.absoluteString) privacyInfo.trackerInfo.addDetectedTracker(detectedTracker, onPageWithURL: URLs.example) @@ -322,10 +139,9 @@ final class DaxDialog: XCTestCase { XCTAssertFalse(result.highlightAddressBar) } - func testWhenExperimentAndBrowsingSpecIsWithMultipleTrackerThenHighlightAddressBarIsFalse() throws { + func testWhenBrowsingSpecIsWithMultipleTrackerThenHighlightAddressBarIsFalse() throws { // GIVEN - mockVariantManager.isSupportedReturns = true - let sut = makeExperimentSUT(settings: InMemoryDaxDialogsSettings()) + let sut = makeSUT(settings: InMemoryDaxDialogsSettings()) let privacyInfo = makePrivacyInfo(url: URLs.example) [URLs.google, URLs.amazon].forEach { tracker in let detectedTracker = detectedTrackerFrom(tracker, pageUrl: URLs.example.absoluteString) @@ -340,11 +156,11 @@ final class DaxDialog: XCTestCase { XCTAssertFalse(result.highlightAddressBar) } - func testWhenExperimentGroupAndURLIsDuckDuckGoSearchAndSearchDialogHasNotBeenSeenThenReturnSpecTypeAfterSearch() { + func testWhenURLIsDuckDuckGoSearchAndSearchDialogHasNotBeenSeenThenReturnSpecTypeAfterSearch() { // GIVEN let settings = InMemoryDaxDialogsSettings() settings.browsingAfterSearchShown = false - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) // WHEN let result = sut.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.ddg)) @@ -353,11 +169,11 @@ final class DaxDialog: XCTestCase { XCTAssertEqual(result?.type, .afterSearch) } - func testWhenExperimentGroupAndURLIsMajorTrackerWebsiteAndMajorTrackerDialogHasNotBeenSeenThenReturnSpecTypeSiteIsMajorTracker() { + func testWhenURLIsMajorTrackerWebsiteAndMajorTrackerDialogHasNotBeenSeenThenReturnSpecTypeSiteIsMajorTracker() { // GIVEN let settings = InMemoryDaxDialogsSettings() settings.browsingMajorTrackingSiteShown = false - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) let privacyInfo = makePrivacyInfo(url: URLs.facebook) // WHEN @@ -367,11 +183,11 @@ final class DaxDialog: XCTestCase { XCTAssertEqual(result?.type, .siteIsMajorTracker) } - func testWhenExperimentGroupAndURLIsOwnedByMajorTrackerAndMajorTrackerDialogHasNotBeenSeenThenReturnSpecTypeSiteOwnedByMajorTracker() { + func testWhenURLIsOwnedByMajorTrackerAndMajorTrackerDialogHasNotBeenSeenThenReturnSpecTypeSiteOwnedByMajorTracker() { // GIVEN let settings = InMemoryDaxDialogsSettings() settings.browsingMajorTrackingSiteShown = false - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) let privacyInfo = makePrivacyInfo(url: URLs.ownedByFacebook) // WHEN @@ -381,11 +197,11 @@ final class DaxDialog: XCTestCase { XCTAssertEqual(result?.type, .siteOwnedByMajorTracker) } - func testWhenExperimentGroupAndURLHasTrackersAndMultipleTrackersDialogHasNotBeenSeenThenReturnSpecTypeWithMultipleTrackers() { + func testWhenURLHasTrackersAndMultipleTrackersDialogHasNotBeenSeenThenReturnSpecTypeWithMultipleTrackers() { // GIVEN let settings = InMemoryDaxDialogsSettings() settings.browsingWithTrackersShown = false - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) let privacyInfo = makePrivacyInfo(url: URLs.example) [URLs.google, URLs.amazon].forEach { url in let detectedTracker = detectedTrackerFrom(url, pageUrl: URLs.example.absoluteString) @@ -399,11 +215,11 @@ final class DaxDialog: XCTestCase { XCTAssertEqual(result?.type, .withMultipleTrackers) } - func testWhenExperimentGroupAndURLHasNoTrackersAndIsNotSERPAndNoTrakcersDialogHasNotBeenSeenThenReturnSpecTypeWithoutTrackers() { + func testWhenURLHasNoTrackersAndIsNotSERPAndNoTrakcersDialogHasNotBeenSeenThenReturnSpecTypeWithoutTrackers() { // GIVEN let settings = InMemoryDaxDialogsSettings() settings.browsingWithoutTrackersShown = false - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) // WHEN let result = sut.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.example)) @@ -412,13 +228,11 @@ final class DaxDialog: XCTestCase { XCTAssertEqual(result?.type, .withoutTrackers) } - func testWhenExperimentGroupAndURLIsDuckDuckGoSearchAndHasVisitedWebsiteThenSpecTypeSearchIsReturned() throws { + func testWhenURLIsDuckDuckGoSearchAndHasVisitedWebsiteThenSpecTypeSearchIsReturned() throws { try [DaxDialogs.BrowsingSpec.withoutTrackers, .siteIsMajorTracker, .siteOwnedByMajorTracker, .withOneTracker, .withMultipleTrackers].forEach { spec in // GIVEN - let isExperiment = true - let mockVariantManager = MockVariantManager(isSupportedReturns: isExperiment) let settings = InMemoryDaxDialogsSettings() - let sut = DaxDialogs(settings: settings, entityProviding: entityProvider, variantManager: mockVariantManager) + let sut = DaxDialogs(settings: settings, entityProviding: entityProvider) sut.overrideShownFlagFor(spec, flag: true) // WHEN @@ -429,12 +243,12 @@ final class DaxDialog: XCTestCase { } } - func testWhenExperimentGroup_AndFireButtonSeen_AndFinalDialogNotSeen_AndSearchDone_ThenFinalBrowsingSpecIsReturned() throws { + func testWhenFireButtonSeen_AndFinalDialogNotSeen_AndSearchDone_ThenFinalBrowsingSpecIsReturned() throws { // GIVEN let settings = InMemoryDaxDialogsSettings() settings.browsingAfterSearchShown = true settings.fireMessageExperimentShown = true - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) // WHEN let result = try XCTUnwrap(sut.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.ddg))) @@ -443,12 +257,12 @@ final class DaxDialog: XCTestCase { XCTAssertEqual(result, .final) } - func testWhenExperimentGroup_AndFireButtonSeen_AndFinalDialogNotSeen_AndWebsiteWithoutTracker_ThenFinalBrowsingSpecIsReturned() throws { + func testWhenFireButtonSeen_AndFinalDialogNotSeen_AndWebsiteWithoutTracker_ThenFinalBrowsingSpecIsReturned() throws { // GIVEN let settings = InMemoryDaxDialogsSettings() settings.browsingWithoutTrackersShown = true settings.fireMessageExperimentShown = true - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) // WHEN let result = try XCTUnwrap(sut.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.example))) @@ -457,12 +271,12 @@ final class DaxDialog: XCTestCase { XCTAssertEqual(result, .final) } - func testWhenExperimentGroup_AndFireButtonSeen_AndFinalDialogNotSeen_AndWebsiteWithTracker_ThenFinalBrowsingSpecIsReturned() throws { + func testWhenFireButtonSeen_AndFinalDialogNotSeen_AndWebsiteWithTracker_ThenFinalBrowsingSpecIsReturned() throws { // GIVEN let settings = InMemoryDaxDialogsSettings() settings.browsingWithTrackersShown = true settings.fireMessageExperimentShown = true - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) let privacyInfo = makePrivacyInfo(url: URLs.example) let detectedTracker = detectedTrackerFrom(URLs.google, pageUrl: URLs.example.absoluteString) privacyInfo.trackerInfo.addDetectedTracker(detectedTracker, onPageWithURL: URLs.example) @@ -474,12 +288,12 @@ final class DaxDialog: XCTestCase { XCTAssertEqual(result, .final) } - func testWhenExperimentGroup_AndFireButtonSeen_AndFinalDialogNotSeen_AndWebsiteMajorTracker_ThenFinalBrowsingSpecIsReturned() throws { + func testWhenFireButtonSeen_AndFinalDialogNotSeen_AndWebsiteMajorTracker_ThenFinalBrowsingSpecIsReturned() throws { // GIVEN let settings = InMemoryDaxDialogsSettings() settings.browsingMajorTrackingSiteShown = true settings.fireMessageExperimentShown = true - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) let privacyInfo = makePrivacyInfo(url: URLs.ownedByFacebook) // WHEN @@ -489,13 +303,13 @@ final class DaxDialog: XCTestCase { XCTAssertEqual(result, .final) } - func testWhenExperimentGroup_AndFireButtonSeen_AndFinalDialogSeen_AndSearchDone_ThenBrowsingSpecIsNil() { + func testWhenFireButtonSeen_AndFinalDialogSeen_AndSearchDone_ThenBrowsingSpecIsNil() { // GIVEN let settings = InMemoryDaxDialogsSettings() settings.browsingAfterSearchShown = true settings.fireMessageExperimentShown = true settings.browsingFinalDialogShown = true - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) // WHEN let result = sut.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.ddg)) @@ -504,13 +318,13 @@ final class DaxDialog: XCTestCase { XCTAssertNil(result) } - func testWhenExperimentGroup_AndFireButtonSeen_AndFinalDialogSeen_AndWebsiteWithoutTracker_ThenBrowsingSpecIsNotFinal() { + func testWhenFireButtonSeen_AndFinalDialogSeen_AndWebsiteWithoutTracker_ThenBrowsingSpecIsNotFinal() { // GIVEN let settings = InMemoryDaxDialogsSettings() settings.browsingWithoutTrackersShown = true settings.fireMessageExperimentShown = true settings.browsingFinalDialogShown = true - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) // WHEN let result = sut.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.example)) @@ -519,13 +333,13 @@ final class DaxDialog: XCTestCase { XCTAssertNil(result) } - func testWhenExperimentGroup_AndFireButtonSeen_AndFinalDialogSeen_AndWebsiteWithTracker_ThenBrowsingSpecIsNil() { + func testWhenFireButtonSeen_AndFinalDialogSeen_AndWebsiteWithTracker_ThenBrowsingSpecIsNil() { // GIVEN let settings = InMemoryDaxDialogsSettings() settings.browsingWithTrackersShown = true settings.fireMessageExperimentShown = true settings.browsingFinalDialogShown = true - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) let privacyInfo = makePrivacyInfo(url: URLs.example) let detectedTracker = detectedTrackerFrom(URLs.google, pageUrl: URLs.example.absoluteString) privacyInfo.trackerInfo.addDetectedTracker(detectedTracker, onPageWithURL: URLs.example) @@ -537,13 +351,13 @@ final class DaxDialog: XCTestCase { XCTAssertNil(result) } - func testWhenExperimentGroup_AndFireButtonSeen_AndFinalDialogSeen_AndWebsiteMajorTracker_ThenFinalBrowsingSpecIsReturned() { + func testWhenFireButtonSeen_AndFinalDialogSeen_AndWebsiteMajorTracker_ThenFinalBrowsingSpecIsReturned() { // GIVEN let settings = InMemoryDaxDialogsSettings() settings.browsingMajorTrackingSiteShown = true settings.fireMessageExperimentShown = true settings.browsingFinalDialogShown = true - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) let privacyInfo = makePrivacyInfo(url: URLs.ownedByFacebook) // WHEN @@ -553,7 +367,7 @@ final class DaxDialog: XCTestCase { XCTAssertNil(result) } - func testWhenExperimentGroup_AndFireButtonSeen_AndFinalDialogSeen_AndSearchNotSeen_ThenAfterSearchSpecIsReturned() { + func testWhenFireButtonSeen_AndFinalDialogSeen_AndSearchNotSeen_ThenAfterSearchSpecIsReturned() { // GIVEN let settings = InMemoryDaxDialogsSettings() settings.browsingWithoutTrackersShown = true @@ -561,7 +375,7 @@ final class DaxDialog: XCTestCase { settings.browsingMajorTrackingSiteShown = true settings.fireMessageExperimentShown = true settings.browsingFinalDialogShown = true - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) // WHEN let result = sut.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.ddg)) @@ -570,10 +384,10 @@ final class DaxDialog: XCTestCase { XCTAssertEqual(result, .afterSearch) } - func testWhenExperimentGroup_AndSearchDialogSeen_OnReload_SearchDialogReturned() { + func testWhenSearchDialogSeen_OnReload_SearchDialogReturned() { // GIVEN let settings = InMemoryDaxDialogsSettings() - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) // WHEN let result1 = sut.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.ddg)) @@ -584,10 +398,10 @@ final class DaxDialog: XCTestCase { XCTAssertEqual(result1, result2) } - func testWhenExperimentGroup_AndSearchDialogSeen_OnLoadingAnotherSearch_NilReturned() { + func testWhenSearchDialogSeen_OnLoadingAnotherSearch_NilReturned() { // GIVEN let settings = InMemoryDaxDialogsSettings() - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) // WHEN let result1 = sut.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.ddg)) @@ -598,10 +412,10 @@ final class DaxDialog: XCTestCase { XCTAssertNil(result2) } - func testWhenExperimentGroup_AndMajorTrackerDialogSeen_OnReload_MajorTrackerDialogReturned() { + func testWhenMajorTrackerDialogSeen_OnReload_MajorTrackerDialogReturned() { // GIVEN let settings = InMemoryDaxDialogsSettings() - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) // WHEN let result1 = sut.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.facebook)) @@ -612,10 +426,10 @@ final class DaxDialog: XCTestCase { XCTAssertEqual(result1, result2) } - func testWhenExperimentGroup_AndMajorTrackerDialogSeen_OnLoadingAnotherSearch_NilReturned() { + func testWhenMajorTrackerDialogSeen_OnLoadingAnotherSearch_NilReturned() { // GIVEN let settings = InMemoryDaxDialogsSettings() - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) // WHEN let result1 = sut.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.facebook)) @@ -626,10 +440,10 @@ final class DaxDialog: XCTestCase { XCTAssertNil(result2) } - func testWhenExperimentGroup_AndMajorTrackerOwnerMessageSeen_OnReload_MajorTrackerOwnerDialogReturned() { + func testWhenMajorTrackerOwnerMessageSeen_OnReload_MajorTrackerOwnerDialogReturned() { // GIVEN let settings = InMemoryDaxDialogsSettings() - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) // WHEN let result1 = sut.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.ownedByFacebook)) @@ -640,10 +454,10 @@ final class DaxDialog: XCTestCase { XCTAssertEqual(result1, result2) } - func testWhenExperimentGroup_AndMajorTrackerOwnerMessageSeen_OnLoadingAnotherSearch_NilReturned() { + func testWhenMajorTrackerOwnerMessageSeen_OnLoadingAnotherSearch_NilReturned() { // GIVEN let settings = InMemoryDaxDialogsSettings() - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) // WHEN let result1 = sut.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.ownedByFacebook)) @@ -654,10 +468,10 @@ final class DaxDialog: XCTestCase { XCTAssertNil(result2) } - func testWhenExperimentGroup_AndWithoutTrackersMessageSeen_OnReload_WithoutTrackersDialogReturned() { + func testWhenWithoutTrackersMessageSeen_OnReload_WithoutTrackersDialogReturned() { // GIVEN let settings = InMemoryDaxDialogsSettings() - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) // WHEN let result1 = sut.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.tracker)) @@ -668,10 +482,10 @@ final class DaxDialog: XCTestCase { XCTAssertEqual(result1, result2) } - func testWhenExperimentGroup_AndWithoutTrackersMessageSeen_OnLoadingAnotherSearch_NilReturned() { + func testWhenWithoutTrackersMessageSeen_OnLoadingAnotherSearch_NilReturned() { // GIVEN let settings = InMemoryDaxDialogsSettings() - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) // WHEN let result1 = sut.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.tracker)) @@ -682,12 +496,12 @@ final class DaxDialog: XCTestCase { XCTAssertNil(result2) } - func testWhenExperimentGroup_AndFinalMessageSeen_OnReload_NilReturned() { + func testWhenFinalMessageSeen_OnReload_NilReturned() { // GIVEN let settings = InMemoryDaxDialogsSettings() settings.browsingWithoutTrackersShown = true settings.fireMessageExperimentShown = true - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) // WHEN let result1 = sut.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.example)) @@ -698,10 +512,10 @@ final class DaxDialog: XCTestCase { XCTAssertNil(result2) } - func testWhenExperimentGroup_AndVisitWebsiteSeen_OnReload_VisitWebsiteReturned() { + func testWhenVisitWebsiteSeen_OnReload_VisitWebsiteReturned() { // GIVEN let settings = InMemoryDaxDialogsSettings() - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) sut.setSearchMessageSeen() // WHEN @@ -716,10 +530,10 @@ final class DaxDialog: XCTestCase { XCTAssertEqual(result2, result3) } - func testWhenExperimentGroup_AndVisitWebsiteSeen_OnLoadingAnotherSearch_NilIseturned() { + func testWhenVisitWebsiteSeen_OnLoadingAnotherSearch_NilIseturned() { // GIVEN let settings = InMemoryDaxDialogsSettings() - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) sut.setSearchMessageSeen() // WHEN @@ -734,10 +548,10 @@ final class DaxDialog: XCTestCase { XCTAssertNil(result3) } - func testWhenExperimentGroup_AndFireMessageSeen_OnReload_FireMessageReturned() { + func testWhenFireMessageSeen_OnReload_FireMessageReturned() { // GIVEN let settings = InMemoryDaxDialogsSettings() - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) sut.setSearchMessageSeen() // WHEN @@ -752,10 +566,10 @@ final class DaxDialog: XCTestCase { XCTAssertEqual(result2, result3) } - func testWhenExperimentGroup_AndSearchNotSeen_AndFireMessageSeen_OnLoadingAnotherSearch_ExpectedDialogIseturned() { + func testWhenSearchNotSeen_AndFireMessageSeen_OnLoadingAnotherSearch_ExpectedDialogIseturned() { // GIVEN let settings = InMemoryDaxDialogsSettings() - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) sut.setSearchMessageSeen() // WHEN @@ -770,10 +584,10 @@ final class DaxDialog: XCTestCase { XCTAssertEqual(result3?.type, .afterSearch) } - func testWhenExperimentGroup_AndSearchSeen_AndFireMessageSeen_OnLoadingAnotherSearch_ExpectedDialogIseturned() { + func testWhenSearchSeen_AndFireMessageSeen_OnLoadingAnotherSearch_ExpectedDialogIseturned() { // GIVEN let settings = InMemoryDaxDialogsSettings() - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) sut.setSearchMessageSeen() // WHEN @@ -791,12 +605,12 @@ final class DaxDialog: XCTestCase { XCTAssertEqual(result4?.type, .final) } - func testWhenExperimentGroup_AndBrowserWithTrackersShown_AndPrivacyAnimationNotShown_ThenShowPrivacyAnimationPulse() { + func testWhenBrowserWithTrackersShown_AndPrivacyAnimationNotShown_ThenShowPrivacyAnimationPulse() { // GIVEN let settings = InMemoryDaxDialogsSettings() settings.browsingWithTrackersShown = true settings.privacyButtonPulseShown = false - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) // WHEN let result = sut.shouldShowPrivacyButtonPulse @@ -805,12 +619,12 @@ final class DaxDialog: XCTestCase { XCTAssertTrue(result) } - func testWhenExperimentGroup_AndBrowserWithTrackersShown_AndPrivacyAnimationShown_ThenDoNotShowPrivacyAnimationPulse() { + func testWhenBrowserWithTrackersShown_AndPrivacyAnimationShown_ThenDoNotShowPrivacyAnimationPulse() { // GIVEN let settings = InMemoryDaxDialogsSettings() settings.browsingWithTrackersShown = true settings.privacyButtonPulseShown = true - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) // WHEN let result = sut.shouldShowPrivacyButtonPulse @@ -819,12 +633,12 @@ final class DaxDialog: XCTestCase { XCTAssertFalse(result) } - func testWhenExperimentGroup_AndBrowserWithTrackersShown_AndFireButtonPulseActive_ThenDoNotShowPrivacyAnimationPulse() { + func testWhenBrowserWithTrackersShown_AndFireButtonPulseActive_ThenDoNotShowPrivacyAnimationPulse() { // GIVEN let settings = InMemoryDaxDialogsSettings() settings.browsingWithTrackersShown = true settings.privacyButtonPulseShown = false - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) sut.fireButtonPulseStarted() // WHEN @@ -834,10 +648,10 @@ final class DaxDialog: XCTestCase { XCTAssertFalse(result) } - func testWhenExperimentGroup_AndCallSetPrivacyButtonPulseSeen_ThenSetPrivacyButtonPulseShownFlagToTrue() { + func testWhenCallSetPrivacyButtonPulseSeen_ThenSetPrivacyButtonPulseShownFlagToTrue() { // GIVEN let settings = InMemoryDaxDialogsSettings() - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) XCTAssertFalse(settings.privacyButtonPulseShown) // WHEN @@ -847,10 +661,10 @@ final class DaxDialog: XCTestCase { XCTAssertTrue(settings.privacyButtonPulseShown) } - func testWhenExperimentGroup_AndSetFireEducationMessageSeenIsCalled_ThenSetPrivacyButtonPulseShownToTrue() { + func testWhenSetFireEducationMessageSeenIsCalled_ThenSetPrivacyButtonPulseShownToTrue() { // GIVEN let settings = InMemoryDaxDialogsSettings() - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) XCTAssertFalse(settings.privacyButtonPulseShown) // WHEN @@ -860,13 +674,13 @@ final class DaxDialog: XCTestCase { XCTAssertTrue(settings.privacyButtonPulseShown) } - func testWhenExperimentGroup_AndFireButtonAnimationPulseNotShown__AndShouldShowFireButtonPulseIsCalled_ThenReturnTrue() { + func testWhenFireButtonAnimationPulseNotShown__AndShouldShowFireButtonPulseIsCalled_ThenReturnTrue() { // GIVEN let settings = InMemoryDaxDialogsSettings() settings.privacyButtonPulseShown = true settings.browsingWithTrackersShown = true settings.fireButtonPulseDateShown = nil - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) // WHEN let result = sut.shouldShowFireButtonPulse @@ -875,13 +689,13 @@ final class DaxDialog: XCTestCase { XCTAssertTrue(result) } - func testWhenExperimentGroup_AndFireButtonAnimationPulseShown_AndShouldShowFireButtonPulseIsCalled_ThenReturnFalse() { + func testWhenFireButtonAnimationPulseShown_AndShouldShowFireButtonPulseIsCalled_ThenReturnFalse() { // GIVEN let settings = InMemoryDaxDialogsSettings() settings.privacyButtonPulseShown = true settings.browsingWithTrackersShown = true settings.fireButtonPulseDateShown = Date() - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) // WHEN let result = sut.shouldShowFireButtonPulse @@ -890,12 +704,12 @@ final class DaxDialog: XCTestCase { XCTAssertFalse(result) } - func testWhenExperimentGroup_AndFireEducationMessageSeen_AndFinalMessageNotSeen_ThenShowFinalMessage() { + func testWhenFireEducationMessageSeen_AndFinalMessageNotSeen_ThenShowFinalMessage() { // GIVEN let settings = InMemoryDaxDialogsSettings() settings.fireMessageExperimentShown = true settings.browsingFinalDialogShown = false - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) // WHEN let result = sut.nextHomeScreenMessageNew() @@ -904,12 +718,12 @@ final class DaxDialog: XCTestCase { XCTAssertEqual(result, .final) } - func testWhenExperimentGroup_AndNextHomeScreenMessageNewIsCalled_ThenLastVisitedOnboardingWebsiteAndLastShownDaxDialogAreSetToNil() { + func testWhenNextHomeScreenMessageNewIsCalled_ThenLastVisitedOnboardingWebsiteAndLastShownDaxDialogAreSetToNil() { // GIVEN let settings = InMemoryDaxDialogsSettings() settings.lastShownContextualOnboardingDialogType = DaxDialogs.BrowsingSpec.fire.type.rawValue settings.lastVisitedOnboardingWebsiteURLPath = "https://www.example.com" - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) XCTAssertNotNil(settings.lastShownContextualOnboardingDialogType) XCTAssertNotNil(settings.lastVisitedOnboardingWebsiteURLPath) @@ -921,54 +735,24 @@ final class DaxDialog: XCTestCase { XCTAssertNil(settings.lastVisitedOnboardingWebsiteURLPath) } - func testWhenExperimentGroup_AndCanEnableAddFavoritesFlowIsCalled_ThenReturnFalse() { + func testWhenEnableAddFavoritesFlowIsCalled_ThenIsAddFavoriteFlowIsTrue() { // GIVEN - let sut = makeExperimentSUT(settings: InMemoryDaxDialogsSettings()) - - // WHEN - let result = sut.canEnableAddFavoriteFlow() - - // THEN - XCTAssertFalse(result) - } - - func testWhenControlGroup_AndCanEnableAddFavoritesFlowIsCalled_ThenReturnTrue() { - // WHEN - let result = onboarding.canEnableAddFavoriteFlow() - - // THEN - XCTAssertTrue(result) - } - - func testWhenControlGroup_AndEnableAddFavoritesFlowIsCalled_ThenIsAddFavoriteFlowIsTrue() { - // GIVEN - XCTAssertFalse(onboarding.isAddFavoriteFlow) - - // WHEN - onboarding.enableAddFavoriteFlow() - - // THEN - XCTAssertTrue(onboarding.isAddFavoriteFlow) - } - - func testWhenExperimentGroup_AndEnableAddFavoritesFlowIsCalled_ThenIsAddFavoriteFlowIsFalse() { - // GIVEN - let sut = makeExperimentSUT(settings: InMemoryDaxDialogsSettings()) + let sut = makeSUT(settings: InMemoryDaxDialogsSettings()) XCTAssertFalse(sut.isAddFavoriteFlow) // WHEN sut.enableAddFavoriteFlow() // THEN - XCTAssertFalse(sut.isAddFavoriteFlow) + XCTAssertTrue(sut.isAddFavoriteFlow) } - func testWhenExperimentGroup_AndBlockedTrackersDialogSeen_AndMajorTrackerNotSeen_ThenReturnNilSpec() { + func testWhenBlockedTrackersDialogSeen_AndMajorTrackerNotSeen_ThenReturnNilSpec() { // GIVEN let settings = InMemoryDaxDialogsSettings() settings.browsingWithTrackersShown = true settings.browsingMajorTrackingSiteShown = false - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) // WHEN let result = sut.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.facebook)) @@ -977,12 +761,12 @@ final class DaxDialog: XCTestCase { XCTAssertNil(result) } - func testWhenExperimentGroup_AndBlockedTrackersDialogNotSeen_AndMajorTrackerNotSeen_ThenReturnMajorNetworkSpec() { + func testWhenBlockedTrackersDialogNotSeen_AndMajorTrackerNotSeen_ThenReturnMajorNetworkSpec() { // GIVEN let settings = InMemoryDaxDialogsSettings() settings.browsingWithTrackersShown = false settings.browsingMajorTrackingSiteShown = false - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) // WHEN let result = sut.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.facebook)) @@ -991,12 +775,12 @@ final class DaxDialog: XCTestCase { XCTAssertEqual(result?.type, .siteIsMajorTracker) } - func testWhenExperimentGroup_AndBlockedTrackersDialogSeen_AndOwnedByMajorTrackerNotSeen_ThenReturnNilSpec() { + func testWhenBlockedTrackersDialogSeen_AndOwnedByMajorTrackerNotSeen_ThenReturnNilSpec() { // GIVEN let settings = InMemoryDaxDialogsSettings() settings.browsingWithTrackersShown = true settings.browsingMajorTrackingSiteShown = false - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) // WHEN let result = sut.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.ownedByFacebook)) @@ -1005,12 +789,12 @@ final class DaxDialog: XCTestCase { XCTAssertNil(result) } - func testWhenExperimentGroup_AndBlockedTrackersDialogNotSeen_AndOwnedByMajorTrackerNotSeen_ThenReturnOwnedByMajorNetworkSpec() { + func testWhenBlockedTrackersDialogNotSeen_AndOwnedByMajorTrackerNotSeen_ThenReturnOwnedByMajorNetworkSpec() { // GIVEN let settings = InMemoryDaxDialogsSettings() settings.browsingWithTrackersShown = false settings.browsingMajorTrackingSiteShown = false - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) // WHEN let result = sut.nextBrowsingMessageIfShouldShow(for: makePrivacyInfo(url: URLs.ownedByFacebook)) @@ -1019,12 +803,12 @@ final class DaxDialog: XCTestCase { XCTAssertEqual(result?.type, .siteOwnedByMajorTracker) } - func testWhenExperimentGroup_AndDismissIsCalled_ThenLastVisitedOnboardingWebsiteAndLastShownDaxDialogAreSetToNil() { + func testWhenDismissIsCalled_ThenLastVisitedOnboardingWebsiteAndLastShownDaxDialogAreSetToNil() { // GIVEN let settings = InMemoryDaxDialogsSettings() settings.lastShownContextualOnboardingDialogType = DaxDialogs.BrowsingSpec.fire.type.rawValue settings.lastVisitedOnboardingWebsiteURLPath = "https://www.example.com" - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) XCTAssertNotNil(settings.lastShownContextualOnboardingDialogType) XCTAssertNotNil(settings.lastVisitedOnboardingWebsiteURLPath) @@ -1036,12 +820,12 @@ final class DaxDialog: XCTestCase { XCTAssertNil(settings.lastVisitedOnboardingWebsiteURLPath) } - func testWhenExperimentGroup_AndSetDaxDialogDismiss_ThenLastVisitedOnboardingWebsiteAndLastShownDaxDialogAreSetToNil() { + func testWhenSetDaxDialogDismiss_ThenLastVisitedOnboardingWebsiteAndLastShownDaxDialogAreSetToNil() { // GIVEN let settings = InMemoryDaxDialogsSettings() settings.lastShownContextualOnboardingDialogType = DaxDialogs.BrowsingSpec.fire.type.rawValue settings.lastVisitedOnboardingWebsiteURLPath = "https://www.example.com" - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) XCTAssertNotNil(settings.lastShownContextualOnboardingDialogType) XCTAssertNotNil(settings.lastVisitedOnboardingWebsiteURLPath) @@ -1053,12 +837,12 @@ final class DaxDialog: XCTestCase { XCTAssertNil(settings.lastVisitedOnboardingWebsiteURLPath) } - func testWhenExperimentGroup_AndClearedBrowserDataIsCalled_ThenLastVisitedOnboardingWebsiteAndLastShownDaxDialogAreSetToNil() throws { + func testWhenClearedBrowserDataIsCalled_ThenLastVisitedOnboardingWebsiteAndLastShownDaxDialogAreSetToNil() throws { // GIVEN let settings = InMemoryDaxDialogsSettings() settings.lastShownContextualOnboardingDialogType = DaxDialogs.BrowsingSpec.fire.type.rawValue settings.lastVisitedOnboardingWebsiteURLPath = "https://www.example.com" - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) XCTAssertNotNil(settings.lastShownContextualOnboardingDialogType) XCTAssertNotNil(settings.lastVisitedOnboardingWebsiteURLPath) @@ -1070,14 +854,14 @@ final class DaxDialog: XCTestCase { XCTAssertNil(settings.lastVisitedOnboardingWebsiteURLPath) } - func testWhenExperimentGroup_AndIsEnabledIsFalse_AndReloadWebsite_ThenReturnNilBrowsingSpec() throws { + func testWhenIsEnabledIsFalse_AndReloadWebsite_ThenReturnNilBrowsingSpec() throws { // GIVEN let lastVisitedWebsitePath = "https://www.example.com" let lastVisitedWebsiteURL = try XCTUnwrap(URL(string: lastVisitedWebsitePath)) let settings = InMemoryDaxDialogsSettings() settings.lastShownContextualOnboardingDialogType = DaxDialogs.BrowsingSpec.fire.type.rawValue settings.lastVisitedOnboardingWebsiteURLPath = lastVisitedWebsitePath - let sut = makeExperimentSUT(settings: settings) + let sut = makeSUT(settings: settings) sut.dismiss() // WHEN @@ -1089,9 +873,7 @@ final class DaxDialog: XCTestCase { func testWhenIsEnabledIsCalled_AndShouldShowDaxDialogsIsTrue_ThenReturnTrue() { // GIVEN - var mockVariantManager = MockVariantManager() - mockVariantManager.currentVariant = MockVariant(features: [.newOnboardingIntro, .contextualDaxDialogs]) - let sut = DaxDialogs(settings: settings, entityProviding: entityProvider, variantManager: mockVariantManager) + let sut = DaxDialogs(settings: settings, entityProviding: entityProvider) // WHEN let result = sut.isEnabled @@ -1107,7 +889,7 @@ final class DaxDialog: XCTestCase { let onboardingManagerMock = OnboardingManagerMock() onboardingManagerMock.addToDockEnabledState = .contextual settings.fireMessageExperimentShown = true - let sut = makeExperimentSUT(settings: settings, onboardingManager: onboardingManagerMock) + let sut = makeSUT(settings: settings, onboardingManager: onboardingManagerMock) _ = sut.nextHomeScreenMessageNew() // WHEN @@ -1121,7 +903,7 @@ final class DaxDialog: XCTestCase { // GIVEN let onboardingManagerMock = OnboardingManagerMock() onboardingManagerMock.addToDockEnabledState = .contextual - let sut = makeExperimentSUT(settings: settings, onboardingManager: onboardingManagerMock) + let sut = makeSUT(settings: settings, onboardingManager: onboardingManagerMock) _ = sut.nextHomeScreenMessageNew() // WHEN @@ -1136,7 +918,7 @@ final class DaxDialog: XCTestCase { let onboardingManagerMock = OnboardingManagerMock() onboardingManagerMock.addToDockEnabledState = .disabled settings.fireMessageExperimentShown = true - let sut = makeExperimentSUT(settings: settings, onboardingManager: onboardingManagerMock) + let sut = makeSUT(settings: settings, onboardingManager: onboardingManagerMock) _ = sut.nextHomeScreenMessageNew() // WHEN @@ -1169,11 +951,7 @@ final class DaxDialog: XCTestCase { protectionStatus: protectionStatus) } - private func makeExperimentSUT(settings: DaxDialogsSettings, onboardingManager: OnboardingAddToDockManaging = OnboardingManagerMock()) -> DaxDialogs { - var mockVariantManager = MockVariantManager() - mockVariantManager.isSupportedBlock = { feature in - feature == .contextualDaxDialogs - } - return DaxDialogs(settings: settings, entityProviding: entityProvider, variantManager: mockVariantManager, onboardingManager: onboardingManager) + private func makeSUT(settings: DaxDialogsSettings, onboardingManager: OnboardingAddToDockManaging = OnboardingManagerMock()) -> DaxDialogs { + DaxDialogs(settings: settings, entityProviding: entityProvider, variantManager: MockVariantManager(), onboardingManager: onboardingManager) } } diff --git a/DuckDuckGoTests/DaxDialogsNewTabTests.swift b/DuckDuckGoTests/DaxDialogsNewTabTests.swift index 23f3617765..d99af3291e 100644 --- a/DuckDuckGoTests/DaxDialogsNewTabTests.swift +++ b/DuckDuckGoTests/DaxDialogsNewTabTests.swift @@ -38,7 +38,6 @@ final class DaxDialogsNewTabTests: XCTestCase { } func testIfIsAddFavoriteFlow_OnNextHomeScreenMessageNew_ReturnsAddFavorite() { - XCTExpectFailure("Add Favrite flow, is currenty disabled for new onboarding. Remove failure expectation once we support it.") // GIVEN daxDialogs.enableAddFavoriteFlow() diff --git a/DuckDuckGoTests/DefaultVariantManagerOnboardingTests.swift b/DuckDuckGoTests/DefaultVariantManagerOnboardingTests.swift deleted file mode 100644 index f9641c8fd1..0000000000 --- a/DuckDuckGoTests/DefaultVariantManagerOnboardingTests.swift +++ /dev/null @@ -1,117 +0,0 @@ -// -// DefaultVariantManagerOnboardingTests.swift -// DuckDuckGo -// -// Copyright © 2024 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import XCTest -import BrowserServicesKit -@testable import Core -@testable import DuckDuckGo - -final class DefaultVariantManagerOnboardingTests: XCTestCase { - - // MARK: - Is Onboarding Highlights - - func testWhenIsOnboardingHighlights_AndFeaturesContainOnboardingHighlights_ThenReturnTrue() { - // GIVEN - let sut = makeVariantManager(features: [.newOnboardingIntroHighlights]) - - // WHEN - let result = sut.isOnboardingHighlightsExperiment - - // THEN - XCTAssertTrue(result) - } - - func testWhenIsOnboardingHighlights_AndFeaturesDoNotContainOnboardingHighlights_ThenReturnFalse() { - // GIVEN - let sut = makeVariantManager(features: [.newOnboardingIntro, .contextualDaxDialogs]) - - // WHEN - let result = sut.isOnboardingHighlightsExperiment - - // THEN - XCTAssertFalse(result) - } - - func testWhenIsOnboardingHighlights_AndFeaturesIsEmpty_ThenReturnFalse() { - // GIVEN - let sut = makeVariantManager(features: []) - - // WHEN - let result = sut.isOnboardingHighlightsExperiment - - // THEN - XCTAssertFalse(result) - } - - // MARK: - Is Contextual Dax Dialogs Enabled - - func testWhenIsContextualDaxDialogsEnabled_AndFeaturesContainContextualDaxDialogs_ThenReturnTrue() { - // GIVEN - let sut = makeVariantManager(features: [.contextualDaxDialogs]) - - // WHEN - let result = sut.isContextualDaxDialogsEnabled - - // THEN - XCTAssertTrue(result) - } - - func testWhenIsContextualDaxDialogsEnabled_AndFeaturesDoNotContainContextualDaxDialogs_ThenReturnFalse() { - // GIVEN - let sut = makeVariantManager(features: [.newOnboardingIntro, .newOnboardingIntroHighlights]) - - // WHEN - let result = sut.isContextualDaxDialogsEnabled - - // THEN - XCTAssertFalse(result) - } - - func testWhenIsContextualDaxDialogsEnabled_AndFeaturesIsEmpty_ThenReturnFalse() { - // GIVEN - let sut = makeVariantManager(features: []) - - // WHEN - let result = sut.isContextualDaxDialogsEnabled - - // THEN - XCTAssertFalse(result) - } - -} - -// MARK: Helpers - -private extension DefaultVariantManagerOnboardingTests { - - func makeVariantManager(features: [FeatureName]) -> DefaultVariantManager { - let mockStatisticStore = MockStatisticsStore() - mockStatisticStore.variant = #function - let variantManager = DefaultVariantManager( - variants: [VariantIOS(name: #function, weight: 1, isIncluded: VariantIOS.When.always, features: features)], - storage: mockStatisticStore, - rng: MockVariantRNG(returnValue: 500), - returningUserMeasurement: MockReturningUserMeasurement(), - variantNameOverride: MockVariantNameOverride() - ) - variantManager.assignVariantIfNeeded { _ in } - return variantManager - } - -} diff --git a/DuckDuckGoTests/DuckPlayerOverlayUsagePixelsTests.swift b/DuckDuckGoTests/DuckPlayerOverlayUsagePixelsTests.swift deleted file mode 100644 index 7f966f334f..0000000000 --- a/DuckDuckGoTests/DuckPlayerOverlayUsagePixelsTests.swift +++ /dev/null @@ -1,130 +0,0 @@ -// -// DuckPlayerOverlayUsagePixelsTests.swift -// DuckDuckGo -// -// Copyright © 2024 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import XCTest -import Core -@testable import DuckDuckGo - -class DuckPlayerOverlayUsagePixelsTests: XCTestCase { - - var duckPlayerOverlayPixels: DuckPlayerOverlayUsagePixels! - - override func setUp() { - super.setUp() - PixelFiringMock.tearDown() - duckPlayerOverlayPixels = DuckPlayerOverlayUsagePixels(pixelFiring: PixelFiringMock.self, timeoutInterval: 3.0) - } - - override func tearDown() { - PixelFiringMock.tearDown() - duckPlayerOverlayPixels = nil - super.tearDown() - } - - // Test: Registering navigation appends URL to history - func testRegisterNavigationAppendsURLToHistory() { - let testURL1 = URL(string: "https://www.youtube.com/watch?v=example1")! - let testURL2 = URL(string: "https://www.youtube.com/playlist?list=PL-gbSnmxoBbmDnoFdZY5OsSuU6kqs_07a")! - let testURL3 = URL(string: "https://www.example.com")! - - // Simulate navigation actions - duckPlayerOverlayPixels.handleNavigationAndFirePixels(url: testURL1, duckPlayerMode: .alwaysAsk) - duckPlayerOverlayPixels.handleNavigationAndFirePixels(url: testURL2, duckPlayerMode: .alwaysAsk) - duckPlayerOverlayPixels.handleNavigationAndFirePixels(url: testURL3, duckPlayerMode: .alwaysAsk) - - // Verify the URLs are appended in order - XCTAssertEqual(duckPlayerOverlayPixels.navigationHistory.count, 3) - XCTAssertEqual(duckPlayerOverlayPixels.navigationHistory[0], testURL1.forComparison()) - XCTAssertEqual(duckPlayerOverlayPixels.navigationHistory[1], testURL2.forComparison()) - XCTAssertEqual(duckPlayerOverlayPixels.navigationHistory[2], testURL3.forComparison()) - } - - // Test: Back navigation triggers duckPlayerYouTubeOverlayNavigationBack pixel - func testBackNavigationTriggersBackPixel() { - let firstURL = URL(string: "https://www.youtube.com/watch?v=example1")! - let secondURL = URL(string: "https://www.youtube.com/watch?v=example2")! - - duckPlayerOverlayPixels.handleNavigationAndFirePixels(url: firstURL, duckPlayerMode: .alwaysAsk) - duckPlayerOverlayPixels.handleNavigationAndFirePixels(url: secondURL, duckPlayerMode: .alwaysAsk) - duckPlayerOverlayPixels.handleNavigationAndFirePixels(url: firstURL, duckPlayerMode: .alwaysAsk) // Simulates back navigation - - XCTAssertEqual(PixelFiringMock.lastPixelName, Pixel.Event.duckPlayerYouTubeOverlayNavigationBack.name) - XCTAssertNotNil(PixelFiringMock.lastPixelInfo, "Pixel should be fired on back navigation.") - } - - // Test: Reload navigation triggers duckPlayerYouTubeOverlayNavigationRefresh pixel - func testReloadNavigationTriggersRefreshPixel() { - let testURL = URL(string: "https://www.youtube.com/watch?v=XTWWSS")! - - duckPlayerOverlayPixels.handleNavigationAndFirePixels(url: testURL, duckPlayerMode: .alwaysAsk) - duckPlayerOverlayPixels.handleNavigationAndFirePixels(url: testURL, duckPlayerMode: .alwaysAsk) // Simulates reload navigation - - XCTAssertEqual(PixelFiringMock.lastPixelName, Pixel.Event.duckPlayerYouTubeOverlayNavigationRefresh.name) - XCTAssertNotNil(PixelFiringMock.lastPixelInfo, "Pixel should be fired on reload navigation.") - } - - // Test: Forward navigation to various YouTube URLs triggers duckPlayerYouTubeNavigationWithinYouTube pixel - func testNavigateWithinYoutubeTriggersWithinYouTubePixel() { - let videoURL = URL(string: "https://www.youtube.com/watch?v=example1")! - let playlistURL = URL(string: "https://www.youtube.com/playlist?list=PL-gbSnmxoBbmDnoFdZY5OsSuU6kqs_07a")! - - duckPlayerOverlayPixels.handleNavigationAndFirePixels(url: videoURL, duckPlayerMode: .alwaysAsk) - duckPlayerOverlayPixels.handleNavigationAndFirePixels(url: playlistURL, duckPlayerMode: .alwaysAsk) - - XCTAssertEqual(PixelFiringMock.lastPixelName, Pixel.Event.duckPlayerYouTubeNavigationWithinYouTube.name) - XCTAssertNotNil(PixelFiringMock.lastPixelInfo, "Pixel should be fired when navigating within YouTube to a non-video URL.") - } - - // Test: Navigating outside YouTube triggers duckPlayerYouTubeOverlayNavigationOutsideYoutube pixel - func testNavigateOutsideYoutubeTriggersOutsideYouTubePixel() { - let youtubeURL = URL(string: "https://www.youtube.com/watch?v=example1")! - let outsideURL = URL(string: "https://www.example.com")! - - duckPlayerOverlayPixels.handleNavigationAndFirePixels(url: youtubeURL, duckPlayerMode: .alwaysAsk) - duckPlayerOverlayPixels.handleNavigationAndFirePixels(url: outsideURL, duckPlayerMode: .alwaysAsk) - - XCTAssertEqual(PixelFiringMock.lastPixelName, Pixel.Event.duckPlayerYouTubeOverlayNavigationOutsideYoutube.name) - XCTAssertNotNil(PixelFiringMock.lastPixelInfo, "Pixel should be fired when navigating outside YouTube.") - } - - // Negative Test: Back navigation does not trigger within YouTube or outside YouTube pixel - func testBackNavigationDoesNotTriggerWithinOrOutsideYouTubePixel() { - let firstURL = URL(string: "https://www.youtube.com/watch?v=example1")! - let secondURL = URL(string: "https://www.youtube.com/watch?v=example2")! - let backURL = URL(string: "https://www.youtube.com/watch?v=example1")! - - duckPlayerOverlayPixels.handleNavigationAndFirePixels(url: firstURL, duckPlayerMode: .alwaysAsk) - duckPlayerOverlayPixels.handleNavigationAndFirePixels(url: secondURL, duckPlayerMode: .alwaysAsk) - duckPlayerOverlayPixels.handleNavigationAndFirePixels(url: backURL, duckPlayerMode: .alwaysAsk) // Simulates back navigation - - XCTAssertNotEqual(PixelFiringMock.lastPixelName, Pixel.Event.duckPlayerYouTubeNavigationWithinYouTube.name, "Within YouTube pixel should not fire on back navigation.") - XCTAssertNotEqual(PixelFiringMock.lastPixelName, Pixel.Event.duckPlayerYouTubeOverlayNavigationOutsideYoutube.name, "Outside YouTube pixel should not fire on back navigation.") - } - - // Negative Test: Reload navigation does not trigger within YouTube or outside YouTube pixel - func testReloadNavigationDoesNotTriggerWithinOrOutsideYouTubePixel() { - let testURL = URL(string: "https://www.youtube.com/watch?v=example")! - - duckPlayerOverlayPixels.handleNavigationAndFirePixels(url: testURL, duckPlayerMode: .alwaysAsk) - duckPlayerOverlayPixels.handleNavigationAndFirePixels(url: testURL, duckPlayerMode: .alwaysAsk) // Simulates reload navigation - - XCTAssertNotEqual(PixelFiringMock.lastPixelName, Pixel.Event.duckPlayerYouTubeNavigationWithinYouTube.name, "Within YouTube pixel should not fire on reload.") - XCTAssertNotEqual(PixelFiringMock.lastPixelName, Pixel.Event.duckPlayerYouTubeOverlayNavigationOutsideYoutube.name, "Outside YouTube pixel should not fire on reload.") - } -} diff --git a/DuckDuckGoTests/HomeRowReminderTests.swift b/DuckDuckGoTests/HomeRowReminderTests.swift index c0e44358f5..33c310d1e7 100644 --- a/DuckDuckGoTests/HomeRowReminderTests.swift +++ b/DuckDuckGoTests/HomeRowReminderTests.swift @@ -60,6 +60,40 @@ class HomeRowReminderTests: XCTestCase { XCTAssertFalse(feature.showNow()) } + // MARK: - Add To Dock - Onboarding + + func testWhenAddToDockHasShownInOboardingIntroThenDoNotShowAddToDockReminder() { + // GIVEN + var variantManager = MockVariantManager() + variantManager.isSupportedBlock = { feature in + feature == .addToDockIntro + } + let sut = HomeRowReminder(storage: storage, variantManager: variantManager) + + // WHEN + let result = sut.showNow() + + // THEN + XCTAssertFalse(result) + } + + func testWhenAddToDockHasShownInContextualOboardingThenDoNotShowAddToDockReminder() { + // GIVEN + var variantManager = MockVariantManager() + variantManager.isSupportedBlock = { feature in + feature == .addToDockContextual + } + let sut = HomeRowReminder(storage: storage, variantManager: variantManager) + + // WHEN + let result = sut.showNow() + + // THEN + XCTAssertFalse(result) + } + + // MARK: - Helper functions + private func setReminderTimeElapsed() { let threeAndABitDaysAgo = -(60 * 60 * 24 * HomeRowReminder.Constants.reminderTimeInDays * 1.1) storage.firstAccessDate = Date(timeIntervalSinceNow: threeAndABitDaysAgo) @@ -67,6 +101,8 @@ class HomeRowReminderTests: XCTestCase { } +// MARK: - Mocks + class MockHomeRowReminderStorage: HomeRowReminderStorage { var firstAccessDate: Date? diff --git a/DuckDuckGoTests/LargeOmniBarStateTests.swift b/DuckDuckGoTests/LargeOmniBarStateTests.swift index caeedfc75d..c30ce2bf06 100644 --- a/DuckDuckGoTests/LargeOmniBarStateTests.swift +++ b/DuckDuckGoTests/LargeOmniBarStateTests.swift @@ -45,7 +45,7 @@ class LargeOmniBarStateTests: XCTestCase { XCTAssertTrue(testee.showBackButton) XCTAssertTrue(testee.showForwardButton) XCTAssertTrue(testee.showBookmarksButton) - XCTAssertFalse(testee.showShareButton) + XCTAssertFalse(testee.showAccessoryButton) } func testWhenInHomeEmptyEditingStateWithVoiceSearchThenCorrectButtonsAreShown() { @@ -66,7 +66,7 @@ class LargeOmniBarStateTests: XCTestCase { XCTAssertTrue(testee.showBackButton) XCTAssertTrue(testee.showForwardButton) XCTAssertTrue(testee.showBookmarksButton) - XCTAssertFalse(testee.showShareButton) + XCTAssertFalse(testee.showAccessoryButton) } func testWhenEnteringHomeEmptyEditingStateThenTextIsCleared() { @@ -122,7 +122,7 @@ class LargeOmniBarStateTests: XCTestCase { XCTAssertTrue(testee.showBackButton) XCTAssertTrue(testee.showForwardButton) XCTAssertTrue(testee.showBookmarksButton) - XCTAssertFalse(testee.showShareButton) + XCTAssertFalse(testee.showAccessoryButton) } func testWhenInHomeTextEditingStateWithVoiceSearchThenCorrectButtonsAreShown() { @@ -143,7 +143,7 @@ class LargeOmniBarStateTests: XCTestCase { XCTAssertTrue(testee.showBackButton) XCTAssertTrue(testee.showForwardButton) XCTAssertTrue(testee.showBookmarksButton) - XCTAssertFalse(testee.showShareButton) + XCTAssertFalse(testee.showAccessoryButton) } func testWhenEnteringHomeTextEditingStateThenTextIsNotCleared() { let testee = LargeOmniBarState.HomeTextEditingState(voiceSearchHelper: enabledVoiceSearchHelper, isLoading: false) @@ -197,7 +197,7 @@ class LargeOmniBarStateTests: XCTestCase { XCTAssertTrue(testee.showBackButton) XCTAssertTrue(testee.showForwardButton) XCTAssertTrue(testee.showBookmarksButton) - XCTAssertFalse(testee.showShareButton) + XCTAssertFalse(testee.showAccessoryButton) } func testWhenInHomeNonEditingStateWithoutVoiceSearchThenCorrectButtonsAreShown() { @@ -217,7 +217,7 @@ class LargeOmniBarStateTests: XCTestCase { XCTAssertTrue(testee.showBackButton) XCTAssertTrue(testee.showForwardButton) XCTAssertTrue(testee.showBookmarksButton) - XCTAssertFalse(testee.showShareButton) + XCTAssertFalse(testee.showAccessoryButton) } func testWhenEnteringHomeNonEditingStateThenTextIsCleared() { @@ -272,7 +272,7 @@ class LargeOmniBarStateTests: XCTestCase { XCTAssertTrue(testee.showBackButton) XCTAssertTrue(testee.showForwardButton) XCTAssertTrue(testee.showBookmarksButton) - XCTAssertTrue(testee.showShareButton) + XCTAssertTrue(testee.showAccessoryButton) } func testWhenInBrowserEmptyEditingStateWithVoiceSearchThenCorrectButtonsAreShown() { @@ -292,7 +292,7 @@ class LargeOmniBarStateTests: XCTestCase { XCTAssertTrue(testee.showBackButton) XCTAssertTrue(testee.showForwardButton) XCTAssertTrue(testee.showBookmarksButton) - XCTAssertTrue(testee.showShareButton) + XCTAssertTrue(testee.showAccessoryButton) } func testWhenEnteringBrowserEmptyEditingStateThenTextIsCleared() { @@ -347,7 +347,7 @@ class LargeOmniBarStateTests: XCTestCase { XCTAssertTrue(testee.showBackButton) XCTAssertTrue(testee.showForwardButton) XCTAssertTrue(testee.showBookmarksButton) - XCTAssertTrue(testee.showShareButton) + XCTAssertTrue(testee.showAccessoryButton) } func testWhenInBrowsingTextEditingStateWithoutVoiceSearchThenCorrectButtonsAreShown() { @@ -367,7 +367,7 @@ class LargeOmniBarStateTests: XCTestCase { XCTAssertTrue(testee.showBackButton) XCTAssertTrue(testee.showForwardButton) XCTAssertTrue(testee.showBookmarksButton) - XCTAssertTrue(testee.showShareButton) + XCTAssertTrue(testee.showAccessoryButton) } func testWhenEnteringBrowsingTextEditingStateThenTextIsMaintained() { @@ -421,7 +421,7 @@ class LargeOmniBarStateTests: XCTestCase { XCTAssertTrue(testee.showBackButton) XCTAssertTrue(testee.showForwardButton) XCTAssertTrue(testee.showBookmarksButton) - XCTAssertTrue(testee.showShareButton) + XCTAssertTrue(testee.showAccessoryButton) } func testWhenEnteringBrowsingNonEditingStateThenTextIsMaintained() { diff --git a/DuckDuckGoTests/MockTabDelegate.swift b/DuckDuckGoTests/MockTabDelegate.swift index 7878b46cbd..47522ae542 100644 --- a/DuckDuckGoTests/MockTabDelegate.swift +++ b/DuckDuckGoTests/MockTabDelegate.swift @@ -68,6 +68,8 @@ final class MockTabDelegate: TabDelegate { func tabDidRequestAutofillLogins(tab: DuckDuckGo.TabViewController) {} + func tabDidRequestAIChat(tab: TabViewController) {} + func tabDidRequestSettings(tab: DuckDuckGo.TabViewController) {} func tab(_ tab: DuckDuckGo.TabViewController, didRequestSettingsToLogins account: BrowserServicesKit.SecureVaultModels.WebsiteAccount) {} diff --git a/DuckDuckGoTests/NewTabPageControllerDaxDialogTests.swift b/DuckDuckGoTests/NewTabPageControllerDaxDialogTests.swift index 1c6322925e..1928a28e03 100644 --- a/DuckDuckGoTests/NewTabPageControllerDaxDialogTests.swift +++ b/DuckDuckGoTests/NewTabPageControllerDaxDialogTests.swift @@ -82,9 +82,8 @@ final class NewTabPageControllerDaxDialogTests: XCTestCase { hvc = nil } - func testWhenContextualDaxDialogsSupported_OnDidAppear_CorrectTypePassedToDialogFactory() throws { + func testWhenViewDidAppear_CorrectTypePassedToDialogFactory() throws { // GIVEN - variantManager.supportedFeatures = [.contextualDaxDialogs] let expectedSpec = randomDialogType() specProvider.specToReturn = expectedSpec @@ -92,30 +91,14 @@ final class NewTabPageControllerDaxDialogTests: XCTestCase { hvc.viewDidAppear(false) // THEN - XCTAssertEqual(self.variantManager.capturedFeatureName?.rawValue, FeatureName.contextualDaxDialogs.rawValue) XCTAssertFalse(self.specProvider.nextHomeScreenMessageCalled) XCTAssertTrue(self.specProvider.nextHomeScreenMessageNewCalled) XCTAssertEqual(self.dialogFactory.homeDialog, expectedSpec) XCTAssertNotNil(self.dialogFactory.onDismiss) } - func testWhenOldOnboarding_OnDidAppear_NothingPassedDialogFactory() throws { + func testWhenOnboardingComplete_CorrectTypePassedToDialogFactory() throws { // GIVEN - variantManager.supportedFeatures = [] - - // WHEN - hvc.viewDidAppear(false) - - // THEN - XCTAssertTrue(specProvider.nextHomeScreenMessageCalled) - XCTAssertFalse(specProvider.nextHomeScreenMessageNewCalled) - XCTAssertNil(dialogFactory.homeDialog) - XCTAssertNil(dialogFactory.onDismiss) - } - - func testWhenContextualDaxDialogsSupported_OnOnboardingComplete_CorrectTypePassedToDialogFactory() throws { - // GIVEN - variantManager.supportedFeatures = [.contextualDaxDialogs] let expectedSpec = randomDialogType() specProvider.specToReturn = expectedSpec @@ -123,51 +106,18 @@ final class NewTabPageControllerDaxDialogTests: XCTestCase { hvc.onboardingCompleted() // THEN - XCTAssertEqual(self.variantManager.capturedFeatureName?.rawValue, FeatureName.contextualDaxDialogs.rawValue) XCTAssertFalse(self.specProvider.nextHomeScreenMessageCalled) XCTAssertTrue(self.specProvider.nextHomeScreenMessageNewCalled) XCTAssertEqual(self.dialogFactory.homeDialog, expectedSpec) XCTAssertNotNil(self.dialogFactory.onDismiss) } - func testWhenOldOnboarding_OnOnboardingComplete_NothingPassedDialogFactory() throws { - // GIVEN - variantManager.supportedFeatures = [] - - // WHEN - hvc.onboardingCompleted() - - // THEN - XCTAssertTrue(specProvider.nextHomeScreenMessageCalled) - XCTAssertFalse(specProvider.nextHomeScreenMessageNewCalled) - XCTAssertNil(dialogFactory.homeDialog) - XCTAssertNil(dialogFactory.onDismiss) - } - - func testWhenOldOnboarding_OnOpenedAsNewTab_NothingPassedDialogFactory() throws { - // GIVEN - variantManager.supportedFeatures = [] - - // WHEN - hvc.openedAsNewTab(allowingKeyboard: true) - - // THEN - XCTAssertTrue(specProvider.nextHomeScreenMessageCalled) - XCTAssertFalse(specProvider.nextHomeScreenMessageNewCalled) - XCTAssertNil(dialogFactory.homeDialog) - XCTAssertNil(dialogFactory.onDismiss) - } - func testWhenShowNextDaxDialog_AndShouldShowDaxDialogs_ThenReturnTrue() { - // GIVEN - variantManager.supportedFeatures = [] - // WHEN hvc.showNextDaxDialog() // THEN - XCTAssertTrue(specProvider.nextHomeScreenMessageCalled) - XCTAssertFalse(specProvider.nextHomeScreenMessageNewCalled) + XCTAssertTrue(specProvider.nextHomeScreenMessageNewCalled) } private func randomDialogType() -> DaxDialogs.HomeScreenSpec { diff --git a/DuckDuckGoTests/OmniBarEqualityCheckTests.swift b/DuckDuckGoTests/OmniBarEqualityCheckTests.swift index c10e468259..4b6f2dd482 100644 --- a/DuckDuckGoTests/OmniBarEqualityCheckTests.swift +++ b/DuckDuckGoTests/OmniBarEqualityCheckTests.swift @@ -50,7 +50,7 @@ final class OmniBarEqualityCheckTests: XCTestCase { barOmniBarState.showBackButton = !fooOmniBarState.showBackButton barOmniBarState.showForwardButton = !fooOmniBarState.showForwardButton barOmniBarState.showBookmarksButton = !fooOmniBarState.showBookmarksButton - barOmniBarState.showShareButton = !fooOmniBarState.showShareButton + barOmniBarState.showAccessoryButton = !fooOmniBarState.showAccessoryButton barOmniBarState.clearTextOnStart = !fooOmniBarState.clearTextOnStart barOmniBarState.allowsTrackersAnimation = !fooOmniBarState.allowsTrackersAnimation barOmniBarState.showSearchLoupe = !fooOmniBarState.showSearchLoupe @@ -77,7 +77,7 @@ private struct DummyOmniBarState: OmniBarState, OmniBarLoadingBearerStateCreatin var showBackButton = false var showForwardButton = false var showBookmarksButton = false - var showShareButton = false + var showAccessoryButton = false var clearTextOnStart = false var allowsTrackersAnimation = false var showSearchLoupe = false diff --git a/DuckDuckGoTests/OnboardingAddressBarPositionPickerViewModelTests.swift b/DuckDuckGoTests/OnboardingAddressBarPositionPickerViewModelTests.swift index 5708195ae5..f11b62c610 100644 --- a/DuckDuckGoTests/OnboardingAddressBarPositionPickerViewModelTests.swift +++ b/DuckDuckGoTests/OnboardingAddressBarPositionPickerViewModelTests.swift @@ -44,15 +44,15 @@ final class OnboardingAddressBarPositionPickerViewModelTests: XCTestCase { // THEN let firstItem = try XCTUnwrap(items.first) XCTAssertEqual(firstItem.type, .top) - XCTAssertEqual(firstItem.title.string, UserText.HighlightsOnboardingExperiment.AddressBarPosition.topTitle + " " + UserText.HighlightsOnboardingExperiment.AddressBarPosition.defaultOption) - XCTAssertEqual(firstItem.message, UserText.HighlightsOnboardingExperiment.AddressBarPosition.topMessage) + XCTAssertEqual(firstItem.title.string, UserText.Onboarding.AddressBarPosition.topTitle + " " + UserText.Onboarding.AddressBarPosition.defaultOption) + XCTAssertEqual(firstItem.message, UserText.Onboarding.AddressBarPosition.topMessage) XCTAssertEqual(firstItem.icon, .addressBarTop) XCTAssertTrue(firstItem.isSelected) let secondItem = try XCTUnwrap(items.last) XCTAssertEqual(secondItem.type, .bottom) - XCTAssertEqual(secondItem.title.string, UserText.HighlightsOnboardingExperiment.AddressBarPosition.bottomTitle) - XCTAssertEqual(secondItem.message, UserText.HighlightsOnboardingExperiment.AddressBarPosition.bottomMessage) + XCTAssertEqual(secondItem.title.string, UserText.Onboarding.AddressBarPosition.bottomTitle) + XCTAssertEqual(secondItem.message, UserText.Onboarding.AddressBarPosition.bottomMessage) XCTAssertEqual(secondItem.icon, .addressBarBottom) XCTAssertFalse(secondItem.isSelected) } @@ -73,15 +73,15 @@ final class OnboardingAddressBarPositionPickerViewModelTests: XCTestCase { let items = sut.items let firstItem = try XCTUnwrap(items.first) XCTAssertEqual(firstItem.type, .top) - XCTAssertEqual(firstItem.title.string, UserText.HighlightsOnboardingExperiment.AddressBarPosition.topTitle + " " + UserText.HighlightsOnboardingExperiment.AddressBarPosition.defaultOption) - XCTAssertEqual(firstItem.message, UserText.HighlightsOnboardingExperiment.AddressBarPosition.topMessage) + XCTAssertEqual(firstItem.title.string, UserText.Onboarding.AddressBarPosition.topTitle + " " + UserText.Onboarding.AddressBarPosition.defaultOption) + XCTAssertEqual(firstItem.message, UserText.Onboarding.AddressBarPosition.topMessage) XCTAssertEqual(firstItem.icon, .addressBarTop) XCTAssertFalse(firstItem.isSelected) let secondItem = try XCTUnwrap(items.last) XCTAssertEqual(secondItem.type, .bottom) - XCTAssertEqual(secondItem.title.string, UserText.HighlightsOnboardingExperiment.AddressBarPosition.bottomTitle) - XCTAssertEqual(secondItem.message, UserText.HighlightsOnboardingExperiment.AddressBarPosition.bottomMessage) + XCTAssertEqual(secondItem.title.string, UserText.Onboarding.AddressBarPosition.bottomTitle) + XCTAssertEqual(secondItem.message, UserText.Onboarding.AddressBarPosition.bottomMessage) XCTAssertEqual(secondItem.icon, .addressBarBottom) XCTAssertTrue(secondItem.isSelected) } diff --git a/DuckDuckGoTests/OnboardingDaxFavouritesTests.swift b/DuckDuckGoTests/OnboardingDaxFavouritesTests.swift index 3cbcc0c9e7..06e0523e4b 100644 --- a/DuckDuckGoTests/OnboardingDaxFavouritesTests.swift +++ b/DuckDuckGoTests/OnboardingDaxFavouritesTests.swift @@ -27,6 +27,7 @@ import RemoteMessaging import Configuration import Core import SubscriptionTestingUtilities +import Common @testable import DuckDuckGo final class OnboardingDaxFavouritesTests: XCTestCase { @@ -47,7 +48,8 @@ final class OnboardingDaxFavouritesTests: XCTestCase { settingHandlers: [], favoritesDisplayModeStorage: MockFavoritesDisplayModeStoring(), syncErrorHandler: SyncErrorHandler(), - faviconStoring: MockFaviconStore() + faviconStoring: MockFaviconStore(), + tld: TLD() ) let remoteMessagingClient = RemoteMessagingClient( @@ -134,18 +136,7 @@ final class OnboardingDaxFavouritesTests: XCTestCase { XCTAssertTrue(result) } - func testWhenAddFavouriteIsCalled_ThenItShouldAskContextualOnboardingLogicIfAddFavoriteFlowCanStart() { - // GIVEN - XCTAssertFalse(contextualOnboardingLogicMock.didCallCanEnableAddFavoriteFlow) - - // WHEN - sut.startAddFavoriteFlow() - - // THEN - XCTAssertTrue(contextualOnboardingLogicMock.didCallCanEnableAddFavoriteFlow) - } - - func testWhenAddFavouriteIsCalled_AndCanStartAddFavouriteFlow_ThenItShouldEnableAddFavouriteFlowOnContextualOnboardingLogic() { + func testWhenAddFavouriteIsCalled_ThenItShouldEnableAddFavouriteFlowOnContextualOnboardingLogic() { // GIVEN contextualOnboardingLogicMock.canStartFavoriteFlow = true XCTAssertFalse(contextualOnboardingLogicMock.didCallEnableAddFavoriteFlow) @@ -157,27 +148,4 @@ final class OnboardingDaxFavouritesTests: XCTestCase { XCTAssertTrue(contextualOnboardingLogicMock.didCallEnableAddFavoriteFlow) } - func testWhenAddFavouriteIsCalled_AndCannotStartAddFavouriteFlow_ThenItShouldNotEnableAddFavouriteFlowOnContextualOnboardingLogic() { - // GIVEN - contextualOnboardingLogicMock.canStartFavoriteFlow = false - XCTAssertFalse(contextualOnboardingLogicMock.didCallEnableAddFavoriteFlow) - - // WHEN - sut.startAddFavoriteFlow() - - // THEN - XCTAssertFalse(contextualOnboardingLogicMock.didCallEnableAddFavoriteFlow) - } - - func testWhenAddFavouriteIsCalled_AndCannotStartAddFavouriteFlow_ThenOpenANewTab() { - // GIVEN - contextualOnboardingLogicMock.canStartFavoriteFlow = false - XCTAssertEqual(sut.tabManager.model.tabs.count, 1) - - // WHEN - sut.startAddFavoriteFlow() - - // THEN - XCTAssertEqual(sut.tabManager.model.tabs.count, 2) - } } diff --git a/DuckDuckGoTests/OnboardingIntroViewModelTests.swift b/DuckDuckGoTests/OnboardingIntroViewModelTests.swift index 8d4f854401..37c3654281 100644 --- a/DuckDuckGoTests/OnboardingIntroViewModelTests.swift +++ b/DuckDuckGoTests/OnboardingIntroViewModelTests.swift @@ -58,18 +58,6 @@ final class OnboardingIntroViewModelTests: XCTestCase { XCTAssertEqual(sut.state, .onboarding(.init(type: .startOnboardingDialog, step: .hidden))) } - func testWhenStartOnboardingActionIsCalledThenViewStateChangesToBrowsersComparisonDialog() { - // GIVEN - let sut = OnboardingIntroViewModel(pixelReporter: OnboardingPixelReporterMock(), onboardingManager: onboardingManager) - XCTAssertEqual(sut.state, .landing) - - // WHEN - sut.startOnboardingAction() - - // THEN - XCTAssertEqual(sut.state, .onboarding(.init(type: .browsersComparisonDialog, step: .hidden))) - } - func testWhenSetDefaultBrowserActionIsCalledThenURLOpenerOpensSettingsURL() { // GIVEN let urlOpenerMock = MockURLOpener() @@ -85,45 +73,10 @@ final class OnboardingIntroViewModelTests: XCTestCase { XCTAssertEqual(urlOpenerMock.capturedURL?.absoluteString, UIApplication.openSettingsURLString) } - func testWhenSetDefaultBrowserActionIsCalledThenOnCompletingOnboardingIntroIsCalled() { - // GIVEN - var didCallOnCompletingOnboardingIntro = false - let sut = OnboardingIntroViewModel(pixelReporter: OnboardingPixelReporterMock(), onboardingManager: onboardingManager, urlOpener: MockURLOpener()) - sut.onCompletingOnboardingIntro = { - didCallOnCompletingOnboardingIntro = true - } - XCTAssertFalse(didCallOnCompletingOnboardingIntro) - - // WHEN - sut.setDefaultBrowserAction() - - // THEN - XCTAssertTrue(didCallOnCompletingOnboardingIntro) - } - - func testWhenCancelSetDefaultBrowserActionIsCalledThenOnCompletingOnboardingIntroIsCalled() { - // GIVEN - var didCallOnCompletingOnboardingIntro = false - let sut = OnboardingIntroViewModel(pixelReporter: OnboardingPixelReporterMock(), onboardingManager: onboardingManager, isIpad: false, urlOpener: MockURLOpener()) - sut.onCompletingOnboardingIntro = { - didCallOnCompletingOnboardingIntro = true - } - XCTAssertFalse(didCallOnCompletingOnboardingIntro) - - // WHEN - sut.cancelSetDefaultBrowserAction() - - // THEN - XCTAssertTrue(didCallOnCompletingOnboardingIntro) - } - - // MARK: - Highlights State + Actions iPhone + // MARK: iPhone Flow - // MARK: iPhone - - func testWhenSubscribeToViewStateAndIsHighlightsIphoneFlowThenShouldSendLanding() { + func testWhenSubscribeToViewStateAndIsIphoneFlowThenShouldSendLanding() { // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = true let sut = OnboardingIntroViewModel(pixelReporter: OnboardingPixelReporterMock(), onboardingManager: onboardingManager, isIpad: false, urlOpener: MockURLOpener()) // WHEN @@ -133,9 +86,8 @@ final class OnboardingIntroViewModelTests: XCTestCase { XCTAssertEqual(result, .landing) } - func testWhenOnAppearIsCalledAndAndIsHighlightsIphoneFlowThenViewStateChangesToStartOnboardingDialogAndProgressIsHidden() { + func testWhenOnAppearIsCalledAndAndIsIphoneFlowThenViewStateChangesToStartOnboardingDialogAndProgressIsHidden() { // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = true let sut = OnboardingIntroViewModel(pixelReporter: OnboardingPixelReporterMock(), onboardingManager: onboardingManager, isIpad: false, urlOpener: MockURLOpener()) XCTAssertEqual(sut.state, .landing) @@ -146,22 +98,9 @@ final class OnboardingIntroViewModelTests: XCTestCase { XCTAssertEqual(sut.state, .onboarding(.init(type: .startOnboardingDialog, step: .hidden))) } - func testWhenStartOnboardingActionIsCalledAndIsHighlightsIphoneFlowThenViewStateChangesToBrowsersComparisonDialogAndProgressIs1Of3() { - // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = true - let sut = OnboardingIntroViewModel(pixelReporter: OnboardingPixelReporterMock(), onboardingManager: onboardingManager, isIpad: false) - XCTAssertEqual(sut.state, .landing) - // WHEN - sut.startOnboardingAction() - - // THEN - XCTAssertEqual(sut.state, .onboarding(.init(type: .browsersComparisonDialog, step: .init(currentStep: 1, totalSteps: 3)))) - } - - func testWhenSetDefaultBrowserActionIsCalledAndIsHighlightsIphoneFlowThenViewStateChangesToChooseAppIconDialogAndProgressIs2Of3() { + func testWhenSetDefaultBrowserActionIsCalledAndIsIphoneFlowThenViewStateChangesToChooseAppIconDialogAndProgressIs2Of3() { // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = true let sut = OnboardingIntroViewModel(pixelReporter: OnboardingPixelReporterMock(), onboardingManager: onboardingManager, isIpad: false, urlOpener: MockURLOpener()) XCTAssertEqual(sut.state, .landing) @@ -172,9 +111,8 @@ final class OnboardingIntroViewModelTests: XCTestCase { XCTAssertEqual(sut.state, .onboarding(.init(type: .chooseAppIconDialog, step: .init(currentStep: 2, totalSteps: 3)))) } - func testWhenCancelSetDefaultBrowserActionIsCalledAndIsHighlightsIphoneFlowThenViewStateChangesToChooseAppIconDialogAndProgressIs2Of3() { + func testWhenCancelSetDefaultBrowserActionIsCalledAndIsIphoneFlowThenViewStateChangesToChooseAppIconDialogAndProgressIs2Of3() { // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = true let sut = OnboardingIntroViewModel(pixelReporter: OnboardingPixelReporterMock(), onboardingManager: onboardingManager, isIpad: false, urlOpener: MockURLOpener()) XCTAssertEqual(sut.state, .landing) @@ -185,9 +123,8 @@ final class OnboardingIntroViewModelTests: XCTestCase { XCTAssertEqual(sut.state, .onboarding(.init(type: .chooseAppIconDialog, step: .init(currentStep: 2, totalSteps: 3)))) } - func testWhenAppIconPickerContinueActionIsCalledAndIsHighlightsIphoneFlowThenViewStateChangesToChooseAddressBarPositionDialogAndProgressIs3Of3() { + func testWhenAppIconPickerContinueActionIsCalledAndIsIphoneFlowThenViewStateChangesToChooseAddressBarPositionDialogAndProgressIs3Of3() { // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = true let sut = OnboardingIntroViewModel(pixelReporter: OnboardingPixelReporterMock(), onboardingManager: onboardingManager, isIpad: false) XCTAssertEqual(sut.state, .landing) @@ -198,9 +135,8 @@ final class OnboardingIntroViewModelTests: XCTestCase { XCTAssertEqual(sut.state, .onboarding(.init(type: .chooseAddressBarPositionDialog, step: .init(currentStep: 3, totalSteps: 3)))) } - func testWhenSelectAddressBarPositionActionIsCalledAndIsHighlightsIphoneFlowThenOnCompletingOnboardingIntroIsCalled() { + func testWhenSelectAddressBarPositionActionIsCalledAndIsIphoneFlowThenOnCompletingOnboardingIntroIsCalled() { // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = true var didCallOnCompletingOnboardingIntro = false let sut = OnboardingIntroViewModel(pixelReporter: OnboardingPixelReporterMock(), onboardingManager: onboardingManager, isIpad: false, urlOpener: MockURLOpener()) sut.onCompletingOnboardingIntro = { @@ -217,9 +153,8 @@ final class OnboardingIntroViewModelTests: XCTestCase { // MARK: iPad - func testWhenSubscribeToViewStateAndIsHighlightsIpadFlowThenShouldSendLanding() { + func testWhenSubscribeToViewStateAndIsIpadFlowThenShouldSendLanding() { // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = true let sut = OnboardingIntroViewModel(pixelReporter: OnboardingPixelReporterMock(), onboardingManager: onboardingManager, isIpad: true, urlOpener: MockURLOpener()) // WHEN @@ -229,9 +164,8 @@ final class OnboardingIntroViewModelTests: XCTestCase { XCTAssertEqual(result, .landing) } - func testWhenOnAppearIsCalledAndAndIsHighlightsIpadFlowThenViewStateChangesToStartOnboardingDialogAndProgressIsHidden() { + func testWhenOnAppearIsCalledAndAndIsIpadFlowThenViewStateChangesToStartOnboardingDialogAndProgressIsHidden() { // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = true let sut = OnboardingIntroViewModel(pixelReporter: OnboardingPixelReporterMock(), onboardingManager: onboardingManager, isIpad: true, urlOpener: MockURLOpener()) XCTAssertEqual(sut.state, .landing) @@ -242,9 +176,8 @@ final class OnboardingIntroViewModelTests: XCTestCase { XCTAssertEqual(sut.state, .onboarding(.init(type: .startOnboardingDialog, step: .hidden))) } - func testWhenStartOnboardingActionIsCalledAndIsHighlightsIpadFlowThenViewStateChangesToBrowsersComparisonDialogAndProgressIs1Of3() { + func testWhenStartOnboardingActionIsCalledAndIsIpadFlowThenViewStateChangesToBrowsersComparisonDialogAndProgressIs1Of3() { // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = true let sut = OnboardingIntroViewModel(pixelReporter: OnboardingPixelReporterMock(), onboardingManager: onboardingManager, isIpad: true) XCTAssertEqual(sut.state, .landing) @@ -255,9 +188,8 @@ final class OnboardingIntroViewModelTests: XCTestCase { XCTAssertEqual(sut.state, .onboarding(.init(type: .browsersComparisonDialog, step: .init(currentStep: 1, totalSteps: 2)))) } - func testWhenSetDefaultBrowserActionIsCalledAndIsHighlightsIpadFlowThenViewStateChangesToChooseAppIconDialogAndProgressIs2Of3() { + func testWhenSetDefaultBrowserActionIsCalledAndIsIpadFlowThenViewStateChangesToChooseAppIconDialogAndProgressIs2Of3() { // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = true let sut = OnboardingIntroViewModel(pixelReporter: OnboardingPixelReporterMock(), onboardingManager: onboardingManager, isIpad: true, urlOpener: MockURLOpener()) XCTAssertEqual(sut.state, .landing) @@ -268,9 +200,8 @@ final class OnboardingIntroViewModelTests: XCTestCase { XCTAssertEqual(sut.state, .onboarding(.init(type: .chooseAppIconDialog, step: .init(currentStep: 2, totalSteps: 2)))) } - func testWhenCancelSetDefaultBrowserActionIsCalledAndIsHighlightsIpadFlowThenViewStateChangesToChooseAppIconDialogAndProgressIs2Of3() { + func testWhenCancelSetDefaultBrowserActionIsCalledAndIsIpadFlowThenViewStateChangesToChooseAppIconDialogAndProgressIs2Of3() { // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = true let sut = OnboardingIntroViewModel(pixelReporter: OnboardingPixelReporterMock(), onboardingManager: onboardingManager, isIpad: true, urlOpener: MockURLOpener()) XCTAssertEqual(sut.state, .landing) @@ -281,9 +212,8 @@ final class OnboardingIntroViewModelTests: XCTestCase { XCTAssertEqual(sut.state, .onboarding(.init(type: .chooseAppIconDialog, step: .init(currentStep: 2, totalSteps: 2)))) } - func testWhenAppIconPickerContinueActionIsCalledAndIsHighlightsIphoneFlowThenOnCompletingOnboardingIntroIsCalled() { + func testWhenAppIconPickerContinueActionIsCalledAndIsIphoneFlowThenOnCompletingOnboardingIntroIsCalled() { // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = true var didCallOnCompletingOnboardingIntro = false let sut = OnboardingIntroViewModel(pixelReporter: OnboardingPixelReporterMock(), onboardingManager: onboardingManager, isIpad: true, urlOpener: MockURLOpener()) sut.onCompletingOnboardingIntro = { @@ -339,11 +269,8 @@ final class OnboardingIntroViewModelTests: XCTestCase { XCTAssertTrue(pixelReporterMock.didCallTrackChooseBrowserCTAAction) } - // MARK: - Pixel "Highlights" - func testWhenStateChangesToChooseAppIconThenPixelReporterTrackAppIconImpression() { // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = true let pixelReporterMock = OnboardingPixelReporterMock() let sut = OnboardingIntroViewModel(pixelReporter: pixelReporterMock, onboardingManager: onboardingManager, urlOpener: MockURLOpener()) XCTAssertFalse(pixelReporterMock.didCallTrackBrowserComparisonImpression) @@ -357,7 +284,6 @@ final class OnboardingIntroViewModelTests: XCTestCase { func testWhenAppIconPickerContinueActionIsCalledAndIconIsCustomColorThenPixelReporterTrackCustomAppIconColor() { // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = true let pixelReporterMock = OnboardingPixelReporterMock() let sut = OnboardingIntroViewModel(pixelReporter: pixelReporterMock, onboardingManager: onboardingManager, urlOpener: MockURLOpener(), appIconProvider: { .purple }) XCTAssertFalse(pixelReporterMock.didCallTrackChooseCustomAppIconColor) @@ -371,7 +297,6 @@ final class OnboardingIntroViewModelTests: XCTestCase { func testWhenAppIconPickerContinueActionIsCalledAndIconIsDefaultColorThenPixelReporterDoNotTrackCustomAppIconColor() { // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = true let pixelReporterMock = OnboardingPixelReporterMock() let sut = OnboardingIntroViewModel(pixelReporter: pixelReporterMock, onboardingManager: onboardingManager, urlOpener: MockURLOpener(), appIconProvider: { .defaultAppIcon }) XCTAssertFalse(pixelReporterMock.didCallTrackChooseCustomAppIconColor) @@ -385,7 +310,6 @@ final class OnboardingIntroViewModelTests: XCTestCase { func testWhenStateChangesToChooseAddressBarPositionThenPixelReporterTrackAddressBarSelectionImpression() { // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = true let pixelReporterMock = OnboardingPixelReporterMock() let sut = OnboardingIntroViewModel(pixelReporter: pixelReporterMock, onboardingManager: onboardingManager, isIpad: false, urlOpener: MockURLOpener()) XCTAssertFalse(pixelReporterMock.didCallTrackAddressBarPositionSelectionImpression) @@ -399,7 +323,6 @@ final class OnboardingIntroViewModelTests: XCTestCase { func testWhenSelectAddressBarPositionActionIsCalledAndAddressBarPositionIsBottomThenPixelReporterTrackChooseBottomAddressBarPosition() { // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = true let pixelReporterMock = OnboardingPixelReporterMock() let sut = OnboardingIntroViewModel(pixelReporter: pixelReporterMock, onboardingManager: onboardingManager, urlOpener: MockURLOpener(), addressBarPositionProvider: { .bottom }) XCTAssertFalse(pixelReporterMock.didCallTrackChooseBottomAddressBarPosition) @@ -413,7 +336,6 @@ final class OnboardingIntroViewModelTests: XCTestCase { func testWhenSelectAddressBarPositionActionIsCalledAndAddressBarPositionIsTopThenPixelReporterDoNotTrackChooseBottomAddressBarPosition() { // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = true let pixelReporterMock = OnboardingPixelReporterMock() let sut = OnboardingIntroViewModel(pixelReporter: pixelReporterMock, onboardingManager: onboardingManager, urlOpener: MockURLOpener(), addressBarPositionProvider: { .top }) XCTAssertFalse(pixelReporterMock.didCallTrackChooseBottomAddressBarPosition) @@ -427,59 +349,32 @@ final class OnboardingIntroViewModelTests: XCTestCase { // MARK: - Copy - func testWhenIsNotHighlightsThenIntroTitleIsCorrect() { - // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = false - let sut = OnboardingIntroViewModel(pixelReporter: OnboardingPixelReporterMock(), onboardingManager: onboardingManager, urlOpener: MockURLOpener()) - - // WHEN - let result = sut.copy.introTitle - - // THEN - XCTAssertEqual(result, UserText.DaxOnboardingExperiment.Intro.title) - } - - func testWhenIsHighlightsThenIntroTitleIsCorrect() { + func testIntroTitleIsCorrect() { // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = true let sut = OnboardingIntroViewModel(pixelReporter: OnboardingPixelReporterMock(), onboardingManager: onboardingManager, urlOpener: MockURLOpener()) // WHEN let result = sut.copy.introTitle // THEN - XCTAssertEqual(result, UserText.HighlightsOnboardingExperiment.Intro.title) - } - - func testWhenIsNotHighlightsThenBrowserComparisonTitleIsCorrect() { - // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = false - let sut = OnboardingIntroViewModel(pixelReporter: OnboardingPixelReporterMock(), onboardingManager: onboardingManager, urlOpener: MockURLOpener()) - - // WHEN - let result = sut.copy.browserComparisonTitle - - // THEN - XCTAssertEqual(result, UserText.DaxOnboardingExperiment.BrowsersComparison.title) + XCTAssertEqual(result, UserText.Onboarding.Intro.title) } - func testWhenIsHighlightsThenBrowserComparisonTitleIsCorrect() { + func testBrowserComparisonTitleIsCorrect() { // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = true let sut = OnboardingIntroViewModel(pixelReporter: OnboardingPixelReporterMock(), onboardingManager: onboardingManager, urlOpener: MockURLOpener()) // WHEN let result = sut.copy.browserComparisonTitle // THEN - XCTAssertEqual(result, UserText.HighlightsOnboardingExperiment.BrowsersComparison.title) + XCTAssertEqual(result, UserText.Onboarding.BrowsersComparison.title) } // MARK: - Add To Dock - func testWhenSetDefaultBrowserActionIsCalledAndIsHighlightsIphoneFlowThenViewStateChangesToAddToDockPromoDialogAndProgressIs2Of4() { + func testWhenSetDefaultBrowserActionIsCalledAndIsIphoneFlowThenViewStateChangesToAddToDockPromoDialogAndProgressIs2Of4() { // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = true onboardingManager.addToDockEnabledState = .intro let sut = OnboardingIntroViewModel(pixelReporter: OnboardingPixelReporterMock(), onboardingManager: onboardingManager, isIpad: false, urlOpener: MockURLOpener()) XCTAssertEqual(sut.state, .landing) @@ -491,9 +386,8 @@ final class OnboardingIntroViewModelTests: XCTestCase { XCTAssertEqual(sut.state, .onboarding(.init(type: .addToDockPromoDialog, step: .init(currentStep: 2, totalSteps: 4)))) } - func testWhenAddtoDockContinueActionIsCalledAndIsHighlightsIphoneFlowThenThenViewStateChangesToChooseAppIconAndProgressIs3of4() { + func testWhenAddtoDockContinueActionIsCalledAndIsIphoneFlowThenThenViewStateChangesToChooseAppIconAndProgressIs3of4() { // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = true onboardingManager.addToDockEnabledState = .intro let sut = OnboardingIntroViewModel(pixelReporter: OnboardingPixelReporterMock(), onboardingManager: onboardingManager, isIpad: false) XCTAssertEqual(sut.state, .landing) @@ -509,7 +403,6 @@ final class OnboardingIntroViewModelTests: XCTestCase { func testWhenStateChangesToAddToDockPromoThenPixelReporterTrackAddToDockPromoImpression() { // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = true onboardingManager.addToDockEnabledState = .intro let pixelReporterMock = OnboardingPixelReporterMock() let sut = OnboardingIntroViewModel(pixelReporter: pixelReporterMock, onboardingManager: onboardingManager, urlOpener: MockURLOpener()) @@ -524,7 +417,6 @@ final class OnboardingIntroViewModelTests: XCTestCase { func testWhenAddToDockShowTutorialActionIsCalledThenPixelReporterTrackAddToDockPromoShowTutorialCTA() { // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = true onboardingManager.addToDockEnabledState = .intro let pixelReporterMock = OnboardingPixelReporterMock() let sut = OnboardingIntroViewModel(pixelReporter: pixelReporterMock, onboardingManager: onboardingManager, urlOpener: MockURLOpener()) @@ -539,7 +431,6 @@ final class OnboardingIntroViewModelTests: XCTestCase { func testWhenAddToDockContinueActionIsCalledAndIsShowingFromAddToDockTutorialIsTrueThenPixelReporterTrackAddToDockTutorialDismissCTA() { // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = true onboardingManager.addToDockEnabledState = .intro let pixelReporterMock = OnboardingPixelReporterMock() let sut = OnboardingIntroViewModel(pixelReporter: pixelReporterMock, onboardingManager: onboardingManager, urlOpener: MockURLOpener()) @@ -554,7 +445,6 @@ final class OnboardingIntroViewModelTests: XCTestCase { func testWhenAddToDockContinueActionIsCalledAndIsShowingFromAddToDockTutorialIsFalseThenPixelReporterTrackAddToDockTutorialDismissCTA() { // GIVEN - onboardingManager.isOnboardingHighlightsEnabled = true onboardingManager.addToDockEnabledState = .intro let pixelReporterMock = OnboardingPixelReporterMock() let sut = OnboardingIntroViewModel(pixelReporter: pixelReporterMock, onboardingManager: onboardingManager, urlOpener: MockURLOpener()) diff --git a/DuckDuckGoTests/OnboardingManagerMock.swift b/DuckDuckGoTests/OnboardingManagerMock.swift index dcd8473b60..ad6d3cae1b 100644 --- a/DuckDuckGoTests/OnboardingManagerMock.swift +++ b/DuckDuckGoTests/OnboardingManagerMock.swift @@ -20,7 +20,6 @@ import Foundation @testable import DuckDuckGo -final class OnboardingManagerMock: OnboardingHighlightsManaging, OnboardingAddToDockManaging { - var isOnboardingHighlightsEnabled: Bool = false +final class OnboardingManagerMock: OnboardingAddToDockManaging { var addToDockEnabledState: OnboardingAddToDockState = .disabled } diff --git a/DuckDuckGoTests/OnboardingManagerTests.swift b/DuckDuckGoTests/OnboardingManagerTests.swift index ef7d174fc0..201d577a45 100644 --- a/DuckDuckGoTests/OnboardingManagerTests.swift +++ b/DuckDuckGoTests/OnboardingManagerTests.swift @@ -43,173 +43,105 @@ final class OnboardingManagerTests: XCTestCase { try super.tearDownWithError() } - // MARK: - Onboarding Highlights - - func testWhenIsOnboardingHighlightsLocalFlagEnabledCalledAndAppDefaultsOnboardingHiglightsEnabledIsTrueThenReturnTrue() { - // GIVEN - appSettingsMock.onboardingHighlightsEnabled = true - - // WHEN - let result = sut.isOnboardingHighlightsLocalFlagEnabled - - // THEN - XCTAssertTrue(result) - } + // MARK: - Add to Dock - func testWhenIsOnboardingHighlightsLocalFlagEnabledCalledAndAppDefaultsOnboardingHiglightsEnabledIsFalseThenReturnFalse() { + func testWhenAddToDockLocalFlagStateCalledAndAppDefaultsOnboardingAddToDockStateIsIntroThenReturnIntro() { // GIVEN - appSettingsMock.onboardingHighlightsEnabled = false + appSettingsMock.onboardingAddToDockState = .intro // WHEN - let result = sut.isOnboardingHighlightsLocalFlagEnabled + let result = sut.addToDockLocalFlagState // THEN - XCTAssertFalse(result) + XCTAssertEqual(result, .intro) } - func testWhenIsOnboardingHighlightsFeatureFlagEnabledCalledAndFeaturFlaggerFeatureIsOnThenReturnTrue() { + func testWhenAddToDockLocalFlagStateCalledAndAppDefaultsOnboardingAddToDockStateIsContextualThenReturnContextual() { // GIVEN - featureFlaggerMock.enabledFeatureFlags = [FeatureFlag.onboardingHighlights] + appSettingsMock.onboardingAddToDockState = .contextual // WHEN - let result = sut.isOnboardingHighlightsFeatureFlagEnabled + let result = sut.addToDockLocalFlagState // THEN - XCTAssertTrue(result) + XCTAssertEqual(result, .contextual) } - func testWhenIsOnboardingHighlightsFeatureFlagEnabledCalledAndFeaturFlaggerFeatureIsOffThenReturnFalse() { + func testWhenAddToDockLocalFlagStateCalledAndAppDefaultsOnboardingAddToDockStateIsDisabledThenReturnDisabled() { // GIVEN - featureFlaggerMock.enabledFeatureFlags = [] + appSettingsMock.onboardingAddToDockState = .disabled // WHEN - let result = sut.isOnboardingHighlightsFeatureFlagEnabled + let result = sut.addToDockLocalFlagState // THEN - XCTAssertFalse(result) + XCTAssertEqual(result, .disabled) } - func testWhenIsOnboardingHiglightsEnabledAndIsLocalFlagEnabledIsFalseReturnFalse() { + func testWhenIsAddToDockFeatureFlagEnabledCalledAndFeaturFlaggerFeatureIsOnThenReturnTrue() { // GIVEN - appSettingsMock.onboardingHighlightsEnabled = false - featureFlaggerMock.enabledFeatureFlags = [FeatureFlag.onboardingHighlights] + featureFlaggerMock.enabledFeatureFlags = [FeatureFlag.onboardingAddToDock] // WHEN - let result = sut.isOnboardingHighlightsEnabled + let result = sut.isAddToDockFeatureFlagEnabled // THEN - XCTAssertFalse(result) + XCTAssertTrue(result) } - func testWhenIsOnboardingHiglightsEnabledAndIsFeatureFlagEnabledIsFalseReturnFalse() { + func testWhenIsAddToDockFeatureFlagEnabledCalledAndFeaturFlaggerFeatureIsOffThenReturnFalse() { // GIVEN - appSettingsMock.onboardingHighlightsEnabled = true featureFlaggerMock.enabledFeatureFlags = [] // WHEN - let result = sut.isOnboardingHighlightsEnabled + let result = sut.isAddToDockFeatureFlagEnabled // THEN XCTAssertFalse(result) } - func testWhenIsOnboardingHiglightsEnabledAndIsLocalFlagEnabledIsTrueAndIsFeatureFlagEnabledIsTrueThenReturnTrue() { + func testWhenAddToDockStateCalledAndVariantManagerSupportsAddToDockIntroThenReturnIntro() { // GIVEN - appSettingsMock.onboardingHighlightsEnabled = true - featureFlaggerMock.enabledFeatureFlags = [FeatureFlag.onboardingHighlights] - - // WHEN - let result = sut.isOnboardingHighlightsEnabled - - // THEN - XCTAssertTrue(result) - } - - func testWhenIsOnboardingHiglightsEnabledAndVariantManagerSupportOnboardingHighlightsReturnTrue() { - // GIVEN - variantManagerMock.isSupportedBlock = { _ in true } - appSettingsMock.onboardingHighlightsEnabled = false - featureFlaggerMock.enabledFeatureFlags = [FeatureFlag.onboardingHighlights] - sut = OnboardingManager(appDefaults: appSettingsMock, featureFlagger: featureFlaggerMock, variantManager: variantManagerMock) - - // WHEN - let result = sut.isOnboardingHighlightsEnabled - - // THEN - XCTAssertTrue(result) - } - - func testWhenIsOnboardingHiglightsEnabledAndVariantManagerSupportOnboardingHighlightsReturnFalse() { - // GIVEN - variantManagerMock.isSupportedBlock = { _ in false } - appSettingsMock.onboardingHighlightsEnabled = false - featureFlaggerMock.enabledFeatureFlags = [FeatureFlag.onboardingHighlights] - sut = OnboardingManager(appDefaults: appSettingsMock, featureFlagger: featureFlaggerMock, variantManager: variantManagerMock) - - // WHEN - let result = sut.isOnboardingHighlightsEnabled - - // THEN - XCTAssertFalse(result) - } + variantManagerMock.isSupportedBlock = { feature in + feature == .addToDockIntro + } + sut = OnboardingManager(appDefaults: appSettingsMock, featureFlagger: featureFlaggerMock, variantManager: variantManagerMock, isIphone: true) - // MARK: - Add to Dock - - func testWhenAddToDockLocalFlagStateCalledAndAppDefaultsOnboardingAddToDockStateIsIntroThenReturnIntro() { - // GIVEN - appSettingsMock.onboardingAddToDockState = .intro // WHEN - let result = sut.addToDockLocalFlagState + let result = sut.addToDockEnabledState // THEN XCTAssertEqual(result, .intro) } - func testWhenAddToDockLocalFlagStateCalledAndAppDefaultsOnboardingAddToDockStateIsContextualThenReturnContextual() { + func testWhenAddToDockStateCalledAndVariantManagerSupportsAddToDockContextualThenReturnContextual() { // GIVEN - appSettingsMock.onboardingAddToDockState = .contextual + variantManagerMock.isSupportedBlock = { feature in + feature == .addToDockContextual + } + sut = OnboardingManager(appDefaults: appSettingsMock, featureFlagger: featureFlaggerMock, variantManager: variantManagerMock, isIphone: true) // WHEN - let result = sut.addToDockLocalFlagState + let result = sut.addToDockEnabledState // THEN XCTAssertEqual(result, .contextual) } - func testWhenAddToDockLocalFlagStateCalledAndAppDefaultsOnboardingAddToDockStateIsDisabledThenReturnDisabled() { + func testWhenAddToDockStateCalledAndVariantManagerDoesNotSupportAddToDockThenReturnDisabled() { // GIVEN - appSettingsMock.onboardingAddToDockState = .disabled + variantManagerMock.isSupportedBlock = { _ in + false + } // WHEN - let result = sut.addToDockLocalFlagState + let result = sut.addToDockEnabledState // THEN XCTAssertEqual(result, .disabled) } - func testWhenIsAddToDockFeatureFlagEnabledCalledAndFeaturFlaggerFeatureIsOnThenReturnTrue() { - // GIVEN - featureFlaggerMock.enabledFeatureFlags = [FeatureFlag.onboardingAddToDock] - - // WHEN - let result = sut.isAddToDockFeatureFlagEnabled - - // THEN - XCTAssertTrue(result) - } - - func testWhenIsAddToDockFeatureFlagEnabledCalledAndFeaturFlaggerFeatureIsOffThenReturnFalse() { - // GIVEN - featureFlaggerMock.enabledFeatureFlags = [] - - // WHEN - let result = sut.isAddToDockFeatureFlagEnabled - - // THEN - XCTAssertFalse(result) - } - func testWhenAddToDockStateCalledAndLocalFlagStateIsDisabledAndFeatureFlagIsFalseThenReturnDisabled() { // GIVEN appSettingsMock.onboardingAddToDockState = .disabled diff --git a/DuckDuckGoTests/OnboardingNavigationDelegateTests.swift b/DuckDuckGoTests/OnboardingNavigationDelegateTests.swift index 8914c8d638..63303472e0 100644 --- a/DuckDuckGoTests/OnboardingNavigationDelegateTests.swift +++ b/DuckDuckGoTests/OnboardingNavigationDelegateTests.swift @@ -27,6 +27,7 @@ import RemoteMessaging import Configuration import Combine import SubscriptionTestingUtilities +import Common @testable import DuckDuckGo @testable import Core @@ -47,7 +48,8 @@ final class OnboardingNavigationDelegateTests: XCTestCase { settingHandlers: [], favoritesDisplayModeStorage: MockFavoritesDisplayModeStoring(), syncErrorHandler: SyncErrorHandler(), - faviconStoring: MockFaviconStore() + faviconStoring: MockFaviconStore(), + tld: TLD() ) let remoteMessagingClient = RemoteMessagingClient( diff --git a/DuckDuckGoTests/OnboardingSuggestedSearchesProviderTests.swift b/DuckDuckGoTests/OnboardingSuggestedSearchesProviderTests.swift index ced3c4b449..1444ef350a 100644 --- a/DuckDuckGoTests/OnboardingSuggestedSearchesProviderTests.swift +++ b/DuckDuckGoTests/OnboardingSuggestedSearchesProviderTests.swift @@ -23,8 +23,7 @@ import Onboarding class OnboardingSuggestedSearchesProviderTests: XCTestCase { private var onboardingManagerMock: OnboardingManagerMock! - let userText = UserText.DaxOnboardingExperiment.ContextualOnboarding.self - let highlightsUserText = UserText.HighlightsOnboardingExperiment.ContextualOnboarding.self + let userText = UserText.Onboarding.ContextualOnboarding.self static let imageSearch = "!image " override func setUpWithError() throws { @@ -39,13 +38,12 @@ class OnboardingSuggestedSearchesProviderTests: XCTestCase { func testSearchesListForEnglishLanguageAndUsRegion() { let mockProvider = MockOnboardingRegionAndLanguageProvider(regionCode: "US", languageCode: "en") - let provider = OnboardingSuggestedSearchesProvider(countryAndLanguageProvider: mockProvider, onboardingManager: onboardingManagerMock) + let provider = OnboardingSuggestedSearchesProvider(countryAndLanguageProvider: mockProvider) let expectedSearches = [ ContextualOnboardingListItem.search(title: userText.tryASearchOption1English), ContextualOnboardingListItem.search(title: userText.tryASearchOption2English), - ContextualOnboardingListItem.search(title: userText.tryASearchOption3), - ContextualOnboardingListItem.surprise(title: userText.tryASearchOptionSurpriseMeEnglish, visibleTitle: "Surprise me!") + ContextualOnboardingListItem.surprise(title: Self.imageSearch + userText.tryASearchOptionSurpriseMe, visibleTitle: "Surprise me!") ] XCTAssertEqual(provider.list, expectedSearches) @@ -53,13 +51,12 @@ class OnboardingSuggestedSearchesProviderTests: XCTestCase { func testSearchesListForNonEnglishLanguageAndNonUSRegion() { let mockProvider = MockOnboardingRegionAndLanguageProvider(regionCode: "FR", languageCode: "fr") - let provider = OnboardingSuggestedSearchesProvider(countryAndLanguageProvider: mockProvider, onboardingManager: onboardingManagerMock) + let provider = OnboardingSuggestedSearchesProvider(countryAndLanguageProvider: mockProvider) let expectedSearches = [ ContextualOnboardingListItem.search(title: userText.tryASearchOption1International), ContextualOnboardingListItem.search(title: userText.tryASearchOption2International), - ContextualOnboardingListItem.search(title: userText.tryASearchOption3), - ContextualOnboardingListItem.surprise(title: userText.tryASearchOptionSurpriseMeInternational, visibleTitle: "Surprise me!") + ContextualOnboardingListItem.surprise(title: Self.imageSearch + userText.tryASearchOptionSurpriseMe, visibleTitle: "Surprise me!") ] XCTAssertEqual(provider.list, expectedSearches) @@ -67,57 +64,12 @@ class OnboardingSuggestedSearchesProviderTests: XCTestCase { func testSearchesListForUSRegionAndNonEnglishLanguage() { let mockProvider = MockOnboardingRegionAndLanguageProvider(regionCode: "US", languageCode: "es") - let provider = OnboardingSuggestedSearchesProvider(countryAndLanguageProvider: mockProvider, onboardingManager: onboardingManagerMock) + let provider = OnboardingSuggestedSearchesProvider(countryAndLanguageProvider: mockProvider) let expectedSearches = [ ContextualOnboardingListItem.search(title: userText.tryASearchOption1International), ContextualOnboardingListItem.search(title: userText.tryASearchOption2English), - ContextualOnboardingListItem.search(title: userText.tryASearchOption3), - ContextualOnboardingListItem.surprise(title: userText.tryASearchOptionSurpriseMeEnglish, visibleTitle: "Surprise me!") - ] - - XCTAssertEqual(provider.list, expectedSearches) - } - - // MARK: - Higlights Experiment - - func testWhenHighlightsOnboardingAndSearchesListForEnglishLanguageAndUsRegionThenDoNotReturnOption3() { - onboardingManagerMock.isOnboardingHighlightsEnabled = true - let mockProvider = MockOnboardingRegionAndLanguageProvider(regionCode: "US", languageCode: "en") - let provider = OnboardingSuggestedSearchesProvider(countryAndLanguageProvider: mockProvider, onboardingManager: onboardingManagerMock) - - let expectedSearches = [ - ContextualOnboardingListItem.search(title: userText.tryASearchOption1English), - ContextualOnboardingListItem.search(title: userText.tryASearchOption2English), - ContextualOnboardingListItem.surprise(title: Self.imageSearch + highlightsUserText.tryASearchOptionSurpriseMe, visibleTitle: "Surprise me!") - ] - - XCTAssertEqual(provider.list, expectedSearches) - } - - func testWhenHighlightsOnboardingAndSearchesListForNonEnglishLanguageAndNonUSRegionThenDoNotReturnOption3() { - onboardingManagerMock.isOnboardingHighlightsEnabled = true - let mockProvider = MockOnboardingRegionAndLanguageProvider(regionCode: "FR", languageCode: "fr") - let provider = OnboardingSuggestedSearchesProvider(countryAndLanguageProvider: mockProvider, onboardingManager: onboardingManagerMock) - - let expectedSearches = [ - ContextualOnboardingListItem.search(title: userText.tryASearchOption1International), - ContextualOnboardingListItem.search(title: userText.tryASearchOption2International), - ContextualOnboardingListItem.surprise(title: Self.imageSearch + highlightsUserText.tryASearchOptionSurpriseMe, visibleTitle: "Surprise me!") - ] - - XCTAssertEqual(provider.list, expectedSearches) - } - - func testWhenHighlightsOnboardingAndSearchesListForUSRegionAndNonEnglishLanguageThenDoNotReturnOption3() { - onboardingManagerMock.isOnboardingHighlightsEnabled = true - let mockProvider = MockOnboardingRegionAndLanguageProvider(regionCode: "US", languageCode: "es") - let provider = OnboardingSuggestedSearchesProvider(countryAndLanguageProvider: mockProvider, onboardingManager: onboardingManagerMock) - - let expectedSearches = [ - ContextualOnboardingListItem.search(title: userText.tryASearchOption1International), - ContextualOnboardingListItem.search(title: userText.tryASearchOption2English), - ContextualOnboardingListItem.surprise(title: Self.imageSearch + highlightsUserText.tryASearchOptionSurpriseMe, visibleTitle: "Surprise me!") + ContextualOnboardingListItem.surprise(title: Self.imageSearch + userText.tryASearchOptionSurpriseMe, visibleTitle: "Surprise me!") ] XCTAssertEqual(provider.list, expectedSearches) diff --git a/DuckDuckGoTests/SmallOmniBarStateTests.swift b/DuckDuckGoTests/SmallOmniBarStateTests.swift index 3cc7f5bf49..d81eb1f171 100644 --- a/DuckDuckGoTests/SmallOmniBarStateTests.swift +++ b/DuckDuckGoTests/SmallOmniBarStateTests.swift @@ -45,7 +45,7 @@ class SmallOmniBarStateTests: XCTestCase { XCTAssertFalse(testee.showBackButton) XCTAssertFalse(testee.showForwardButton) XCTAssertFalse(testee.showBookmarksButton) - XCTAssertFalse(testee.showShareButton) + XCTAssertFalse(testee.showAccessoryButton) } func testWhenInHomeEmptyEditingStateWithVoiceSearchThenCorrectButtonsAreShown() { @@ -66,7 +66,7 @@ class SmallOmniBarStateTests: XCTestCase { XCTAssertFalse(testee.showBackButton) XCTAssertFalse(testee.showForwardButton) XCTAssertFalse(testee.showBookmarksButton) - XCTAssertFalse(testee.showShareButton) + XCTAssertFalse(testee.showAccessoryButton) } func testWhenEnteringHomeEmptyEditingStateThenTextIsCleared() { @@ -121,7 +121,7 @@ class SmallOmniBarStateTests: XCTestCase { XCTAssertFalse(testee.showBackButton) XCTAssertFalse(testee.showForwardButton) XCTAssertFalse(testee.showBookmarksButton) - XCTAssertFalse(testee.showShareButton) + XCTAssertFalse(testee.showAccessoryButton) } func testWhenInHomeTextEditingStateWithVoiceSearchThenCorrectButtonsAreShown() { @@ -141,7 +141,7 @@ class SmallOmniBarStateTests: XCTestCase { XCTAssertFalse(testee.showBackButton) XCTAssertFalse(testee.showForwardButton) XCTAssertFalse(testee.showBookmarksButton) - XCTAssertFalse(testee.showShareButton) + XCTAssertFalse(testee.showAccessoryButton) } func testWhenEnteringHomeTextEditingStateThenTextIsNotCleared() { @@ -196,7 +196,7 @@ class SmallOmniBarStateTests: XCTestCase { XCTAssertFalse(testee.showBackButton) XCTAssertFalse(testee.showForwardButton) XCTAssertFalse(testee.showBookmarksButton) - XCTAssertFalse(testee.showShareButton) + XCTAssertFalse(testee.showAccessoryButton) } func testWhenInHomeNonEditingStateWithVoiceSearchThenCorrectButtonsAreShown() { @@ -216,7 +216,7 @@ class SmallOmniBarStateTests: XCTestCase { XCTAssertFalse(testee.showBackButton) XCTAssertFalse(testee.showForwardButton) XCTAssertFalse(testee.showBookmarksButton) - XCTAssertFalse(testee.showShareButton) + XCTAssertFalse(testee.showAccessoryButton) } func testWhenEnteringHomeNonEditingStateThenTextIsCleared() { @@ -271,7 +271,7 @@ class SmallOmniBarStateTests: XCTestCase { XCTAssertFalse(testee.showBackButton) XCTAssertFalse(testee.showForwardButton) XCTAssertFalse(testee.showBookmarksButton) - XCTAssertFalse(testee.showShareButton) + XCTAssertFalse(testee.showAccessoryButton) } func testWhenInBrowserEmptyEditingStateWithVoiceSearchThenCorrectButtonsAreShown() { @@ -291,7 +291,7 @@ class SmallOmniBarStateTests: XCTestCase { XCTAssertFalse(testee.showBackButton) XCTAssertFalse(testee.showForwardButton) XCTAssertFalse(testee.showBookmarksButton) - XCTAssertFalse(testee.showShareButton) + XCTAssertFalse(testee.showAccessoryButton) } func testWhenEnteringBrowserEmptyEditingStateThenTextIsCleared() { @@ -346,7 +346,7 @@ class SmallOmniBarStateTests: XCTestCase { XCTAssertFalse(testee.showBackButton) XCTAssertFalse(testee.showForwardButton) XCTAssertFalse(testee.showBookmarksButton) - XCTAssertFalse(testee.showShareButton) + XCTAssertFalse(testee.showAccessoryButton) } func testWhenInBrowsingTextEditingStateWithVoiceSearchThenCorrectButtonsAreShown() { @@ -366,7 +366,7 @@ class SmallOmniBarStateTests: XCTestCase { XCTAssertFalse(testee.showBackButton) XCTAssertFalse(testee.showForwardButton) XCTAssertFalse(testee.showBookmarksButton) - XCTAssertFalse(testee.showShareButton) + XCTAssertFalse(testee.showAccessoryButton) } func testWhenEnteringBrowsingTextEditingStateThenTextIsMaintained() { @@ -420,7 +420,7 @@ class SmallOmniBarStateTests: XCTestCase { XCTAssertFalse(testee.showBackButton) XCTAssertFalse(testee.showForwardButton) XCTAssertFalse(testee.showBookmarksButton) - XCTAssertTrue(testee.showShareButton) + XCTAssertTrue(testee.showAccessoryButton) } func testWhenEnteringBrowsingNonEditingStateThenTextIsMaintained() { diff --git a/DuckDuckGoTests/StatisticsLoaderTests.swift b/DuckDuckGoTests/StatisticsLoaderTests.swift index f066097e99..f64d667df5 100644 --- a/DuckDuckGoTests/StatisticsLoaderTests.swift +++ b/DuckDuckGoTests/StatisticsLoaderTests.swift @@ -27,20 +27,30 @@ class StatisticsLoaderTests: XCTestCase { var mockStatisticsStore: StatisticsStore! var mockUsageSegmentation: MockUsageSegmentation! + var mockPixelFiring: PixelFiringMock.Type! var testee: StatisticsLoader! + private var fireAppRetentionExperimentPixelsCalled = false + private var fireSearchExperimentPixelsCalled = false override func setUpWithError() throws { try super.setUpWithError() + PixelFiringMock.tearDown() + + mockPixelFiring = PixelFiringMock.self mockStatisticsStore = MockStatisticsStore() mockUsageSegmentation = MockUsageSegmentation() testee = StatisticsLoader(statisticsStore: mockStatisticsStore, usageSegmentation: mockUsageSegmentation, - inconsistencyMonitoring: MockStatisticsStoreInconsistencyMonitoring()) + inconsistencyMonitoring: MockStatisticsStoreInconsistencyMonitoring(), + fireAppRetentionExperimentPixels: { self.fireAppRetentionExperimentPixelsCalled = true }, + fireSearchExperimentPixels: { self.fireSearchExperimentPixelsCalled = true }, + pixelFiring: mockPixelFiring) } override func tearDown() { HTTPStubs.removeAllStubs() + PixelFiringMock.tearDown() super.tearDown() } @@ -56,6 +66,7 @@ class StatisticsLoaderTests: XCTestCase { } wait(for: [testExpectation], timeout: 5.0) XCTAssertTrue(mockUsageSegmentation.atbs[0].installAtb.isReturningUser) + XCTAssertTrue(fireAppRetentionExperimentPixelsCalled) } func testWhenReturnUser_ThenSegmentationIncludesCorrectVariant() { @@ -70,6 +81,7 @@ class StatisticsLoaderTests: XCTestCase { } wait(for: [testExpectation], timeout: 5.0) XCTAssertTrue(mockUsageSegmentation.atbs[0].installAtb.isReturningUser) + XCTAssertTrue(fireSearchExperimentPixelsCalled) } func testWhenSearchRefreshHappensButNotInstalled_ThenRetentionSegmentationNotified() { @@ -81,6 +93,7 @@ class StatisticsLoaderTests: XCTestCase { } wait(for: [testExpectation], timeout: 5.0) XCTAssertFalse(mockUsageSegmentation.atbs.isEmpty) + XCTAssertTrue(fireSearchExperimentPixelsCalled) } func testWhenAppRefreshHappensButNotInstalled_ThenRetentionSegmentationNotified() { @@ -92,6 +105,7 @@ class StatisticsLoaderTests: XCTestCase { } wait(for: [testExpectation], timeout: 5.0) XCTAssertFalse(mockUsageSegmentation.atbs.isEmpty) + XCTAssertTrue(fireAppRetentionExperimentPixelsCalled) } func testWhenStatisticsInstalled_ThenRetentionSegmentationNotNotified() { @@ -116,6 +130,7 @@ class StatisticsLoaderTests: XCTestCase { } wait(for: [testExpectation], timeout: 5.0) XCTAssertFalse(mockUsageSegmentation.atbs.isEmpty) + XCTAssertTrue(fireAppRetentionExperimentPixelsCalled) } func testWhenSearchRetentionRefreshHappens_ThenRetentionSegmentationNotified() { @@ -129,6 +144,7 @@ class StatisticsLoaderTests: XCTestCase { } wait(for: [testExpectation], timeout: 5.0) XCTAssertFalse(mockUsageSegmentation.atbs.isEmpty) + XCTAssertTrue(self.fireSearchExperimentPixelsCalled) } func testWhenSearchRefreshHasSuccessfulUpdateAtbRequestThenSearchRetentionAtbUpdated() { @@ -145,6 +161,7 @@ class StatisticsLoaderTests: XCTestCase { } waitForExpectations(timeout: 5, handler: nil) + XCTAssertTrue(self.fireSearchExperimentPixelsCalled) } func testWhenAppRefreshHasSuccessfulUpdateAtbRequestThenAppRetentionAtbUpdated() { @@ -161,6 +178,7 @@ class StatisticsLoaderTests: XCTestCase { } waitForExpectations(timeout: 5, handler: nil) + XCTAssertTrue(self.fireAppRetentionExperimentPixelsCalled) } func testWhenLoadHasSuccessfulAtbAndExtiRequestsThenStoreUpdatedWithVariant() { @@ -270,6 +288,19 @@ class StatisticsLoaderTests: XCTestCase { waitForExpectations(timeout: 5, handler: nil) } + func testWhenInstallStatisticsRequestedThenInstallPixelIsFired() { + loadSuccessfulExiStub() + + let testExpectation = expectation(description: "refresh complete") + testee.refreshAppRetentionAtb { + Thread.sleep(forTimeInterval: .seconds(0.1)) + testExpectation.fulfill() + } + + wait(for: [testExpectation], timeout: 5.0) + XCTAssertEqual(mockPixelFiring.lastPixelName, Pixel.Event.appInstall.name) + } + func loadSuccessfulAtbStub() { stub(condition: isHost(URL.atb.host!)) { _ in let path = OHPathForFile("MockFiles/atb.json", type(of: self))! diff --git a/DuckDuckGoTests/SyncCredentialsAdapterTests.swift b/DuckDuckGoTests/SyncCredentialsAdapterTests.swift index 3f6f7635ac..78b12db40c 100644 --- a/DuckDuckGoTests/SyncCredentialsAdapterTests.swift +++ b/DuckDuckGoTests/SyncCredentialsAdapterTests.swift @@ -23,6 +23,7 @@ import Combine import DDGSync import SecureStorage import Core +import Common @testable import DuckDuckGo final class SyncCredentialsAdapterTests: XCTestCase { @@ -34,7 +35,7 @@ final class SyncCredentialsAdapterTests: XCTestCase { override func setUpWithError() throws { errorHandler = CapturingAdapterErrorHandler() - adapter = SyncCredentialsAdapter(secureVaultErrorReporter: MockSecureVaultReporting(), syncErrorHandler: errorHandler) + adapter = SyncCredentialsAdapter(secureVaultErrorReporter: MockSecureVaultReporting(), syncErrorHandler: errorHandler, tld: TLD()) cancellables = [] } diff --git a/DuckDuckGoTests/SyncSettingsViewControllerErrorTests.swift b/DuckDuckGoTests/SyncSettingsViewControllerErrorTests.swift index d929969a45..0233f92598 100644 --- a/DuckDuckGoTests/SyncSettingsViewControllerErrorTests.swift +++ b/DuckDuckGoTests/SyncSettingsViewControllerErrorTests.swift @@ -23,6 +23,7 @@ import Core import Combine import DDGSync import Persistence +import Common final class SyncSettingsViewControllerErrorTests: XCTestCase { @@ -53,7 +54,8 @@ final class SyncSettingsViewControllerErrorTests: XCTestCase { faviconStoring: MockFaviconStore()) let credentialsAdapter = SyncCredentialsAdapter( secureVaultErrorReporter: MockSecureVaultReporting(), - syncErrorHandler: CapturingAdapterErrorHandler()) + syncErrorHandler: CapturingAdapterErrorHandler(), + tld: TLD()) vc = SyncSettingsViewController( syncService: ddgSyncing, syncBookmarksAdapter: bookmarksAdapter, diff --git a/DuckDuckGoTests/TabSwitcherOpenDailyPixelTests.swift b/DuckDuckGoTests/TabSwitcherOpenDailyPixelTests.swift index 9d5d9c984c..4b8aae8e9a 100644 --- a/DuckDuckGoTests/TabSwitcherOpenDailyPixelTests.swift +++ b/DuckDuckGoTests/TabSwitcherOpenDailyPixelTests.swift @@ -51,12 +51,8 @@ final class TabSwitcherOpenDailyPixelTests: XCTestCase { 21...40: "21-40", 41...60: "41-60", 61...80: "61-80", - 81...100: "81-100", - 101...125: "101-125", - 126...150: "126-150", - 151...250: "151-250", - 251...500: "251-500", - 501...504: "501+"] + 81...90: "81+", + 501...504: "81+"] for bucket in bucketValues { for value in bucket.key { diff --git a/DuckDuckGoTests/TabViewControllerDaxDialogTests.swift b/DuckDuckGoTests/TabViewControllerDaxDialogTests.swift index ebb5810a83..ea943dfba3 100644 --- a/DuckDuckGoTests/TabViewControllerDaxDialogTests.swift +++ b/DuckDuckGoTests/TabViewControllerDaxDialogTests.swift @@ -233,7 +233,6 @@ final class ContextualOnboardingLogicMock: ContextualOnboardingLogic { private(set) var didCallSetFireEducationMessageSeen = false private(set) var didCallsetFinalOnboardingDialogSeen = false private(set) var didCallsetsetSearchMessageSeen = false - private(set) var didCallCanEnableAddFavoriteFlow = false private(set) var didCallEnableAddFavoriteFlow = false private(set) var didCallSetDaxDialogDismiss = false private(set) var didCallClearedBrowserData = false @@ -263,11 +262,6 @@ final class ContextualOnboardingLogicMock: ContextualOnboardingLogic { } - func canEnableAddFavoriteFlow() -> Bool { - didCallCanEnableAddFavoriteFlow = true - return canStartFavoriteFlow - } - func enableAddFavoriteFlow() { didCallEnableAddFavoriteFlow = true } diff --git a/DuckDuckGoTests/YoutublePlayerNavigationHandlerTests.swift b/DuckDuckGoTests/YoutublePlayerNavigationHandlerTests.swift index 4b968fcd68..3ae7bc73ea 100644 --- a/DuckDuckGoTests/YoutublePlayerNavigationHandlerTests.swift +++ b/DuckDuckGoTests/YoutublePlayerNavigationHandlerTests.swift @@ -582,4 +582,24 @@ class DuckPlayerNavigationHandlerTests: XCTestCase { XCTAssertNil(tabNavigator.openedURL, "No new tabs should open") } + @MainActor + func testHandleDelegateNavigation_YoutubeWatchURLWithDuckPlayerEnabledAndSameVideoNavigation_ReturnsFalse() async { + // Arrange + let youtubeURL = URL(string: "https://www.youtube.com/watch?v=abc123")! + let youtubeInternalURL = URL(string: "https://www.youtube.com/watch?v=abc123&settings")! + let request = URLRequest(url: youtubeURL) + let mockFrameInfo = MockFrameInfo(isMainFrame: true) + let navigationAction = MockNavigationAction(request: request, targetFrame: mockFrameInfo) + playerSettings.mode = .enabled + featureFlagger.enabledFeatures = [.duckPlayer, .duckPlayerOpenInNewTab] + + mockWebView.setCurrentURL(youtubeInternalURL) + + // Act + let shouldCancel = handler.handleDelegateNavigation(navigationAction: navigationAction, webView: mockWebView) + + // Assert + XCTAssertFalse(shouldCancel, "Expected navigation NOT to be cancelled as it's Youtube Internal navigation") + } + } diff --git a/IntegrationTests/AtbServerTests.swift b/IntegrationTests/AtbServerTests.swift index 029e2bba34..624e66a3e4 100644 --- a/IntegrationTests/AtbServerTests.swift +++ b/IntegrationTests/AtbServerTests.swift @@ -20,6 +20,8 @@ import XCTest @testable import Core @testable import BrowserServicesKit +import Combine +import PixelKit class AtbServerTests: XCTestCase { @@ -32,7 +34,7 @@ class AtbServerTests: XCTestCase { override func setUp() { super.setUp() - + PixelKit.configureExperimentKit(featureFlagger: MockFeatureFlagger()) store = MockStatisticsStore() loader = StatisticsLoader(statisticsStore: store, inconsistencyMonitoring: MockStatisticsStoreInconsistencyMonitoring()) @@ -136,3 +138,32 @@ private struct MockStatisticsStoreInconsistencyMonitoring: StatisticsStoreIncons } } + +class MockFeatureFlagger: FeatureFlagger { + func isFeatureOn(for featureFlag: Flag, allowOverride: Bool) -> Bool where Flag: FeatureFlagDescribing { + return false + } + + var internalUserDecider: any InternalUserDecider = MockInteranlUserDecider() + + var localOverrides: (any BrowserServicesKit.FeatureFlagLocalOverriding)? + + func getCohortIfEnabled(for featureFlag: Flag) -> (any FlagCohort)? where Flag: FeatureFlagExperimentDescribing { + return nil + } + + func getAllActiveExperiments() -> Experiments { + return [:] + } + +} + +class MockInteranlUserDecider: InternalUserDecider { + var isInternalUser: Bool = false + + var isInternalUserPublisher: AnyPublisher = Just(false).eraseToAnyPublisher() + + func markUserAsInternalIfNeeded(forUrl url: URL?, response: HTTPURLResponse?) -> Bool { + return false + } +} diff --git a/LocalPackages/AIChat/Package.swift b/LocalPackages/AIChat/Package.swift new file mode 100644 index 0000000000..478b4b4705 --- /dev/null +++ b/LocalPackages/AIChat/Package.swift @@ -0,0 +1,24 @@ +// swift-tools-version: 6.0 +// The swift-tools-version declares the minimum version of Swift required to build this package. +import PackageDescription + +let package = Package( + name: "AIChat", + platforms: [ + .iOS(.v15) + ], + products: [ + .library( + name: "AIChat", + targets: ["AIChat"] + ), + ], + targets: [ + .target( + name: "AIChat", + resources: [ + .process("Resources/Assets.xcassets") + ] + ), + ] +) diff --git a/LocalPackages/AIChat/Sources/AIChat/AIChatViewModel.swift b/LocalPackages/AIChat/Sources/AIChat/AIChatViewModel.swift new file mode 100644 index 0000000000..8c936f45cb --- /dev/null +++ b/LocalPackages/AIChat/Sources/AIChat/AIChatViewModel.swift @@ -0,0 +1,45 @@ +// +// AIChatViewModel.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import WebKit +import Combine +import os.log + +protocol AIChatViewModeling { + /// The URL to be loaded in the AI Chat View Controller's web view. + var aiChatURL: URL { get } + + /// The configuration settings for the web view used in the AI Chat. + /// This configuration can include preferences such as data storage + var webViewConfiguration: WKWebViewConfiguration { get } +} + +final class AIChatViewModel: AIChatViewModeling { + private let settings: AIChatSettingsProvider + let webViewConfiguration: WKWebViewConfiguration + + init(webViewConfiguration: WKWebViewConfiguration, settings: AIChatSettingsProvider) { + self.webViewConfiguration = webViewConfiguration + self.settings = settings + } + + var aiChatURL: URL { + settings.aiChatURL + } +} diff --git a/LocalPackages/AIChat/Sources/AIChat/AIChatWebViewController.swift b/LocalPackages/AIChat/Sources/AIChat/AIChatWebViewController.swift new file mode 100644 index 0000000000..2c2e47fa20 --- /dev/null +++ b/LocalPackages/AIChat/Sources/AIChat/AIChatWebViewController.swift @@ -0,0 +1,133 @@ +// +// AIChatWebViewController.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +import UIKit +import WebKit + +protocol AIChatWebViewControllerDelegate: AnyObject { + @MainActor func aiChatWebViewController(_ viewController: AIChatWebViewController, didRequestToLoad url: URL) +} + +final class AIChatWebViewController: UIViewController { + weak var delegate: AIChatWebViewControllerDelegate? + private let chatModel: AIChatViewModeling + + private lazy var webView: WKWebView = { + let webView = WKWebView(frame: .zero, configuration: chatModel.webViewConfiguration) + webView.isOpaque = false /// Required to make the background color visible + webView.backgroundColor = .webViewBackgroundColor + webView.navigationDelegate = self + webView.translatesAutoresizingMaskIntoConstraints = false + return webView + }() + + private lazy var loadingView: UIActivityIndicatorView = { + let activityIndicator = UIActivityIndicatorView(style: .large) + activityIndicator.color = .label + activityIndicator.translatesAutoresizingMaskIntoConstraints = false + activityIndicator.hidesWhenStopped = true + return activityIndicator + }() + + init(chatModel: AIChatViewModeling) { + self.chatModel = chatModel + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + + view.backgroundColor = .black + + setupWebView() + setupLoadingView() + loadWebsite() + } + + private func setupWebView() { + view.addSubview(webView) + + NSLayoutConstraint.activate([ + webView.topAnchor.constraint(equalTo: view.topAnchor), + webView.bottomAnchor.constraint(equalTo: view.bottomAnchor), + webView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + webView.trailingAnchor.constraint(equalTo: view.trailingAnchor) + ]) + } + + private func setupLoadingView() { + view.addSubview(loadingView) + + NSLayoutConstraint.activate([ + loadingView.centerXAnchor.constraint(equalTo: view.centerXAnchor), + loadingView.centerYAnchor.constraint(equalTo: view.centerYAnchor) + ]) + } +} + +// MARK: - WebView functions + +extension AIChatWebViewController { + + func reload() { + loadWebsite() + } + + private func loadWebsite() { + let request = URLRequest(url: chatModel.aiChatURL) + webView.load(request) + } +} + +// MARK: - WKNavigationDelegate + +extension AIChatWebViewController: WKNavigationDelegate { + + func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction) async -> WKNavigationActionPolicy { + if let url = navigationAction.request.url { + if url == chatModel.aiChatURL || navigationAction.targetFrame?.isMainFrame == false { + return .allow + } else { + delegate?.aiChatWebViewController(self, didRequestToLoad: url) + return .cancel + } + } else { + return .allow + } + } + + func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) { + loadingView.startAnimating() + } + + func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { + loadingView.stopAnimating() + } + + func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) { + loadingView.stopAnimating() + } + + func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) { + loadingView.stopAnimating() + } +} diff --git a/LocalPackages/AIChat/Sources/AIChat/Public API/AIChatSettingsProvider.swift b/LocalPackages/AIChat/Sources/AIChat/Public API/AIChatSettingsProvider.swift new file mode 100644 index 0000000000..7d9aff7e19 --- /dev/null +++ b/LocalPackages/AIChat/Sources/AIChat/Public API/AIChatSettingsProvider.swift @@ -0,0 +1,46 @@ +// +// AIChatSettingsProvider.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +public protocol AIChatSettingsProvider { + /// The URL used to open AI Chat in the `AIChatViewController`. + var aiChatURL: URL { get } + + /// The user settings state for the AI Chat browsing address bar. + var isAIChatAddressBarUserSettingsEnabled: Bool { get } + + /// The user settings state for the AI Chat browsing menu icon. + var isAIChatBrowsingMenuUserSettingsEnabled: Bool { get } + + /// The remote feature flag state for AI Chat. + var isAIChatFeatureEnabled: Bool { get } + + /// The remote feature flag for the AI Chat shortcut in the browsing menu. + var isAIChatBrowsingMenubarShortcutFeatureEnabled: Bool { get } + + /// The remote feature flag for the AI Chat shortcut in the address bar. + var isAIChatAddressBarShortcutFeatureEnabled: Bool { get } + + /// Updates the user settings state for the AI Chat browsing menu. + func enableAIChatBrowsingMenuUserSettings(enable: Bool) + + /// Updates the user settings state for the AI Chat address bar. + func enableAIChatAddressBarUserSettings(enable: Bool) +} diff --git a/LocalPackages/AIChat/Sources/AIChat/Public API/AIChatViewController.swift b/LocalPackages/AIChat/Sources/AIChat/Public API/AIChatViewController.swift new file mode 100644 index 0000000000..31d394969c --- /dev/null +++ b/LocalPackages/AIChat/Sources/AIChat/Public API/AIChatViewController.swift @@ -0,0 +1,122 @@ +// +// AIChatViewController.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +import UIKit +import Combine +import WebKit + +/// A protocol that defines the delegate methods for `AIChatViewController`. +public protocol AIChatViewControllerDelegate: AnyObject { + /// Tells the delegate that a request to load a URL has been made. + /// + /// - Parameters: + /// - viewController: The `AIChatViewController` instance making the request. + /// - url: The `URL` that is requested to be loaded. + func aiChatViewController(_ viewController: AIChatViewController, didRequestToLoad url: URL) +} + +public final class AIChatViewController: UIViewController { + public weak var delegate: AIChatViewControllerDelegate? + private let chatModel: AIChatViewModeling + private var webViewController: AIChatWebViewController? + + /// Initializes a new instance of `AIChatViewController` with the specified remote settings and web view configuration. + /// + /// - Parameters: + /// - remoteSettings: An object conforming to `AIChatSettingsProvider` that provides remote settings. + /// - webViewConfiguration: A `WKWebViewConfiguration` object used to configure the web view. + public convenience init(settings: AIChatSettingsProvider, webViewConfiguration: WKWebViewConfiguration) { + let chatModel = AIChatViewModel(webViewConfiguration: webViewConfiguration, settings: settings) + self.init(chatModel: chatModel) + } + + internal init(chatModel: AIChatViewModeling) { + self.chatModel = chatModel + + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +// MARK: - Lifecycle +extension AIChatViewController { + + public override func viewDidLoad() { + super.viewDidLoad() + self.view.backgroundColor = .black + } + + public override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + addWebViewController() + } + + public override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + /// Clean up the previous conversation and prepare duck.ai for future presentation + webViewController?.reload() + } + + public override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + + if viewIfLoaded?.window == nil { + removeWebViewController() + } + } +} + +// MARK: - Views Setup +extension AIChatViewController { + + private func addWebViewController() { + guard webViewController == nil else { return } + + let viewController = AIChatWebViewController(chatModel: chatModel) + viewController.delegate = self + webViewController = viewController + + addChild(viewController) + view.addSubview(viewController.view) + viewController.view.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + viewController.view.topAnchor.constraint(equalTo: view.topAnchor), + viewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor), + viewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), + viewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor) + ]) + + viewController.didMove(toParent: self) + } + + private func removeWebViewController() { + webViewController?.removeFromParent() + webViewController?.view.removeFromSuperview() + webViewController = nil + } +} + +extension AIChatViewController: AIChatWebViewControllerDelegate { + func aiChatWebViewController(_ viewController: AIChatWebViewController, didRequestToLoad url: URL) { + delegate?.aiChatViewController(self, didRequestToLoad: url) + } +} diff --git a/DuckDuckGo/OnboardingExperiment/DefaultVariantManager+Onboarding.swift b/LocalPackages/AIChat/Sources/AIChat/Public API/Logger+AIChat.swift similarity index 67% rename from DuckDuckGo/OnboardingExperiment/DefaultVariantManager+Onboarding.swift rename to LocalPackages/AIChat/Sources/AIChat/Public API/Logger+AIChat.swift index 528ff47fc8..d928d98e2e 100644 --- a/DuckDuckGo/OnboardingExperiment/DefaultVariantManager+Onboarding.swift +++ b/LocalPackages/AIChat/Sources/AIChat/Public API/Logger+AIChat.swift @@ -1,5 +1,5 @@ // -// DefaultVariantManager+Onboarding.swift +// Logger+AIChat.swift // DuckDuckGo // // Copyright © 2024 DuckDuckGo. All rights reserved. @@ -18,16 +18,8 @@ // import Foundation -import BrowserServicesKit - -extension VariantManager { - - var isOnboardingHighlightsExperiment: Bool { - isSupported(feature: .newOnboardingIntroHighlights) - } - - var isContextualDaxDialogsEnabled: Bool { - isSupported(feature: .contextualDaxDialogs) - } +import os.log +public extension Logger { + static let aiChat = { Logger(subsystem: "AI Chat", category: "") }() } diff --git a/LocalPackages/AIChat/Sources/AIChat/Resources/Assets.xcassets/Contents.json b/LocalPackages/AIChat/Sources/AIChat/Resources/Assets.xcassets/Contents.json new file mode 100644 index 0000000000..73c00596a7 --- /dev/null +++ b/LocalPackages/AIChat/Sources/AIChat/Resources/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/LocalPackages/AIChat/Sources/AIChat/Resources/Assets.xcassets/webViewBackgroundColor.colorset/Contents.json b/LocalPackages/AIChat/Sources/AIChat/Resources/Assets.xcassets/webViewBackgroundColor.colorset/Contents.json new file mode 100644 index 0000000000..ed36c62a6f --- /dev/null +++ b/LocalPackages/AIChat/Sources/AIChat/Resources/Assets.xcassets/webViewBackgroundColor.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "0.086", + "green" : "0.086", + "red" : "0.086" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/LocalPackages/AIChat/Sources/AIChat/UIColor+Extension.swift b/LocalPackages/AIChat/Sources/AIChat/UIColor+Extension.swift new file mode 100644 index 0000000000..f78e43ecca --- /dev/null +++ b/LocalPackages/AIChat/Sources/AIChat/UIColor+Extension.swift @@ -0,0 +1,26 @@ +// +// UIColor+Extension.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +extension UIColor { + static var webViewBackgroundColor: UIColor { + return UIColor(named: "webViewBackgroundColor", in: Bundle.module, compatibleWith: nil) ?? UIColor.clear + } +} diff --git a/LocalPackages/AIChat/Sources/AIChat/UserText.swift b/LocalPackages/AIChat/Sources/AIChat/UserText.swift new file mode 100644 index 0000000000..d33e745d2d --- /dev/null +++ b/LocalPackages/AIChat/Sources/AIChat/UserText.swift @@ -0,0 +1,24 @@ +// +// UserText.swift +// DuckDuckGo +// +// Copyright © 2024 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +public struct UserText { + public static let aiChatTitle = NSLocalizedString("aichat.title", value: "DuckDuckGo AI Chat", comment: "Title for DuckDuckGo AI Chat. Should not be translated") +} diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Resources/ro.lproj/Localizable.strings b/LocalPackages/SyncUI/Sources/SyncUI/Resources/ro.lproj/Localizable.strings index e2ca572ba5..010348bc1f 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Resources/ro.lproj/Localizable.strings +++ b/LocalPackages/SyncUI/Sources/SyncUI/Resources/ro.lproj/Localizable.strings @@ -230,7 +230,7 @@ "sync.and.backup.this.device.link" = "Sincronizează și fă backup pentru acest dispozitiv"; /* Button to get DuckDuckGo on other devices */ -"sync.get.other.devices" = "Obține DuckDuckGo pe alte dispozitive"; +"sync.get.other.devices" = "Descarcă DuckDuckGo pe alte dispozitive"; /* Button title to share link for downloading DuckDuckGo on other devices */ "sync.get.other.devices.card.button.title" = "Trimite linkul de descărcare"; diff --git a/PacketTunnelProvider/NetworkProtection/NetworkProtectionUNNotificationPresenter.swift b/PacketTunnelProvider/NetworkProtection/NetworkProtectionUNNotificationPresenter.swift index 28849d38d7..3af4a6038f 100644 --- a/PacketTunnelProvider/NetworkProtection/NetworkProtectionUNNotificationPresenter.swift +++ b/PacketTunnelProvider/NetworkProtection/NetworkProtectionUNNotificationPresenter.swift @@ -88,11 +88,6 @@ final class NetworkProtectionUNNotificationPresenter: NSObject, NetworkProtectio showNotification(.connection, content) } - func showConnectionNotification(serverLocation: String?) { - let content = notificationContent(body: UserText.networkProtectionConnectionSuccessNotificationBody) - showNotification(.connection, content) - } - func showReconnectingNotification() { let content = notificationContent(body: UserText.networkProtectionConnectionInterruptedNotificationBody) showNotification(.connection, content) diff --git a/PacketTunnelProvider/UserText.swift b/PacketTunnelProvider/UserText.swift index 48436d15bc..2a116bf7a0 100644 --- a/PacketTunnelProvider/UserText.swift +++ b/PacketTunnelProvider/UserText.swift @@ -23,15 +23,15 @@ final class UserText { // MARK: - Network Protection Notifications - static let networkProtectionNotificationsTitle = NSLocalizedString("network.protection.notification.title", value: "DuckDuckGo", comment: "The title of the notifications shown from Network Protection") + static let networkProtectionNotificationsTitle = NSLocalizedString("network.protection.notification.title", value: "DuckDuckGo", comment: "The title of the notifications shown from VPN") - static let networkProtectionConnectionSuccessNotificationBody = NSLocalizedString("network.protection.success.notification.body", value: "Network Protection is On. Your location and online activity are protected.", comment: "The body of the notification shown when Network Protection reconnects successfully") + static let networkProtectionConnectionSuccessNotificationBody = NSLocalizedString("network.protection.success.notification.body", value: "DuckDuckGo VPN is On. Your location and online activity are protected.", comment: "The body of the notification shown when VPN reconnects successfully") static func networkProtectionConnectionSuccessNotificationBody(serverLocation: String) -> String { let localized = NSLocalizedString( "network.protection.success.notification.subtitle.including.serverLocation", value: "Routing device traffic through %@.", - comment: "The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter" + comment: "The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter" ) return String(format: localized, serverLocation) } @@ -40,14 +40,14 @@ final class UserText { let localized = NSLocalizedString( "network.protection.success.notification.subtitle.snooze.ended.including.serverLocation", value: "VPN snooze has ended. Routing device traffic through %@.", - comment: "The body of the notification shown when Network Protection connects successfully after snooze with the city + state/country as formatted parameter" + comment: "The body of the notification shown when VPN connects successfully after snooze with the city + state/country as formatted parameter" ) return String(format: localized, serverLocation) } - static let networkProtectionConnectionInterruptedNotificationBody = NSLocalizedString("network.protection.interrupted.notification.body", value: "Network Protection was interrupted. Attempting to reconnect now...", comment: "The body of the notification shown when Network Protection's connection is interrupted") + static let networkProtectionConnectionInterruptedNotificationBody = NSLocalizedString("network.protection.interrupted.notification.body", value: "DuckDuckGo VPN was interrupted. Attempting to reconnect now...", comment: "The body of the notification shown when VPN connection is interrupted") - static let networkProtectionConnectionFailureNotificationBody = NSLocalizedString("network.protection.failure.notification.body", value: "Network Protection failed to connect. Please try again later.", comment: "The body of the notification shown when Network Protection fails to reconnect") + static let networkProtectionConnectionFailureNotificationBody = NSLocalizedString("network.protection.failure.notification.body", value: "DuckDuckGo VPN failed to connect. Please try again later.", comment: "The body of the notification shown when VPN fails to reconnect") static let networkProtectionEntitlementExpiredNotificationBody = NSLocalizedString("network.protection.entitlement.expired.notification.body", value: "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN.", comment: "The body of the notification when Privacy Pro subscription expired") diff --git a/PacketTunnelProvider/bg.lproj/Localizable.strings b/PacketTunnelProvider/bg.lproj/Localizable.strings index f7fa04f072..b4194a286c 100644 --- a/PacketTunnelProvider/bg.lproj/Localizable.strings +++ b/PacketTunnelProvider/bg.lproj/Localizable.strings @@ -1,18 +1,24 @@ /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN връзката е прекъсната поради изтекъл абонамент. Абонирайте се за Privacy Pro, за да свържете отново DuckDuckGo VPN."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Неуспешно свързване на мрежовата защита. Моля, опитайте отново по-късно."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "DuckDuckGo VPN не можа да се свърже. Моля, опитайте отново по-късно."; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Мрежовата защита е прекъсната. Извършва се опит за повторно свързване..."; +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "DuckDuckGo VPN беше прекъснат. Извършва се опит за повторно свързване..."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Мрежовата защита е включена. Вашето местоположение и онлайн активност са защитени."; +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN е отложен за %@"; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "DuckDuckGo VPN е включен. Вашето местоположение и онлайн активност са защитени."; + +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Трафикът на устройството се маршрутизира през %@."; +/* The body of the notification shown when VPN connects successfully after snooze with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "Отлагането на VPN приключи. Трафикът на устройството се маршрутизира през %@."; + diff --git a/PacketTunnelProvider/cs.lproj/Localizable.strings b/PacketTunnelProvider/cs.lproj/Localizable.strings index 379129d107..421f470f4d 100644 --- a/PacketTunnelProvider/cs.lproj/Localizable.strings +++ b/PacketTunnelProvider/cs.lproj/Localizable.strings @@ -1,18 +1,24 @@ /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN byla odpojena kvůli vypršení platnosti předplatného. Předplať si službu Privacy Pro, ať můžeš DuckDuckGo VPN dál používat."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Ochrana sítě se nepřipojila. Zkus to znovu později."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "VPN DuckDuckGo se nepodařilo připojit. Zkus to znovu později."; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Ochrana sítě je přerušená. Probíhá pokus o opětovné připojení..."; +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "VPN DuckDuckGo byla odpojená. Probíhá pokus o opětovné připojení..."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Ochrana sítě je zapnutá. Tvoje poloha a online aktivita jsou chráněné."; +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN je uspaná na %@"; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "VPN DuckDuckGo je zapnutá. Tvoje poloha a online aktivita jsou chráněné."; + +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Směrování provozu zařízení přes %@."; +/* The body of the notification shown when VPN connects successfully after snooze with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "VPN už není uspaná. Směrování provozu zařízení přes %@."; + diff --git a/PacketTunnelProvider/da.lproj/Localizable.strings b/PacketTunnelProvider/da.lproj/Localizable.strings index 77331ab558..527fdc337d 100644 --- a/PacketTunnelProvider/da.lproj/Localizable.strings +++ b/PacketTunnelProvider/da.lproj/Localizable.strings @@ -1,18 +1,24 @@ /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN er afbrudt på grund af et udløbet abonnement. Abonner på Privacy Pro for at genoprette forbindelsen til DuckDuckGo VPN."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Netværksbeskyttelse kunne ikke oprette forbindelse. Prøv igen senere."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "DuckDuckGo VPN kunne ikke oprette forbindelse. Prøv igen senere."; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Netværksbeskyttelse blev afbrudt. Forsøger at genoprette forbindelse ..."; +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "DuckDuckGo VPN blev afbrudt. Forsøger at genoprette forbindelse ..."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Netværksbeskyttelse er TIL. Din placering og onlineaktivitet er beskyttet."; +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN er sat på pause i %@"; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "DuckDuckGo VPN er tændt. Din placering og onlineaktivitet er beskyttet."; + +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Router enhedens trafik gennem %@."; +/* The body of the notification shown when VPN connects successfully after snooze with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "VPN-pausen er afsluttet. Enhedens trafik routes gennem %@."; + diff --git a/PacketTunnelProvider/de.lproj/Localizable.strings b/PacketTunnelProvider/de.lproj/Localizable.strings index 2488f25a86..08398dccd0 100644 --- a/PacketTunnelProvider/de.lproj/Localizable.strings +++ b/PacketTunnelProvider/de.lproj/Localizable.strings @@ -1,18 +1,24 @@ /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN aufgrund abgelaufenen Abonnements getrennt. Abonniere Privacy Pro, um die Verbindung zum DuckDuckGo-VPN wiederherzustellen."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Network Protection konnte keine Verbindung herstellen. Bitte versuche es zu einem späteren Zeitpunkt erneut."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "DuckDuckGo VPN konnte keine Verbindung herstellen. Bitte versuche es zu einem späteren Zeitpunkt erneut."; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Network Protection wurde unterbrochen. Versuche jetzt, die Verbindung wiederherzustellen ..."; +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "DuckDuckGo VPN wurde unterbrochen. Verbindung wird wiederhergestellt ..."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Network Protection ist aktiviert.Dein Standort und deine Online-Aktivitäten sind geschützt."; +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN ist für dich für %@ pausiert"; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "DuckDuckGo VPN ist aktiviert. Dein Standort und deine Online-Aktivitäten sind geschützt."; + +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Geräteverkehr wird über %@ geleitet."; +/* The body of the notification shown when VPN connects successfully after snooze with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "Die VPN-Pause ist vorbei. Geräteverkehr wird über %@ geleitet."; + diff --git a/PacketTunnelProvider/el.lproj/Localizable.strings b/PacketTunnelProvider/el.lproj/Localizable.strings index a4af94847b..0d7bb891d0 100644 --- a/PacketTunnelProvider/el.lproj/Localizable.strings +++ b/PacketTunnelProvider/el.lproj/Localizable.strings @@ -1,18 +1,24 @@ /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "Το VPN αποσυνδέθηκε λόγω λήξης της συνδρομής. Εγγραφείτε στο Privacy Pro για να συνδέσετε εκ νέου το DuckDuckGo VPN."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Η σύνδεση της Προστασίας δικτύου απέτυχε. Ξαναδοκιμάστε αργότερα."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "Η σύνδεση του DuckDuckGo VPN απέτυχε. Ξαναδοκιμάστε αργότερα."; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Η Προστασία δικτύου διακόπηκε. Γίνεται προσπάθεια επανασύνδεσης τώρα..."; +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "Το DuckDuckGo VPN διακόπηκε. Γίνεται προσπάθεια επανασύνδεσης τώρα..."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Η Προστασία δικτύου είναι Ενεργή. Η τοποθεσία και η διαδικτυακή δραστηριότητά σας προστατεύονται."; +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "Το VPN τέθηκε σε αναστολή για %@"; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "Το DuckDuckGo VPN είναι ενεργοποιημένο. Η τοποθεσία και η διαδικτυακή δραστηριότητά σας προστατεύονται."; + +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Δρομολόγηση κυκλοφορίας της συσκευής μέσω %@."; +/* The body of the notification shown when VPN connects successfully after snooze with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "Η αναστολή του VPN έχει τελειώσει. Δρομολόγηση κυκλοφορίας της συσκευής μέσω %@."; + diff --git a/PacketTunnelProvider/en.lproj/Localizable.strings b/PacketTunnelProvider/en.lproj/Localizable.strings index b665180896..154d78cde6 100644 --- a/PacketTunnelProvider/en.lproj/Localizable.strings +++ b/PacketTunnelProvider/en.lproj/Localizable.strings @@ -1,24 +1,24 @@ /* The body of the notification when Privacy Pro subscription expired */ "network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Network Protection failed to connect. Please try again later."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "DuckDuckGo VPN failed to connect. Please try again later."; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Network Protection was interrupted. Attempting to reconnect now..."; +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "DuckDuckGo VPN was interrupted. Attempting to reconnect now..."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; /* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ "network.protection.snoozed.notification.body" = "VPN snoozed for %@"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Network Protection is On. Your location and online activity are protected."; +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "DuckDuckGo VPN is On. Your location and online activity are protected."; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Routing device traffic through %@."; -/* The body of the notification shown when Network Protection connects successfully after snooze with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN connects successfully after snooze with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "VPN snooze has ended. Routing device traffic through %@."; diff --git a/PacketTunnelProvider/es.lproj/Localizable.strings b/PacketTunnelProvider/es.lproj/Localizable.strings index fe768a3a89..91ac1cde9c 100644 --- a/PacketTunnelProvider/es.lproj/Localizable.strings +++ b/PacketTunnelProvider/es.lproj/Localizable.strings @@ -1,18 +1,24 @@ /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN desconectada por suscripción caducada. Suscríbete a Privacy Pro para volver a conectar la VPN de DuckDuckGo."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "No se ha podido conectar la protección de red. Inténtalo de nuevo más tarde."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "No se ha podido conectar la VPN de DuckDuckGo. Inténtalo de nuevo más tarde."; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "La protección de red se ha interrumpido. Intentando volver a conectar ahora..."; +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "La VPN de DuckDuckGo se ha interrumpido. Intentando volver a conectar ahora..."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "La protección de red está activada. Tu ubicación y actividad en línea están protegidas."; +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "La VPN está en modo de suspenso durante %@"; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "La VPN de DuckDuckGo está activada. Tu ubicación y actividad en línea están protegidas."; + +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Redirigiendo el tráfico de dispositivos a través de %@."; +/* The body of the notification shown when VPN connects successfully after snooze with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "El modo en suspenso de la VPN ha terminado. Redirigiendo el tráfico de dispositivos a través de %@."; + diff --git a/PacketTunnelProvider/et.lproj/Localizable.strings b/PacketTunnelProvider/et.lproj/Localizable.strings index f6d08d8d41..3bb5425b7b 100644 --- a/PacketTunnelProvider/et.lproj/Localizable.strings +++ b/PacketTunnelProvider/et.lproj/Localizable.strings @@ -1,18 +1,24 @@ /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN-ühendus katkestati aegunud tellimuse tõttu. DuckDuckGo VPN-i taasühendamiseks telli Privacy Pro."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Network Protectioni ühenduse loomine nurjus. Proovi hiljem uuesti."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "DuckDuckGo VPN-i ühendamine ebaõnnestus. Proovige hiljem uuesti."; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Network Protection katkestati. Proovin uuesti ühendust luua ..."; +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "DuckDuckGo VPN katkestati. Proovin uuesti ühendust luua..."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Network Protection on sisse lülitatud. Sinu asukoht ja võrgutegevus on kaitstud."; +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN on peatatud %@ jooksul"; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "DuckDuckGo VPN on sisse lülitatud. Teie asukoht ja võrgutegevus on kaitstud."; + +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Seadme liiklus suunatakse läbi %@."; +/* The body of the notification shown when VPN connects successfully after snooze with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "VPN-i peatamine on lõppenud. Seadme liiklus suunatakse läbi %@."; + diff --git a/PacketTunnelProvider/fi.lproj/Localizable.strings b/PacketTunnelProvider/fi.lproj/Localizable.strings index 6fa273cf6d..b0ff0c55df 100644 --- a/PacketTunnelProvider/fi.lproj/Localizable.strings +++ b/PacketTunnelProvider/fi.lproj/Localizable.strings @@ -1,18 +1,24 @@ /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN katkaistu vanhentuneen tilauksen takia. Tilaa Privacy Pro, jotta voit yhdistää DuckDuckGo VPN:n uudelleen."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Network Protection ei onnistunut muodostamaan yhteyttä. Yritä myöhemmin uudelleen."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "DuckDuckGo VPN ei onnistunut muodostamaan yhteyttä. Yritä myöhemmin uudelleen."; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Network Protection keskeytettiin. Yritetään muodostaa yhteys uudelleen..."; +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "DuckDuckGo VPN keskeytyi. Yritetään muodostaa yhteys uudelleen..."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Network Protection on käytössä. Sijaintisi ja verkkotoimintasi on suojattu."; +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN on lepotilassa %@"; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "DuckDuckGo VPN on päällä. Sijaintisi ja verkkotoimintasi on suojattu."; + +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Laitteen liikenne reititetään %@ kautta."; +/* The body of the notification shown when VPN connects successfully after snooze with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "VPN-lepotila on päättynyt. Laitteen liikenne reititetään %@ kautta."; + diff --git a/PacketTunnelProvider/fr.lproj/Localizable.strings b/PacketTunnelProvider/fr.lproj/Localizable.strings index 10d0783738..8f4b99e091 100644 --- a/PacketTunnelProvider/fr.lproj/Localizable.strings +++ b/PacketTunnelProvider/fr.lproj/Localizable.strings @@ -1,18 +1,24 @@ /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN déconnecté en raison de l'expiration de l'abonnement. Abonnez-vous à Privacy Pro pour reconnecter le VPN DuckDuckGo."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Échec de la connexion à Network Protection. Veuillez réessayer plus tard."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "Le VPN DuckDuckGo n'a pas réussi à se connecter. Veuillez réessayer plus tard."; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Interruption de Network Protection. Tentative de reconnexion en cours…"; +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "Le VPN DuckDuckGo a été interrompu. Tentative de reconnexion en cours…"; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Network Protection est activé. Votre emplacement et votre activité en ligne sont protégés."; +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "Le VPN est en veille pendant %@"; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "Le VPN DuckDuckGo est activé. Votre emplacement et votre activité en ligne sont protégés."; + +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Routage du trafic de l'appareil via %@."; +/* The body of the notification shown when VPN connects successfully after snooze with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "La veille du VPN a pris fin. Routage du trafic de l'appareil via %@."; + diff --git a/PacketTunnelProvider/hr.lproj/Localizable.strings b/PacketTunnelProvider/hr.lproj/Localizable.strings index a9a6fc432e..dbfa65a212 100644 --- a/PacketTunnelProvider/hr.lproj/Localizable.strings +++ b/PacketTunnelProvider/hr.lproj/Localizable.strings @@ -1,18 +1,24 @@ /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN je prekinut zbog istekle pretplate. Pretplati se na Privacy Pro kako bi ponovno povezao DuckDuckGo VPN."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Mrežna zaštita nije se uspjela povezati. Pokušaj ponovno nešto kasnije."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "DuckDuckGo VPN nije se mogao povezati. Pokušaj ponovno nešto kasnije."; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Mrežna zaštita je prekinuta. Sada se pokušava ponovno povezati..."; +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "DuckDuckGo VPN je prekinut. Sada se pokušava ponovno povezati..."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Uključena je mrežna zaštita. Tvoja lokacija i online aktivnost su zaštićeni."; +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN je u stanju mirovanja %@"; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "DuckDuckGo VPN je uključen. Tvoja lokacija i aktivnosti na internetu zaštićeni su."; + +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Usmjeravanje prometa uređaja kroz %@."; +/* The body of the notification shown when VPN connects successfully after snooze with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "Mirovanje VPN-a je završilo. Usmjeravanje prometa uređaja kroz %@."; + diff --git a/PacketTunnelProvider/hu.lproj/Localizable.strings b/PacketTunnelProvider/hu.lproj/Localizable.strings index 7b8becbc60..ae90002b96 100644 --- a/PacketTunnelProvider/hu.lproj/Localizable.strings +++ b/PacketTunnelProvider/hu.lproj/Localizable.strings @@ -1,18 +1,24 @@ /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "A VPN-kapcsolat megszakadt, mert lejárt az előfizetésed. Fizess elő a Privacy Pro szolgáltatásra a DuckDuckGo VPN újracsatlakoztatásához."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "A hálózatvédelmi funkció csatlakozása sikertelen. Próbálkozz újra később."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "A DuckDuckGo VPN nem tudott csatlakozni. Próbálkozz újra később."; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "A hálózatvédelmi funkció kapcsolata megszakadt. Újracsatlakozási kísérlet folyamatban…"; +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "A DuckDuckGo VPN-kapcsolat megszakadt. Újracsatlakozási kísérlet folyamatban…"; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "A hálózatvédelem be van kapcsolva. A tartózkodási helyed és online tevékenységed védelme aktív."; +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN szüneteltetve ennyi időre: %@"; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "A DuckDuckGo VPN be van kapcsolva. A tartózkodási helyed és az online tevékenységed védelme aktív."; + +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Az eszköz forgalmának átirányítási helyszíne: %@."; +/* The body of the notification shown when VPN connects successfully after snooze with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "A VPN szüneteltetése véget ért. Az eszköz forgalmának átirányítása ezen keresztül: %@."; + diff --git a/PacketTunnelProvider/it.lproj/Localizable.strings b/PacketTunnelProvider/it.lproj/Localizable.strings index 9f423dc124..cc09e4ae7b 100644 --- a/PacketTunnelProvider/it.lproj/Localizable.strings +++ b/PacketTunnelProvider/it.lproj/Localizable.strings @@ -1,18 +1,24 @@ /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "La VPN si è scollegata perché l'abbonamento è scaduto. Iscriviti a Privacy Pro per riconnettere la VPN di DuckDuckGo."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Connessione di Network Protection non riuscita. Riprova più tardi."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "La VPN di DuckDuckGo non è riuscita a connettersi. Riprova in seguito."; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "La funzione Network Protection è stata interrotta. Tentativo di riconnessione in corso..."; +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "La VPN di DuckDuckGo è stata interrotta. Tentativo di riconnessione in corso..."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Network Protection è attiva. La tua posizione e le tue attività online sono protette."; +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "La VPN è sospesa per %@"; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "La VPN di DuckDuckGo è attiva. La tua posizione e le tue attività online sono protette."; + +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Instradamento del traffico del dispositivo tramite %@."; +/* The body of the notification shown when VPN connects successfully after snooze with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "La sospensione della VPN è finita. Instradamento del traffico del dispositivo tramite %@."; + diff --git a/PacketTunnelProvider/lt.lproj/Localizable.strings b/PacketTunnelProvider/lt.lproj/Localizable.strings index f2e9b94eb0..df0ec64a6b 100644 --- a/PacketTunnelProvider/lt.lproj/Localizable.strings +++ b/PacketTunnelProvider/lt.lproj/Localizable.strings @@ -1,18 +1,24 @@ /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN atjungtas dėl pasibaigusios prenumeratos. „DuckDuckGo“ VPN galima naudoti nemokamai, kol veikia beta versija."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Nepavyko prisijungti prie tinklo apsaugos. Pabandykite dar kartą vėliau."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "„DuckDuckGo“ VPN nepavyko prisijungti. Bandykite dar kartą vėliau."; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Tinklo apsauga buvo nutraukta. Dabar bandome prisijungti..."; +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "„DuckDuckGo“ VPN buvo pertrauktas. Dabar bandome prisijungti..."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "„DuckDuckGo“"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Tinklo apsauga įjungta. Jūsų vieta ir veikla internete yra apsaugota."; +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN pristabdytas %@ laikui"; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "„DuckDuckGo“ VPN įjungtas. Jūsų vieta ir veikla internete yra apsaugota."; + +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Nukreipiamas įrenginio srautas per %@."; +/* The body of the notification shown when VPN connects successfully after snooze with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "VPN pristabdymas baigėsi. Nukreipiamas įrenginio srautas per %@."; + diff --git a/PacketTunnelProvider/lv.lproj/Localizable.strings b/PacketTunnelProvider/lv.lproj/Localizable.strings index 5023945ee9..afd274958e 100644 --- a/PacketTunnelProvider/lv.lproj/Localizable.strings +++ b/PacketTunnelProvider/lv.lproj/Localizable.strings @@ -1,18 +1,24 @@ /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN atvienots, jo ir beidzies abonements. Abonē Privacy Pro, lai atkārtoti izveidotu savienojumu ar DuckDuckGo VPN."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Tīkla aizsardzībai neizdevās izveidot savienojumu. Lūdzu, mēģini vēlreiz vēlāk."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "Neizdevās izveidot savienojumu ar DuckDuckGo VPN. Lūdzu, mēģini vēlreiz vēlāk."; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Tīkla aizsardzība tika pārtraukta. Tagad tiek mēģināts atkārtoti izveidot savienojumu..."; +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "DuckDuckGo VPN savienojums ir pārtraukts. Mēģina atjaunot savienojumu..."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Tīkla aizsardzība ir ieslēgta. Tava atrašanās vieta un darbības tiešsaistē ir aizsargātas."; +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN ir atslēgts uz %@"; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "DuckDuckGo VPN ir ieslēgts. Tava atrašanās vieta un darbības tiešsaistē ir aizsargātas."; + +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Ierīces datplūsma tiek maršrutēta caur: %@."; +/* The body of the notification shown when VPN connects successfully after snooze with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "VPN snauda ir beigusies. Ierīces datplūsma tiek maršrutēta caur: %@."; + diff --git a/PacketTunnelProvider/nb.lproj/Localizable.strings b/PacketTunnelProvider/nb.lproj/Localizable.strings index d69b9546a0..9b71aa7160 100644 --- a/PacketTunnelProvider/nb.lproj/Localizable.strings +++ b/PacketTunnelProvider/nb.lproj/Localizable.strings @@ -1,18 +1,24 @@ /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN ble frakoblet på grunn av et utløpt abonnement. Abonner på Privacy Pro for å koble til DuckDuckGo-VPN igjen."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Nettverksbeskyttelse kunne ikke koble til. Prøv igjen senere."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "DuckDuckGo VPN mislyktes i å koble til. Prøv igjen senere."; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Nettverksbeskyttelse ble avbrutt. Prøver å koble til igjen nå..."; +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "DuckDuckGo VPN ble avbrutt. Prøver å koble til igjen nå..."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Nettverksbeskyttelse er på. Plasseringen og nettaktiviteten din er beskyttet."; +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN satt på slumring i %@"; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "DuckDuckGo VPN er på. Plasseringen og nettaktiviteten din er beskyttet."; + +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Omdirigerer enhetstrafikk gjennom %@."; +/* The body of the notification shown when VPN connects successfully after snooze with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "VPN-slumringen er avsluttet. Ruter enhetstrafikk gjennom %@."; + diff --git a/PacketTunnelProvider/nl.lproj/Localizable.strings b/PacketTunnelProvider/nl.lproj/Localizable.strings index 1b6ea4629c..e16ade9000 100644 --- a/PacketTunnelProvider/nl.lproj/Localizable.strings +++ b/PacketTunnelProvider/nl.lproj/Localizable.strings @@ -1,18 +1,24 @@ /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "De VPN-verbinding is verbroken omdat het abonnement is verlopen. Abonneer je op Privacy Pro om opnieuw verbinding te maken met DuckDuckGo VPN."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "De netwerkbeveiliging kan geen verbinding maken. Probeer het later opnieuw."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "DuckDuckGo VPN kon geen verbinding maken. Probeer het later opnieuw."; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "De netwerkbeveiliging is onderbroken. Er wordt geprobeerd opnieuw verbinding te maken ..."; +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "DuckDuckGo VPN werd onderbroken. Er wordt geprobeerd opnieuw verbinding te maken ..."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "De netwerkbeveiliging is ingeschakeld. Je locatie en online-activiteiten zijn beschermd."; +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN in sluimerstand voor %@"; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "DuckDuckGo VPN is ingeschakeld. Je locatie en online-activiteiten zijn beschermd."; + +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Apparaatverkeer routeren via %@."; +/* The body of the notification shown when VPN connects successfully after snooze with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "De VPN-sluimerstand is beëindigd. Apparaatverkeer wordt gerouteerd via %@."; + diff --git a/PacketTunnelProvider/pl.lproj/Localizable.strings b/PacketTunnelProvider/pl.lproj/Localizable.strings index 54f90fc27c..7c42b1409a 100644 --- a/PacketTunnelProvider/pl.lproj/Localizable.strings +++ b/PacketTunnelProvider/pl.lproj/Localizable.strings @@ -1,18 +1,24 @@ /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "Sieć VPN została rozłączona z powodu wygasłej subskrypcji. Subskrybuj Privacy Pro, aby ponownie połączyć się z siecią DuckDuckGo VPN."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Niepowodzenie połączenia usługi ochrony sieci.Spróbuj ponownie później."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "Nie udało się nawiązać połączenia DuckDuckGo VPN. Spróbuj ponownie później."; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Usługa ochrony sieci została przerwana. Próba ponownego połączenia..."; +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "Usługa DuckDuckGo VPN została przerwana. Próba ponownego połączenia..."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Usługa ochrony sieci jest włączona. Twoja lokalizacja i aktywność w Internecie są chronione."; +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "Wstrzymano VPN na %@"; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "Usługa DuckDuckGo VPN jest włączona. Twoja lokalizacja i aktywność w Internecie są chronione."; + +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Przekierowanie ruchu urządzenia przez %@."; +/* The body of the notification shown when VPN connects successfully after snooze with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "Wstrzymanie VPN zostało zakończone. Przekierowanie ruchu urządzenia przez %@."; + diff --git a/PacketTunnelProvider/pt.lproj/Localizable.strings b/PacketTunnelProvider/pt.lproj/Localizable.strings index b6791c7790..1461824952 100644 --- a/PacketTunnelProvider/pt.lproj/Localizable.strings +++ b/PacketTunnelProvider/pt.lproj/Localizable.strings @@ -1,18 +1,24 @@ /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN desativada devido a subscrição expirada. Subscreve o Privacy Pro para voltar a ligar a DuckDuckGo VPN."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Falha na ligação da Network Protection. Tenta novamente mais tarde."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "A VPN DuckDuckGo não conseguiu estabelecer ligação. Tenta novamente mais tarde."; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "A Network Protection foi interrompida. A tentar ligar novamente..."; +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "A VPN DuckDuckGo foi interrompida. A tentar ligar novamente…"; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "A Network Protection está ativada. A tua localização e atividade online estão protegidas."; +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN suspensa por %@"; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "A VPN DuckDuckGo está ativada. A tua localização e atividade online estão protegidas."; + +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "A encaminhar o tráfego do dispositivo através de %@."; +/* The body of the notification shown when VPN connects successfully after snooze with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "A suspensão da VPN terminou. A encaminhar o tráfego do dispositivo por %@."; + diff --git a/PacketTunnelProvider/ro.lproj/Localizable.strings b/PacketTunnelProvider/ro.lproj/Localizable.strings index ef6af74673..6f1601ea02 100644 --- a/PacketTunnelProvider/ro.lproj/Localizable.strings +++ b/PacketTunnelProvider/ro.lproj/Localizable.strings @@ -1,18 +1,24 @@ /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN deconectat din cauza expirării abonamentului. Abonează-te la Privacy Pro pentru a reconecta DuckDuckGo VPN."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Network Protection nu a reușit să se conecteze. Încearcă din nou mai târziu."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "DuckDuckGo VPN nu a reușit să se conecteze. Încearcă din nou mai târziu."; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Network Protection a fost întreruptă. Se încearcă reconectarea acum..."; +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "DuckDuckGo VPN a fost întrerupt. Se încearcă reconectarea acum..."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Network Protection este activată. Locația și activitatea ta online sunt protejate."; +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN-ul este amânat pentru %@"; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "DuckDuckGo VPN este activat. Locația și activitatea ta online sunt protejate."; + +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Dirijarea traficului dispozitivului prin %@."; +/* The body of the notification shown when VPN connects successfully after snooze with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "Amânarea VPN a luat sfârșit. Se dirijează traficul dispozitivului prin %@."; + diff --git a/PacketTunnelProvider/ru.lproj/Localizable.strings b/PacketTunnelProvider/ru.lproj/Localizable.strings index e7f7dfb788..19d3bdbc3d 100644 --- a/PacketTunnelProvider/ru.lproj/Localizable.strings +++ b/PacketTunnelProvider/ru.lproj/Localizable.strings @@ -1,18 +1,24 @@ /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN отключен ввиду окончания подписки. Чтобы вновь подключиться к DuckDuckGo VPN, оформите подписку Privacy Pro."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Функции защиты сети (Network Protection) не удалось установить соединение. Повторите попытку позже."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "Подключиться к DuckDuckGo VPN не удалось. Повторите попытку позже."; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Прервана работа функции защиты сети (Network Protection). Выполняется попытка повторного подключения..."; +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "Работа DuckDuckGo VPN была прервана. Выполняется попытка повторного подключения..."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Защита сети (Network Protection) включена. Ваши геопозиция и онлайн-активность скрыты от посторонних глаз."; +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "Работа VPN отложена на %@"; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "DuckDuckGo VPN включен. Ваши геопозиция и онлайн-активность скрыты от посторонних глаз."; + +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Трафик устройства направляется через: %@"; +/* The body of the notification shown when VPN connects successfully after snooze with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "VPN вышел из отложенного режима. Трафик устройства направляется через: %@."; + diff --git a/PacketTunnelProvider/sk.lproj/Localizable.strings b/PacketTunnelProvider/sk.lproj/Localizable.strings index 6b75ee4f28..9d8522b1d9 100644 --- a/PacketTunnelProvider/sk.lproj/Localizable.strings +++ b/PacketTunnelProvider/sk.lproj/Localizable.strings @@ -1,18 +1,24 @@ /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN bola odpojená kvôli vypršaniu platnosti predplatného. Predplať si Privacy Pro, aby si znova pripojil/a DuckDuckGo VPN."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Ochrana siete sa nepripojila. Prosím, skúste to neskôr znova."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "DuckDuckGo VPN sa nepodarilo pripojiť. Skús to neskôr znova, prosím."; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Ochrana siete bola prerušená. Prebieha pokus o opätovné pripojenie..."; +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "DuckDuckGo VPN bola prerušená. Prebieha pokus o opätovné pripojenie..."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Ochrana siete je zapnutá. Vaša poloha a online aktivita sú chránené."; +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN je pozastavená na %@"; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "DuckDuckGo VPN je zapnutá. Tvoja poloha a online aktivita sú chránené."; + +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Smerovanie komunikácie zariadenia cez %@."; +/* The body of the notification shown when VPN connects successfully after snooze with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "Odloženie VPN sa skončilo. Smerovanie prevádzky zariadenia cez %@."; + diff --git a/PacketTunnelProvider/sl.lproj/Localizable.strings b/PacketTunnelProvider/sl.lproj/Localizable.strings index b08a6a094b..e164cac1b8 100644 --- a/PacketTunnelProvider/sl.lproj/Localizable.strings +++ b/PacketTunnelProvider/sl.lproj/Localizable.strings @@ -1,18 +1,24 @@ /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN je prekinjen zaradi potekle naročnine. Naročite se na Privacy Pro, da znova povežete DuckDuckGo VPN."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Zaščita omrežja se ni uspela povezati. Poskusite znova pozneje."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "DuckDuckGo VPN se ni mogel povezati. Poskusite znova pozneje."; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Zaščita omrežja je bila prekinjena. Poskušam se znova povezati zdaj ..."; +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "DuckDuckGo VPN je bil prekinjen. Poskušam se znova povezati zdaj ..."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Zaščita omrežja je vklopljena. Vaša lokacija in spletna dejavnost sta zaščiteni."; +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN je preložen za %@"; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "Omrežje VPN DuckDuckGo je vklopljeno. Vaša lokacija in spletna dejavnost sta zaščiteni."; + +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Usmerjanje prometa naprave prek kraja %@."; +/* The body of the notification shown when VPN connects successfully after snooze with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "Dremež za VPN je končan. Usmerjanje prometa naprave prek kraja %@."; + diff --git a/PacketTunnelProvider/sv.lproj/Localizable.strings b/PacketTunnelProvider/sv.lproj/Localizable.strings index a26d457092..c71eac0124 100644 --- a/PacketTunnelProvider/sv.lproj/Localizable.strings +++ b/PacketTunnelProvider/sv.lproj/Localizable.strings @@ -1,18 +1,24 @@ /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "VPN har kopplats från på grund av att abonnemanget har gått ut. Prenumerera på Privacy Pro för att återansluta DuckDuckGo VPN."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Nätverksskyddet kunde inte ansluta. Försök igen senare."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "DuckDuckGo VPN kunde inte ansluta. Försök igen senare."; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Nätverksskyddet avbröts. Försöker återuppta kontakten nu..."; +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "DuckDuckGo VPN avbröts. Försöker återuppta kontakten nu ..."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Nätverksskydd är På. Din plats och onlineaktivitet är skyddad."; +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN har pausats i %@"; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "DuckDuckGo VPN är på. Din plats och onlineaktivitet är skyddad."; + +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Dirigera enhetstrafik genom %@."; +/* The body of the notification shown when VPN connects successfully after snooze with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "VPN-pausen har avslutats. Dirigerar enhetstrafik via %@."; + diff --git a/PacketTunnelProvider/tr.lproj/Localizable.strings b/PacketTunnelProvider/tr.lproj/Localizable.strings index 0a383313a5..73225a865b 100644 --- a/PacketTunnelProvider/tr.lproj/Localizable.strings +++ b/PacketTunnelProvider/tr.lproj/Localizable.strings @@ -1,18 +1,24 @@ /* The body of the notification when Privacy Pro subscription expired */ -"network.protection.entitlement.expired.notification.body" = "VPN disconnected due to expired subscription. Subscribe to Privacy Pro to reconnect DuckDuckGo VPN."; +"network.protection.entitlement.expired.notification.body" = "Abonelik süresi dolduğu için VPN bağlantısı kesildi. DuckDuckGo VPN'e yeniden bağlanmak için Privacy Pro'ya abone olun."; -/* The body of the notification shown when Network Protection fails to reconnect */ -"network.protection.failure.notification.body" = "Ağ Koruması bağlanamadı. Lütfen daha sonra tekrar deneyin."; +/* The body of the notification shown when VPN fails to reconnect */ +"network.protection.failure.notification.body" = "DuckDuckGo VPN bağlanamadı. Lütfen daha sonra tekrar deneyin."; -/* The body of the notification shown when Network Protection's connection is interrupted */ -"network.protection.interrupted.notification.body" = "Ağ Koruması kesintiye uğradı. Şimdi yeniden bağlanmaya çalışılıyor..."; +/* The body of the notification shown when VPN connection is interrupted */ +"network.protection.interrupted.notification.body" = "DuckDuckGo VPN kesintiye uğradı. Şimdi yeniden bağlanmaya çalışılıyor..."; -/* The title of the notifications shown from Network Protection */ +/* The title of the notifications shown from VPN */ "network.protection.notification.title" = "DuckDuckGo"; -/* The body of the notification shown when Network Protection reconnects successfully */ -"network.protection.success.notification.body" = "Ağ Koruması Açık. Konumunuz ve çevrim içi etkinliğiniz korunuyor."; +/* The body of the notification when the VPN is snoozed, with a duration string as parameter (e.g, 30 minutes) */ +"network.protection.snoozed.notification.body" = "VPN %@ için uyku moduna alındı"; -/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +/* The body of the notification shown when VPN reconnects successfully */ +"network.protection.success.notification.body" = "DuckDuckGo VPN Açık. Konumunuz ve çevrimiçi etkinliğiniz korunuyor."; + +/* The body of the notification shown when VPN connects successfully with the city + state/country as formatted parameter */ "network.protection.success.notification.subtitle.including.serverLocation" = "Cihaz trafiği %@ üzerinden yönlendiriliyor."; +/* The body of the notification shown when VPN connects successfully after snooze with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.snooze.ended.including.serverLocation" = "VPN uyku modu sona erdi. Cihaz trafiği %@ üzerinden yönlendiriliyor."; + diff --git a/Widgets/VPNWidget.swift b/Widgets/VPNWidget.swift index 3f6309ff88..9ad909410f 100644 --- a/Widgets/VPNWidget.swift +++ b/Widgets/VPNWidget.swift @@ -110,9 +110,12 @@ extension NEVPNStatus { @available(iOSApplicationExtension 17.0, *) struct VPNStatusView: View { + @Environment(\.widgetFamily) var family: WidgetFamily + @Environment(\.widgetRenderingMode) var widgetRenderingMode @Environment(\.colorScheme) private var colorScheme @Environment(\.openURL) private var openURL + var entry: VPNStatusTimelineProvider.Entry private let dateFormatter: DateFormatter = { @@ -143,7 +146,10 @@ struct VPNStatusView: View { private func connectionView(with status: NEVPNStatus) -> some View { HStack { VStack(alignment: .leading, spacing: 0) { - Image(headerImageName(with: status)).padding([.bottom], 7) + Image(headerImageName(with: status)) + .useFullColorRendering() + .padding([.bottom], 7) + .accessibilityHidden(true) Text(title(with: status)) .font(.system(size: 16, weight: .semibold)) @@ -168,11 +174,12 @@ struct VPNStatusView: View { let intent: any AppIntent = snoozeTimingStore.isSnoozing ? CancelSnoozeVPNIntent() : DisableVPNIntent() Button(buttonTitle, intent: intent) + .borderedStyle(widgetRenderingMode == .fullColor) + .makeAccentable(status == .connected) .font(.system(size: 14, weight: .semibold)) .foregroundStyle(snoozeTimingStore.isSnoozing ? connectButtonForegroundColor(isDisabled: false) : disconnectButtonForegroundColor(isDisabled: status != .connected)) - .buttonStyle(.borderedProminent) .buttonBorderShape(.roundedRectangle(radius: 8)) .tint(snoozeTimingStore.isSnoozing ? Color(designSystemColor: .accent) : @@ -184,9 +191,10 @@ struct VPNStatusView: View { .padding(.bottom, 16) case .connecting, .reasserting: Button(UserText.vpnWidgetDisconnectButton, intent: DisableVPNIntent()) + .borderedStyle(widgetRenderingMode == .fullColor) + .makeAccentable(status == .connected) .font(.system(size: 14, weight: .semibold)) .foregroundStyle(disconnectButtonForegroundColor(isDisabled: status != .connected)) - .buttonStyle(.borderedProminent) .buttonBorderShape(.roundedRectangle(radius: 8)) .tint(disconnectButtonBackgroundColor(isDisabled: status != .connected)) .disabled(status != .connected) @@ -195,9 +203,10 @@ struct VPNStatusView: View { .padding(.bottom, 16) case .disconnected, .disconnecting: connectButton + .borderedStyle(widgetRenderingMode == .fullColor) + .makeAccentable(status == .disconnected) .font(.system(size: 14, weight: .semibold)) .foregroundStyle(connectButtonForegroundColor(isDisabled: status != .disconnected)) - .buttonStyle(.borderedProminent) .buttonBorderShape(.roundedRectangle(radius: 8)) .tint(Color(designSystemColor: .accent)) .disabled(status != .disconnected) @@ -351,3 +360,16 @@ struct VPNStatusView_Previews: PreviewProvider { } } } + +extension Button { + + @ViewBuilder + func borderedStyle(_ isBordered: Bool) -> some View { + if isBordered { + self.buttonStyle(.borderedProminent) + } else { + self.buttonStyle(.automatic) + } + } + +} diff --git a/Widgets/WidgetViews.swift b/Widgets/WidgetViews.swift index 2215b1aead..a99451f7ab 100644 --- a/Widgets/WidgetViews.swift +++ b/Widgets/WidgetViews.swift @@ -43,15 +43,24 @@ struct FavoriteView: View { .shadow(color: Color.black.opacity(0.08), radius: 8, x: 0, y: 2) if let image = favorite.favicon { - - Image(uiImage: image) - .scaleDown(image.size.width > 60) - .cornerRadius(10) + + if image.size.width > 60 { + Image(uiImage: image) + .resizable() + .useFullColorRendering() + .aspectRatio(contentMode: .fit) + .cornerRadius(10) + } else { + Image(uiImage: image) + .useFullColorRendering() + .cornerRadius(10) + } } else if favorite.isDuckDuckGo { Image(.duckDuckGoColor24) .resizable() + .useFullColorRendering() .frame(width: 45, height: 45, alignment: .center) } else { @@ -83,13 +92,14 @@ struct LargeSearchFieldView: View { ZStack { RoundedRectangle(cornerSize: CGSize(width: 8, height: 8)) - .fill(Color.widgetSearchFieldBackground) + .fill(Color(designSystemColor: .container)) .frame(minHeight: 46, maxHeight: 46) .padding(.vertical, 16) HStack { Image(.duckDuckGoColor24) + .useFullColorRendering() .frame(width: 24, height: 24, alignment: .leading) Text(UserText.searchDuckDuckGo) @@ -99,6 +109,7 @@ struct LargeSearchFieldView: View { Spacer() Image(.findSearch20) + .useFullColorRendering() .foregroundColor(Color(designSystemColor: .textPrimary).opacity(0.5)) }.padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)) @@ -167,7 +178,6 @@ struct FavoritesWidgetView: View { var body: some View { ZStack { - Rectangle().fill(Color(designSystemColor: .backgroundSheets)) VStack(alignment: .center, spacing: 0) { @@ -215,14 +225,11 @@ struct SearchWidgetView: View { var body: some View { ZStack { - Rectangle() - .fill(Color(designSystemColor: .backgroundSheets)) - .accessibilityLabel(Text(UserText.searchDuckDuckGo)) - VStack(alignment: .center, spacing: 15) { Image(.logo) .resizable() + .useFullColorRendering() .frame(width: 46, height: 46, alignment: .center) .isHidden(false) .accessibilityHidden(true) @@ -230,10 +237,11 @@ struct SearchWidgetView: View { ZStack(alignment: Alignment(horizontal: .trailing, vertical: .center)) { RoundedRectangle(cornerSize: CGSize(width: 8, height: 8)) - .fill(Color.widgetSearchFieldBackground) + .fill(Color(designSystemColor: .container)) .frame(width: 126, height: 46) Image(.findSearch20) + .useFullColorRendering() .frame(width: 20, height: 20) .padding(.leading) .padding(.trailing, 13) @@ -241,7 +249,8 @@ struct SearchWidgetView: View { .accessibilityHidden(true) .foregroundColor(Color(designSystemColor: .textPrimary).opacity(0.5)) } - }.accessibilityHidden(true) + .accessibilityHidden(true) + }.accessibilityLabel(Text(UserText.searchDuckDuckGo)) } .widgetContainerBackground(color: Color(designSystemColor: .backgroundSheets)) } @@ -252,13 +261,10 @@ struct PasswordsWidgetView: View { var body: some View { ZStack { - Rectangle() - .fill(Color(designSystemColor: .backgroundSheets)) - .accessibilityLabel(Text(UserText.passwords)) - VStack(alignment: .center, spacing: 6) { Image(.widgetPasswordIllustration) + .useFullColorRendering() .frame(width: 96, height: 72) .isHidden(false) .accessibilityHidden(true) @@ -270,7 +276,7 @@ struct PasswordsWidgetView: View { .padding(.horizontal, 8) } - .accessibilityHidden(true) + .accessibilityLabel(Text(UserText.passwords)) } .widgetContainerBackground(color: Color(designSystemColor: .backgroundSheets)) } @@ -285,7 +291,15 @@ extension View { color } } else { - self + background(color) + } + } + + func makeAccentable(_ isAccentable: Bool = true) -> some View { + if #available(iOSApplicationExtension 16.0, *) { + return self.widgetAccentable(isAccentable) + } else { + return self } } @@ -325,9 +339,10 @@ extension View { extension Image { - @ViewBuilder func scaleDown(_ shouldScale: Bool) -> some View { - if shouldScale { - self.resizable().aspectRatio(contentMode: .fit) + /// Marks images as exempt from tint color overrides, such as favicons which should not have their color modified even when a tint color is set. + @ViewBuilder func useFullColorRendering() -> some View { + if #available(iOSApplicationExtension 18.0, *) { + self.widgetAccentedRenderingMode(.fullColor) } else { self } diff --git a/Widgets/bg.lproj/Localizable.strings b/Widgets/bg.lproj/Localizable.strings index 9d9a8519cf..fc45defbe6 100644 --- a/Widgets/bg.lproj/Localizable.strings +++ b/Widgets/bg.lproj/Localizable.strings @@ -1,9 +1,3 @@ -/* No comment provided by engineer. */ -"iOS 17 required" = "iOS 17 required"; - -/* No comment provided by engineer. */ -"Label" = "Етикет"; - /* Description shown to the user when adding the Email Protection lock screen widget */ "lock.screen.widget.email.description" = "Незабавно генериране на нов личен Duck Address."; @@ -82,12 +76,30 @@ /* VPN disconnect button text */ "widget.vpn.button.disconnect" = "Прекъсване на връзката"; +/* Label for the snooze end date, e.g. 'Until 9:51 AM' */ +"widget.vpn.label.snoozing-until" = "До %@"; + +/* VPN Live Activity dismiss button text */ +"widget.vpn.live-activity.button.dismiss" = "Отказване"; + +/* VPN Live Activity wake up button text */ +"widget.vpn.live-activity.button.wake-up" = "Събуждане"; + +/* VPN Live Activity active label text */ +"widget.vpn.live-activity.label.active" = "VPN е включена"; + +/* VPN Live Activity snoozing label text */ +"widget.vpn.live-activity.label.snoozing" = "VPN е отложен"; + /* Message describing VPN connected status */ "widget.vpn.status.connected" = "VPN е включена"; /* Message describing VPN disconnected status */ "widget.vpn.status.disconnected" = "VPN е изключена"; +/* Message describing VPN snoozing status */ +"widget.vpn.status.snoozed" = "VPN е в режим на изчакване"; + /* Subtitle describing VPN disconnected status */ "widget.vpn.subtitle.disconnected" = "Няма връзка"; diff --git a/Widgets/cs.lproj/Localizable.strings b/Widgets/cs.lproj/Localizable.strings index 74b8bd02b5..e186c91722 100644 --- a/Widgets/cs.lproj/Localizable.strings +++ b/Widgets/cs.lproj/Localizable.strings @@ -1,9 +1,3 @@ -/* No comment provided by engineer. */ -"iOS 17 required" = "iOS 17 required"; - -/* No comment provided by engineer. */ -"Label" = "Štítek"; - /* Description shown to the user when adding the Email Protection lock screen widget */ "lock.screen.widget.email.description" = "Okamžitě vygeneruje novou soukromou adresu Duck Address."; @@ -82,12 +76,30 @@ /* VPN disconnect button text */ "widget.vpn.button.disconnect" = "Odpojit"; +/* Label for the snooze end date, e.g. 'Until 9:51 AM' */ +"widget.vpn.label.snoozing-until" = "Až do %@"; + +/* VPN Live Activity dismiss button text */ +"widget.vpn.live-activity.button.dismiss" = "Odmítnout"; + +/* VPN Live Activity wake up button text */ +"widget.vpn.live-activity.button.wake-up" = "Probudit"; + +/* VPN Live Activity active label text */ +"widget.vpn.live-activity.label.active" = "VPN je zapnutá"; + +/* VPN Live Activity snoozing label text */ +"widget.vpn.live-activity.label.snoozing" = "VPN je uspaná"; + /* Message describing VPN connected status */ "widget.vpn.status.connected" = "VPN je zapnutá"; /* Message describing VPN disconnected status */ "widget.vpn.status.disconnected" = "VPN je vypnutá"; +/* Message describing VPN snoozing status */ +"widget.vpn.status.snoozed" = "VPN je pozastavená"; + /* Subtitle describing VPN disconnected status */ "widget.vpn.subtitle.disconnected" = "Nepřipojeno"; diff --git a/Widgets/da.lproj/Localizable.strings b/Widgets/da.lproj/Localizable.strings index 629616ba8a..0c48bef611 100644 --- a/Widgets/da.lproj/Localizable.strings +++ b/Widgets/da.lproj/Localizable.strings @@ -1,9 +1,3 @@ -/* No comment provided by engineer. */ -"iOS 17 required" = "iOS 17 påkrævet"; - -/* No comment provided by engineer. */ -"Label" = "Etiket"; - /* Description shown to the user when adding the Email Protection lock screen widget */ "lock.screen.widget.email.description" = "Generer øjeblikkeligt en ny privat Duck Address."; @@ -82,12 +76,30 @@ /* VPN disconnect button text */ "widget.vpn.button.disconnect" = "Afbryd forbindelse"; +/* Label for the snooze end date, e.g. 'Until 9:51 AM' */ +"widget.vpn.label.snoozing-until" = "Indtil %@"; + +/* VPN Live Activity dismiss button text */ +"widget.vpn.live-activity.button.dismiss" = "Afvis"; + +/* VPN Live Activity wake up button text */ +"widget.vpn.live-activity.button.wake-up" = "Afbryd pause"; + +/* VPN Live Activity active label text */ +"widget.vpn.live-activity.label.active" = "VPN er slået til"; + +/* VPN Live Activity snoozing label text */ +"widget.vpn.live-activity.label.snoozing" = "VPN er sat på pause"; + /* Message describing VPN connected status */ "widget.vpn.status.connected" = "VPN er slået til"; /* Message describing VPN disconnected status */ "widget.vpn.status.disconnected" = "VPN er slået fra"; +/* Message describing VPN snoozing status */ +"widget.vpn.status.snoozed" = "VPN er sat på pause"; + /* Subtitle describing VPN disconnected status */ "widget.vpn.subtitle.disconnected" = "Ikke forbundet"; diff --git a/Widgets/de.lproj/Localizable.strings b/Widgets/de.lproj/Localizable.strings index 33de6c3a82..cf28d34267 100644 --- a/Widgets/de.lproj/Localizable.strings +++ b/Widgets/de.lproj/Localizable.strings @@ -1,9 +1,3 @@ -/* No comment provided by engineer. */ -"iOS 17 required" = "iOS 17 required"; - -/* No comment provided by engineer. */ -"Label" = "Label"; - /* Description shown to the user when adding the Email Protection lock screen widget */ "lock.screen.widget.email.description" = "Erstelle sofort eine neue private Duck Address."; @@ -82,12 +76,30 @@ /* VPN disconnect button text */ "widget.vpn.button.disconnect" = "Trennen"; +/* Label for the snooze end date, e.g. 'Until 9:51 AM' */ +"widget.vpn.label.snoozing-until" = "Bis %@"; + +/* VPN Live Activity dismiss button text */ +"widget.vpn.live-activity.button.dismiss" = "Verwerfen"; + +/* VPN Live Activity wake up button text */ +"widget.vpn.live-activity.button.wake-up" = "Aufwachen"; + +/* VPN Live Activity active label text */ +"widget.vpn.live-activity.label.active" = "VPN ist An"; + +/* VPN Live Activity snoozing label text */ +"widget.vpn.live-activity.label.snoozing" = "VPN-Pause"; + /* Message describing VPN connected status */ "widget.vpn.status.connected" = "VPN ist An"; /* Message describing VPN disconnected status */ "widget.vpn.status.disconnected" = "VPN ist Aus"; +/* Message describing VPN snoozing status */ +"widget.vpn.status.snoozed" = "VPN ist pausiert"; + /* Subtitle describing VPN disconnected status */ "widget.vpn.subtitle.disconnected" = "Nicht verbunden"; diff --git a/Widgets/el.lproj/Localizable.strings b/Widgets/el.lproj/Localizable.strings index 4d82898813..0f6877f81d 100644 --- a/Widgets/el.lproj/Localizable.strings +++ b/Widgets/el.lproj/Localizable.strings @@ -1,9 +1,3 @@ -/* No comment provided by engineer. */ -"iOS 17 required" = "iOS 17 required"; - -/* No comment provided by engineer. */ -"Label" = "Label"; - /* Description shown to the user when adding the Email Protection lock screen widget */ "lock.screen.widget.email.description" = "Δημιουργήστε αμέσως μια νέα ιδιωτική διεύθυνση Duck Address."; @@ -82,12 +76,30 @@ /* VPN disconnect button text */ "widget.vpn.button.disconnect" = "Αποσύνδεση"; +/* Label for the snooze end date, e.g. 'Until 9:51 AM' */ +"widget.vpn.label.snoozing-until" = "Μέχρι %@"; + +/* VPN Live Activity dismiss button text */ +"widget.vpn.live-activity.button.dismiss" = "Απόρριψη"; + +/* VPN Live Activity wake up button text */ +"widget.vpn.live-activity.button.wake-up" = "Αφύπνιση"; + +/* VPN Live Activity active label text */ +"widget.vpn.live-activity.label.active" = "Το VPN είναι ενεργοποιημένο"; + +/* VPN Live Activity snoozing label text */ +"widget.vpn.live-activity.label.snoozing" = "Αναστολή VPN"; + /* Message describing VPN connected status */ "widget.vpn.status.connected" = "Το VPN είναι ενεργοποιημένο"; /* Message describing VPN disconnected status */ "widget.vpn.status.disconnected" = "Το VPN είναι απενεργοποιημένο"; +/* Message describing VPN snoozing status */ +"widget.vpn.status.snoozed" = "Το VPN είναι σε αναστολή"; + /* Subtitle describing VPN disconnected status */ "widget.vpn.subtitle.disconnected" = "Δεν έχει συνδεθεί"; diff --git a/Widgets/es.lproj/Localizable.strings b/Widgets/es.lproj/Localizable.strings index 305d0007c0..5ed35ef0e9 100644 --- a/Widgets/es.lproj/Localizable.strings +++ b/Widgets/es.lproj/Localizable.strings @@ -1,9 +1,3 @@ -/* No comment provided by engineer. */ -"iOS 17 required" = "Se necesita iOS 17"; - -/* No comment provided by engineer. */ -"Label" = "Etiqueta"; - /* Description shown to the user when adding the Email Protection lock screen widget */ "lock.screen.widget.email.description" = "Genera al instante una nueva Duck Address privada."; @@ -82,12 +76,30 @@ /* VPN disconnect button text */ "widget.vpn.button.disconnect" = "Desconectar"; +/* Label for the snooze end date, e.g. 'Until 9:51 AM' */ +"widget.vpn.label.snoozing-until" = "Hasta %@"; + +/* VPN Live Activity dismiss button text */ +"widget.vpn.live-activity.button.dismiss" = "Descartar"; + +/* VPN Live Activity wake up button text */ +"widget.vpn.live-activity.button.wake-up" = "Despertar"; + +/* VPN Live Activity active label text */ +"widget.vpn.live-activity.label.active" = "La VPN está activada"; + +/* VPN Live Activity snoozing label text */ +"widget.vpn.live-activity.label.snoozing" = "VPN en modo de suspenso"; + /* Message describing VPN connected status */ "widget.vpn.status.connected" = "La VPN está activada"; /* Message describing VPN disconnected status */ "widget.vpn.status.disconnected" = "La VPN está desactivada"; +/* Message describing VPN snoozing status */ +"widget.vpn.status.snoozed" = "La VPN está en modo de suspensión"; + /* Subtitle describing VPN disconnected status */ "widget.vpn.subtitle.disconnected" = "No conectado"; diff --git a/Widgets/et.lproj/Localizable.strings b/Widgets/et.lproj/Localizable.strings index c87230ba66..b287caa947 100644 --- a/Widgets/et.lproj/Localizable.strings +++ b/Widgets/et.lproj/Localizable.strings @@ -1,9 +1,3 @@ -/* No comment provided by engineer. */ -"iOS 17 required" = "iOS 17 required"; - -/* No comment provided by engineer. */ -"Label" = "Silt"; - /* Description shown to the user when adding the Email Protection lock screen widget */ "lock.screen.widget.email.description" = "Loo hetkega uus privaatne Duck Address."; @@ -82,12 +76,30 @@ /* VPN disconnect button text */ "widget.vpn.button.disconnect" = "Katkesta ühendus"; +/* Label for the snooze end date, e.g. 'Until 9:51 AM' */ +"widget.vpn.label.snoozing-until" = "Kuni %@"; + +/* VPN Live Activity dismiss button text */ +"widget.vpn.live-activity.button.dismiss" = "Loobu"; + +/* VPN Live Activity wake up button text */ +"widget.vpn.live-activity.button.wake-up" = "Ärata"; + +/* VPN Live Activity active label text */ +"widget.vpn.live-activity.label.active" = "VPN on Sees"; + +/* VPN Live Activity snoozing label text */ +"widget.vpn.live-activity.label.snoozing" = "VPN-i peatamine"; + /* Message describing VPN connected status */ "widget.vpn.status.connected" = "VPN on Sees"; /* Message describing VPN disconnected status */ "widget.vpn.status.disconnected" = "VPN on Väljas"; +/* Message describing VPN snoozing status */ +"widget.vpn.status.snoozed" = "VPN on uinutatud"; + /* Subtitle describing VPN disconnected status */ "widget.vpn.subtitle.disconnected" = "Ei ole ühendatud"; diff --git a/Widgets/fi.lproj/Localizable.strings b/Widgets/fi.lproj/Localizable.strings index 6f78497eb2..0e7c84c375 100644 --- a/Widgets/fi.lproj/Localizable.strings +++ b/Widgets/fi.lproj/Localizable.strings @@ -1,9 +1,3 @@ -/* No comment provided by engineer. */ -"iOS 17 required" = "iOS 17 vaaditaan"; - -/* No comment provided by engineer. */ -"Label" = "Label"; - /* Description shown to the user when adding the Email Protection lock screen widget */ "lock.screen.widget.email.description" = "Luo välittömästi uusi yksityinen Duck Address."; @@ -82,12 +76,30 @@ /* VPN disconnect button text */ "widget.vpn.button.disconnect" = "Katkaise yhteys"; +/* Label for the snooze end date, e.g. 'Until 9:51 AM' */ +"widget.vpn.label.snoozing-until" = "%@ asti"; + +/* VPN Live Activity dismiss button text */ +"widget.vpn.live-activity.button.dismiss" = "Hylkää"; + +/* VPN Live Activity wake up button text */ +"widget.vpn.live-activity.button.wake-up" = "Herätä"; + +/* VPN Live Activity active label text */ +"widget.vpn.live-activity.label.active" = "VPN on päällä"; + +/* VPN Live Activity snoozing label text */ +"widget.vpn.live-activity.label.snoozing" = "VPN lepotilassa"; + /* Message describing VPN connected status */ "widget.vpn.status.connected" = "VPN on päällä"; /* Message describing VPN disconnected status */ "widget.vpn.status.disconnected" = "VPN on pois päältä"; +/* Message describing VPN snoozing status */ +"widget.vpn.status.snoozed" = "VPN on lepotilassa"; + /* Subtitle describing VPN disconnected status */ "widget.vpn.subtitle.disconnected" = "Ei yhteyttä"; diff --git a/Widgets/fr.lproj/Localizable.strings b/Widgets/fr.lproj/Localizable.strings index 758fc32185..14ff413d37 100644 --- a/Widgets/fr.lproj/Localizable.strings +++ b/Widgets/fr.lproj/Localizable.strings @@ -1,9 +1,3 @@ -/* No comment provided by engineer. */ -"iOS 17 required" = "iOS 17 est requis"; - -/* No comment provided by engineer. */ -"Label" = "Label"; - /* Description shown to the user when adding the Email Protection lock screen widget */ "lock.screen.widget.email.description" = "Générez instantanément une nouvelle Duck Address."; @@ -82,12 +76,30 @@ /* VPN disconnect button text */ "widget.vpn.button.disconnect" = "Déconnecter"; +/* Label for the snooze end date, e.g. 'Until 9:51 AM' */ +"widget.vpn.label.snoozing-until" = "Jusqu'à %@"; + +/* VPN Live Activity dismiss button text */ +"widget.vpn.live-activity.button.dismiss" = "Ignorer"; + +/* VPN Live Activity wake up button text */ +"widget.vpn.live-activity.button.wake-up" = "Réveiller"; + +/* VPN Live Activity active label text */ +"widget.vpn.live-activity.label.active" = "Le VPN est activé"; + +/* VPN Live Activity snoozing label text */ +"widget.vpn.live-activity.label.snoozing" = "Mise en veille du VPN"; + /* Message describing VPN connected status */ "widget.vpn.status.connected" = "Le VPN est activé"; /* Message describing VPN disconnected status */ "widget.vpn.status.disconnected" = "Le VPN est désactivé"; +/* Message describing VPN snoozing status */ +"widget.vpn.status.snoozed" = "Le VPN est en veille"; + /* Subtitle describing VPN disconnected status */ "widget.vpn.subtitle.disconnected" = "Non connecté"; diff --git a/Widgets/hr.lproj/Localizable.strings b/Widgets/hr.lproj/Localizable.strings index 9256aeb08c..311a2abb3b 100644 --- a/Widgets/hr.lproj/Localizable.strings +++ b/Widgets/hr.lproj/Localizable.strings @@ -1,9 +1,3 @@ -/* No comment provided by engineer. */ -"iOS 17 required" = "iOS 17 required"; - -/* No comment provided by engineer. */ -"Label" = "Label"; - /* Description shown to the user when adding the Email Protection lock screen widget */ "lock.screen.widget.email.description" = "Odmah generiraj novu privatnu adresu - Duck Address."; @@ -82,12 +76,30 @@ /* VPN disconnect button text */ "widget.vpn.button.disconnect" = "Prekini vezu"; +/* Label for the snooze end date, e.g. 'Until 9:51 AM' */ +"widget.vpn.label.snoozing-until" = "Do %@"; + +/* VPN Live Activity dismiss button text */ +"widget.vpn.live-activity.button.dismiss" = "Odbaci"; + +/* VPN Live Activity wake up button text */ +"widget.vpn.live-activity.button.wake-up" = "Probudi"; + +/* VPN Live Activity active label text */ +"widget.vpn.live-activity.label.active" = "VPN je uključen"; + +/* VPN Live Activity snoozing label text */ +"widget.vpn.live-activity.label.snoozing" = "VPN je u stanju mirovanja"; + /* Message describing VPN connected status */ "widget.vpn.status.connected" = "VPN je uključen"; /* Message describing VPN disconnected status */ "widget.vpn.status.disconnected" = "VPN je isključen"; +/* Message describing VPN snoozing status */ +"widget.vpn.status.snoozed" = "VPN je u stanju mirovanja"; + /* Subtitle describing VPN disconnected status */ "widget.vpn.subtitle.disconnected" = "Nije povezano"; diff --git a/Widgets/hu.lproj/Localizable.strings b/Widgets/hu.lproj/Localizable.strings index 70325f9159..ab812f4721 100644 --- a/Widgets/hu.lproj/Localizable.strings +++ b/Widgets/hu.lproj/Localizable.strings @@ -1,9 +1,3 @@ -/* No comment provided by engineer. */ -"iOS 17 required" = "iOS 17 szükséges"; - -/* No comment provided by engineer. */ -"Label" = "Címke"; - /* Description shown to the user when adding the Email Protection lock screen widget */ "lock.screen.widget.email.description" = "Új privát Duck-cím azonnali létrehozása."; @@ -82,12 +76,30 @@ /* VPN disconnect button text */ "widget.vpn.button.disconnect" = "Leválasztás"; +/* Label for the snooze end date, e.g. 'Until 9:51 AM' */ +"widget.vpn.label.snoozing-until" = "%@-ig"; + +/* VPN Live Activity dismiss button text */ +"widget.vpn.live-activity.button.dismiss" = "Elutasítás"; + +/* VPN Live Activity wake up button text */ +"widget.vpn.live-activity.button.wake-up" = "Ébredj fel"; + +/* VPN Live Activity active label text */ +"widget.vpn.live-activity.label.active" = "VPN csatlakoztatva"; + +/* VPN Live Activity snoozing label text */ +"widget.vpn.live-activity.label.snoozing" = "VPN szüneteltetése"; + /* Message describing VPN connected status */ "widget.vpn.status.connected" = "VPN csatlakoztatva"; /* Message describing VPN disconnected status */ "widget.vpn.status.disconnected" = "VPN kikapcsolva"; +/* Message describing VPN snoozing status */ +"widget.vpn.status.snoozed" = "A VPN szüneteltetve van"; + /* Subtitle describing VPN disconnected status */ "widget.vpn.subtitle.disconnected" = "Nincs csatlakoztatva"; diff --git a/Widgets/it.lproj/Localizable.strings b/Widgets/it.lproj/Localizable.strings index d10e40bb4c..6668599ad5 100644 --- a/Widgets/it.lproj/Localizable.strings +++ b/Widgets/it.lproj/Localizable.strings @@ -1,9 +1,3 @@ -/* No comment provided by engineer. */ -"iOS 17 required" = "iOS 17 required"; - -/* No comment provided by engineer. */ -"Label" = "Etichetta"; - /* Description shown to the user when adding the Email Protection lock screen widget */ "lock.screen.widget.email.description" = "Genera istantaneamente un nuovo Duck Address privato."; @@ -82,12 +76,30 @@ /* VPN disconnect button text */ "widget.vpn.button.disconnect" = "Disconnettiti"; +/* Label for the snooze end date, e.g. 'Until 9:51 AM' */ +"widget.vpn.label.snoozing-until" = "Fino a %@"; + +/* VPN Live Activity dismiss button text */ +"widget.vpn.live-activity.button.dismiss" = "Ignora"; + +/* VPN Live Activity wake up button text */ +"widget.vpn.live-activity.button.wake-up" = "Sveglia"; + +/* VPN Live Activity active label text */ +"widget.vpn.live-activity.label.active" = "La VPN è attiva"; + +/* VPN Live Activity snoozing label text */ +"widget.vpn.live-activity.label.snoozing" = "Sospensione della VPN"; + /* Message describing VPN connected status */ "widget.vpn.status.connected" = "La VPN è attiva"; /* Message describing VPN disconnected status */ "widget.vpn.status.disconnected" = "La VPN è disattivata"; +/* Message describing VPN snoozing status */ +"widget.vpn.status.snoozed" = "La VPN è sospesa"; + /* Subtitle describing VPN disconnected status */ "widget.vpn.subtitle.disconnected" = "Non collegata"; diff --git a/Widgets/lt.lproj/Localizable.strings b/Widgets/lt.lproj/Localizable.strings index 63d921f75f..e67108987a 100644 --- a/Widgets/lt.lproj/Localizable.strings +++ b/Widgets/lt.lproj/Localizable.strings @@ -1,9 +1,3 @@ -/* No comment provided by engineer. */ -"iOS 17 required" = "Reikalinga „iOS 17“"; - -/* No comment provided by engineer. */ -"Label" = "Etiketė"; - /* Description shown to the user when adding the Email Protection lock screen widget */ "lock.screen.widget.email.description" = "Nedelsiant sugeneruokite naują privatų „Duck Address“."; @@ -82,12 +76,30 @@ /* VPN disconnect button text */ "widget.vpn.button.disconnect" = "Atjungti"; +/* Label for the snooze end date, e.g. 'Until 9:51 AM' */ +"widget.vpn.label.snoozing-until" = "Iki %@"; + +/* VPN Live Activity dismiss button text */ +"widget.vpn.live-activity.button.dismiss" = "Atmesti"; + +/* VPN Live Activity wake up button text */ +"widget.vpn.live-activity.button.wake-up" = "Atšaukti pristabdymą"; + +/* VPN Live Activity active label text */ +"widget.vpn.live-activity.label.active" = "VPN įjungtas"; + +/* VPN Live Activity snoozing label text */ +"widget.vpn.live-activity.label.snoozing" = "VPN pristabdymas"; + /* Message describing VPN connected status */ "widget.vpn.status.connected" = "VPN įjungtas"; /* Message describing VPN disconnected status */ "widget.vpn.status.disconnected" = "VPN išjungtas"; +/* Message describing VPN snoozing status */ +"widget.vpn.status.snoozed" = "VPN pristabdytas"; + /* Subtitle describing VPN disconnected status */ "widget.vpn.subtitle.disconnected" = "Neprijungta"; diff --git a/Widgets/lv.lproj/Localizable.strings b/Widgets/lv.lproj/Localizable.strings index 9ab34f39a0..0b26680728 100644 --- a/Widgets/lv.lproj/Localizable.strings +++ b/Widgets/lv.lproj/Localizable.strings @@ -1,9 +1,3 @@ -/* No comment provided by engineer. */ -"iOS 17 required" = "Nepieciešams iOS 17"; - -/* No comment provided by engineer. */ -"Label" = "Etiķete"; - /* Description shown to the user when adding the Email Protection lock screen widget */ "lock.screen.widget.email.description" = "Nekavējoties ģenerē jaunu privātu Duck adresi."; @@ -82,12 +76,30 @@ /* VPN disconnect button text */ "widget.vpn.button.disconnect" = "Atvienot"; +/* Label for the snooze end date, e.g. 'Until 9:51 AM' */ +"widget.vpn.label.snoozing-until" = "Līdz %@."; + +/* VPN Live Activity dismiss button text */ +"widget.vpn.live-activity.button.dismiss" = "Nerādīt"; + +/* VPN Live Activity wake up button text */ +"widget.vpn.live-activity.button.wake-up" = "Pamodināt"; + +/* VPN Live Activity active label text */ +"widget.vpn.live-activity.label.active" = "VPN ir ieslēgts"; + +/* VPN Live Activity snoozing label text */ +"widget.vpn.live-activity.label.snoozing" = "VPN atslēgšana"; + /* Message describing VPN connected status */ "widget.vpn.status.connected" = "VPN ir ieslēgts"; /* Message describing VPN disconnected status */ "widget.vpn.status.disconnected" = "VPN ir izslēgts"; +/* Message describing VPN snoozing status */ +"widget.vpn.status.snoozed" = "VPN ir atslēgts"; + /* Subtitle describing VPN disconnected status */ "widget.vpn.subtitle.disconnected" = "Nav savienots"; diff --git a/Widgets/nb.lproj/Localizable.strings b/Widgets/nb.lproj/Localizable.strings index cd0ca864d5..f2678a7d3b 100644 --- a/Widgets/nb.lproj/Localizable.strings +++ b/Widgets/nb.lproj/Localizable.strings @@ -1,9 +1,3 @@ -/* No comment provided by engineer. */ -"iOS 17 required" = "iOS 17 required"; - -/* No comment provided by engineer. */ -"Label" = "Etikett"; - /* Description shown to the user when adding the Email Protection lock screen widget */ "lock.screen.widget.email.description" = "Generer en ny privat Duck Address på et øyeblikk."; @@ -82,12 +76,30 @@ /* VPN disconnect button text */ "widget.vpn.button.disconnect" = "Koble fra"; +/* Label for the snooze end date, e.g. 'Until 9:51 AM' */ +"widget.vpn.label.snoozing-until" = "Til %@"; + +/* VPN Live Activity dismiss button text */ +"widget.vpn.live-activity.button.dismiss" = "Avvis"; + +/* VPN Live Activity wake up button text */ +"widget.vpn.live-activity.button.wake-up" = "Vekk opp"; + +/* VPN Live Activity active label text */ +"widget.vpn.live-activity.label.active" = "VPN er på"; + +/* VPN Live Activity snoozing label text */ +"widget.vpn.live-activity.label.snoozing" = "VPN slumrer"; + /* Message describing VPN connected status */ "widget.vpn.status.connected" = "VPN er på"; /* Message describing VPN disconnected status */ "widget.vpn.status.disconnected" = "VPN er av"; +/* Message describing VPN snoozing status */ +"widget.vpn.status.snoozed" = "VPN slumrer"; + /* Subtitle describing VPN disconnected status */ "widget.vpn.subtitle.disconnected" = "Ikke tilkoblet"; diff --git a/Widgets/nl.lproj/Localizable.strings b/Widgets/nl.lproj/Localizable.strings index f6ea7210c6..2056845b3e 100644 --- a/Widgets/nl.lproj/Localizable.strings +++ b/Widgets/nl.lproj/Localizable.strings @@ -1,9 +1,3 @@ -/* No comment provided by engineer. */ -"iOS 17 required" = "iOS 17 vereist"; - -/* No comment provided by engineer. */ -"Label" = "Label"; - /* Description shown to the user when adding the Email Protection lock screen widget */ "lock.screen.widget.email.description" = "Maak meteen een nieuw privaat Duck Address aan."; @@ -82,12 +76,30 @@ /* VPN disconnect button text */ "widget.vpn.button.disconnect" = "Verbinding verbreken"; +/* Label for the snooze end date, e.g. 'Until 9:51 AM' */ +"widget.vpn.label.snoozing-until" = "Tot %@"; + +/* VPN Live Activity dismiss button text */ +"widget.vpn.live-activity.button.dismiss" = "Negeren"; + +/* VPN Live Activity wake up button text */ +"widget.vpn.live-activity.button.wake-up" = "Inschakelen"; + +/* VPN Live Activity active label text */ +"widget.vpn.live-activity.label.active" = "VPN is ingeschakeld"; + +/* VPN Live Activity snoozing label text */ +"widget.vpn.live-activity.label.snoozing" = "VPN in sluimerstand"; + /* Message describing VPN connected status */ "widget.vpn.status.connected" = "VPN is ingeschakeld"; /* Message describing VPN disconnected status */ "widget.vpn.status.disconnected" = "VPN is uitgeschakeld"; +/* Message describing VPN snoozing status */ +"widget.vpn.status.snoozed" = "VPN is in sluimerstand"; + /* Subtitle describing VPN disconnected status */ "widget.vpn.subtitle.disconnected" = "Niet verbonden"; diff --git a/Widgets/pl.lproj/Localizable.strings b/Widgets/pl.lproj/Localizable.strings index 6be2d64e2e..54b81da1f6 100644 --- a/Widgets/pl.lproj/Localizable.strings +++ b/Widgets/pl.lproj/Localizable.strings @@ -1,9 +1,3 @@ -/* No comment provided by engineer. */ -"iOS 17 required" = "iOS 17 required"; - -/* No comment provided by engineer. */ -"Label" = "Etykieta"; - /* Description shown to the user when adding the Email Protection lock screen widget */ "lock.screen.widget.email.description" = "Błyskawicznie wygeneruj prywatny adres Duck Address."; @@ -82,12 +76,30 @@ /* VPN disconnect button text */ "widget.vpn.button.disconnect" = "Odłącz"; +/* Label for the snooze end date, e.g. 'Until 9:51 AM' */ +"widget.vpn.label.snoozing-until" = "Do %@"; + +/* VPN Live Activity dismiss button text */ +"widget.vpn.live-activity.button.dismiss" = "Odrzuć"; + +/* VPN Live Activity wake up button text */ +"widget.vpn.live-activity.button.wake-up" = "Przywróć"; + +/* VPN Live Activity active label text */ +"widget.vpn.live-activity.label.active" = "Włączono VPN"; + +/* VPN Live Activity snoozing label text */ +"widget.vpn.live-activity.label.snoozing" = "Wstrzymano VPN"; + /* Message describing VPN connected status */ "widget.vpn.status.connected" = "Włączono VPN"; /* Message describing VPN disconnected status */ "widget.vpn.status.disconnected" = "Wyłączono VPN"; +/* Message describing VPN snoozing status */ +"widget.vpn.status.snoozed" = "Wstrzymano VPN"; + /* Subtitle describing VPN disconnected status */ "widget.vpn.subtitle.disconnected" = "Nie połączono"; diff --git a/Widgets/pt.lproj/Localizable.strings b/Widgets/pt.lproj/Localizable.strings index 2d9e895eb8..ea25e42c1d 100644 --- a/Widgets/pt.lproj/Localizable.strings +++ b/Widgets/pt.lproj/Localizable.strings @@ -1,9 +1,3 @@ -/* No comment provided by engineer. */ -"iOS 17 required" = "iOS 17 required"; - -/* No comment provided by engineer. */ -"Label" = "Rótulo"; - /* Description shown to the user when adding the Email Protection lock screen widget */ "lock.screen.widget.email.description" = "Gera instantaneamente um novo Duck Address privado."; @@ -82,12 +76,30 @@ /* VPN disconnect button text */ "widget.vpn.button.disconnect" = "Desligar"; +/* Label for the snooze end date, e.g. 'Until 9:51 AM' */ +"widget.vpn.label.snoozing-until" = "Até %@"; + +/* VPN Live Activity dismiss button text */ +"widget.vpn.live-activity.button.dismiss" = "Ignorar"; + +/* VPN Live Activity wake up button text */ +"widget.vpn.live-activity.button.wake-up" = "Acordar"; + +/* VPN Live Activity active label text */ +"widget.vpn.live-activity.label.active" = "A VPN está ativada"; + +/* VPN Live Activity snoozing label text */ +"widget.vpn.live-activity.label.snoozing" = "Suspensão da VPN"; + /* Message describing VPN connected status */ "widget.vpn.status.connected" = "A VPN está ativada"; /* Message describing VPN disconnected status */ "widget.vpn.status.disconnected" = "A VPN está desativada"; +/* Message describing VPN snoozing status */ +"widget.vpn.status.snoozed" = "A VPN está suspensa"; + /* Subtitle describing VPN disconnected status */ "widget.vpn.subtitle.disconnected" = "Não ligada"; diff --git a/Widgets/ro.lproj/Localizable.strings b/Widgets/ro.lproj/Localizable.strings index e268927bb4..53f83f011a 100644 --- a/Widgets/ro.lproj/Localizable.strings +++ b/Widgets/ro.lproj/Localizable.strings @@ -1,9 +1,3 @@ -/* No comment provided by engineer. */ -"iOS 17 required" = "iOS 17 required"; - -/* No comment provided by engineer. */ -"Label" = "Label"; - /* Description shown to the user when adding the Email Protection lock screen widget */ "lock.screen.widget.email.description" = "Generează instantaneu o nouă Duck Address privată."; @@ -82,12 +76,30 @@ /* VPN disconnect button text */ "widget.vpn.button.disconnect" = "Deconectează-te"; +/* Label for the snooze end date, e.g. 'Until 9:51 AM' */ +"widget.vpn.label.snoozing-until" = "Până la %@"; + +/* VPN Live Activity dismiss button text */ +"widget.vpn.live-activity.button.dismiss" = "Renunță"; + +/* VPN Live Activity wake up button text */ +"widget.vpn.live-activity.button.wake-up" = "Activare"; + +/* VPN Live Activity active label text */ +"widget.vpn.live-activity.label.active" = "VPN este activat"; + +/* VPN Live Activity snoozing label text */ +"widget.vpn.live-activity.label.snoozing" = "Amânare VPN"; + /* Message describing VPN connected status */ "widget.vpn.status.connected" = "VPN este activat"; /* Message describing VPN disconnected status */ "widget.vpn.status.disconnected" = "VPN este dezactivat"; +/* Message describing VPN snoozing status */ +"widget.vpn.status.snoozed" = "VPN-ul este în repaus"; + /* Subtitle describing VPN disconnected status */ "widget.vpn.subtitle.disconnected" = "Nu este conectat"; diff --git a/Widgets/ru.lproj/Localizable.strings b/Widgets/ru.lproj/Localizable.strings index 98fd5367a7..1030dede6c 100644 --- a/Widgets/ru.lproj/Localizable.strings +++ b/Widgets/ru.lproj/Localizable.strings @@ -1,9 +1,3 @@ -/* No comment provided by engineer. */ -"iOS 17 required" = "iOS 17 required"; - -/* No comment provided by engineer. */ -"Label" = "Метка"; - /* Description shown to the user when adding the Email Protection lock screen widget */ "lock.screen.widget.email.description" = "Моментальное создание приватного адреса Duck Address."; @@ -82,12 +76,30 @@ /* VPN disconnect button text */ "widget.vpn.button.disconnect" = "Отключиться"; +/* Label for the snooze end date, e.g. 'Until 9:51 AM' */ +"widget.vpn.label.snoozing-until" = "До %@"; + +/* VPN Live Activity dismiss button text */ +"widget.vpn.live-activity.button.dismiss" = "Отклонить"; + +/* VPN Live Activity wake up button text */ +"widget.vpn.live-activity.button.wake-up" = "Разбудить"; + +/* VPN Live Activity active label text */ +"widget.vpn.live-activity.label.active" = "VPN включен"; + +/* VPN Live Activity snoozing label text */ +"widget.vpn.live-activity.label.snoozing" = "Работа VPN отложена"; + /* Message describing VPN connected status */ "widget.vpn.status.connected" = "VPN включен"; /* Message describing VPN disconnected status */ "widget.vpn.status.disconnected" = "VPN отключен"; +/* Message describing VPN snoozing status */ +"widget.vpn.status.snoozed" = "Работа VPN приостановлена"; + /* Subtitle describing VPN disconnected status */ "widget.vpn.subtitle.disconnected" = "Не подключен"; diff --git a/Widgets/sk.lproj/Localizable.strings b/Widgets/sk.lproj/Localizable.strings index 290b7e4c6a..6181d0c876 100644 --- a/Widgets/sk.lproj/Localizable.strings +++ b/Widgets/sk.lproj/Localizable.strings @@ -1,9 +1,3 @@ -/* No comment provided by engineer. */ -"iOS 17 required" = "Vyžaduje sa iOS 17"; - -/* No comment provided by engineer. */ -"Label" = "Označenie"; - /* Description shown to the user when adding the Email Protection lock screen widget */ "lock.screen.widget.email.description" = "Okamžite vygenerujte novú súkromnú Duck adresu."; @@ -82,12 +76,30 @@ /* VPN disconnect button text */ "widget.vpn.button.disconnect" = "Odpojenie"; +/* Label for the snooze end date, e.g. 'Until 9:51 AM' */ +"widget.vpn.label.snoozing-until" = "Do %@"; + +/* VPN Live Activity dismiss button text */ +"widget.vpn.live-activity.button.dismiss" = "Odmietnuť"; + +/* VPN Live Activity wake up button text */ +"widget.vpn.live-activity.button.wake-up" = "Prebuď sa"; + +/* VPN Live Activity active label text */ +"widget.vpn.live-activity.label.active" = "VPN je zapnutá"; + +/* VPN Live Activity snoozing label text */ +"widget.vpn.live-activity.label.snoozing" = "Odloženie VPN"; + /* Message describing VPN connected status */ "widget.vpn.status.connected" = "VPN je zapnutá"; /* Message describing VPN disconnected status */ "widget.vpn.status.disconnected" = "VPN je vypnutá"; +/* Message describing VPN snoozing status */ +"widget.vpn.status.snoozed" = "VPN je pozastavená"; + /* Subtitle describing VPN disconnected status */ "widget.vpn.subtitle.disconnected" = "Nie je pripojený"; diff --git a/Widgets/sl.lproj/Localizable.strings b/Widgets/sl.lproj/Localizable.strings index 16c5c4761c..9d1443e0eb 100644 --- a/Widgets/sl.lproj/Localizable.strings +++ b/Widgets/sl.lproj/Localizable.strings @@ -1,9 +1,3 @@ -/* No comment provided by engineer. */ -"iOS 17 required" = "Zahtevan je iOS 17"; - -/* No comment provided by engineer. */ -"Label" = "Oznaka"; - /* Description shown to the user when adding the Email Protection lock screen widget */ "lock.screen.widget.email.description" = "Takoj ustvarite nov zasebni naslov Duck Address."; @@ -82,12 +76,30 @@ /* VPN disconnect button text */ "widget.vpn.button.disconnect" = "Prekini"; +/* Label for the snooze end date, e.g. 'Until 9:51 AM' */ +"widget.vpn.label.snoozing-until" = "Do %@"; + +/* VPN Live Activity dismiss button text */ +"widget.vpn.live-activity.button.dismiss" = "Opusti"; + +/* VPN Live Activity wake up button text */ +"widget.vpn.live-activity.button.wake-up" = "Prebudi"; + +/* VPN Live Activity active label text */ +"widget.vpn.live-activity.label.active" = "VPN je vklopljen"; + +/* VPN Live Activity snoozing label text */ +"widget.vpn.live-activity.label.snoozing" = "Dremež za VPN"; + /* Message describing VPN connected status */ "widget.vpn.status.connected" = "VPN je vklopljen"; /* Message describing VPN disconnected status */ "widget.vpn.status.disconnected" = "VPN je izklopljen"; +/* Message describing VPN snoozing status */ +"widget.vpn.status.snoozed" = "VPN je začasno zaustavljen"; + /* Subtitle describing VPN disconnected status */ "widget.vpn.subtitle.disconnected" = "Brez povezave"; diff --git a/Widgets/sv.lproj/Localizable.strings b/Widgets/sv.lproj/Localizable.strings index 6ae579bc27..fd68f9e69c 100644 --- a/Widgets/sv.lproj/Localizable.strings +++ b/Widgets/sv.lproj/Localizable.strings @@ -1,9 +1,3 @@ -/* No comment provided by engineer. */ -"iOS 17 required" = "iOS 17 krävs"; - -/* No comment provided by engineer. */ -"Label" = "Etikett"; - /* Description shown to the user when adding the Email Protection lock screen widget */ "lock.screen.widget.email.description" = "Generera omedelbart en ny privat Duck Address."; @@ -82,12 +76,30 @@ /* VPN disconnect button text */ "widget.vpn.button.disconnect" = "Koppla från"; +/* Label for the snooze end date, e.g. 'Until 9:51 AM' */ +"widget.vpn.label.snoozing-until" = "Fram till %@"; + +/* VPN Live Activity dismiss button text */ +"widget.vpn.live-activity.button.dismiss" = "Avvisa"; + +/* VPN Live Activity wake up button text */ +"widget.vpn.live-activity.button.wake-up" = "Väck"; + +/* VPN Live Activity active label text */ +"widget.vpn.live-activity.label.active" = "VPN är på"; + +/* VPN Live Activity snoozing label text */ +"widget.vpn.live-activity.label.snoozing" = "VPN har pausats"; + /* Message describing VPN connected status */ "widget.vpn.status.connected" = "VPN är på"; /* Message describing VPN disconnected status */ "widget.vpn.status.disconnected" = "VPN är av"; +/* Message describing VPN snoozing status */ +"widget.vpn.status.snoozed" = "VPN har pausats"; + /* Subtitle describing VPN disconnected status */ "widget.vpn.subtitle.disconnected" = "Ej ansluten"; diff --git a/Widgets/tr.lproj/Localizable.strings b/Widgets/tr.lproj/Localizable.strings index e00756b552..b0b92980f7 100644 --- a/Widgets/tr.lproj/Localizable.strings +++ b/Widgets/tr.lproj/Localizable.strings @@ -1,9 +1,3 @@ -/* No comment provided by engineer. */ -"iOS 17 required" = "iOS 17 required"; - -/* No comment provided by engineer. */ -"Label" = "Label"; - /* Description shown to the user when adding the Email Protection lock screen widget */ "lock.screen.widget.email.description" = "Hemen yeni bir özel Duck Address oluşturun."; @@ -82,12 +76,30 @@ /* VPN disconnect button text */ "widget.vpn.button.disconnect" = "Bağlantıyı Kes"; +/* Label for the snooze end date, e.g. 'Until 9:51 AM' */ +"widget.vpn.label.snoozing-until" = "Uyku modu sonu: %@"; + +/* VPN Live Activity dismiss button text */ +"widget.vpn.live-activity.button.dismiss" = "Reddet"; + +/* VPN Live Activity wake up button text */ +"widget.vpn.live-activity.button.wake-up" = "Uyandır"; + +/* VPN Live Activity active label text */ +"widget.vpn.live-activity.label.active" = "VPN Açık"; + +/* VPN Live Activity snoozing label text */ +"widget.vpn.live-activity.label.snoozing" = "VPN Uyku Moduna Alma"; + /* Message describing VPN connected status */ "widget.vpn.status.connected" = "VPN Açık"; /* Message describing VPN disconnected status */ "widget.vpn.status.disconnected" = "VPN Kapalı"; +/* Message describing VPN snoozing status */ +"widget.vpn.status.snoozed" = "VPN uyku modunda"; + /* Subtitle describing VPN disconnected status */ "widget.vpn.subtitle.disconnected" = "Bağlı değil"; diff --git a/adhocExportOptions.plist b/adhocExportOptions.plist index 8af7edc154..de2b86d155 100644 --- a/adhocExportOptions.plist +++ b/adhocExportOptions.plist @@ -25,6 +25,9 @@ com.duckduckgo.mobile.ios.NetworkExtension match AdHoc com.duckduckgo.mobile.ios.NetworkExtension + + com.duckduckgo.mobile.ios.CredentialExtension + match AdHoc com.duckduckgo.mobile.ios.CredentialExtension diff --git a/alphaAdhocExportOptions.plist b/alphaAdhocExportOptions.plist index faed54b818..d733a242c0 100644 --- a/alphaAdhocExportOptions.plist +++ b/alphaAdhocExportOptions.plist @@ -20,6 +20,8 @@ match AdHoc com.duckduckgo.mobile.ios.alpha.Widgets com.duckduckgo.mobile.ios.alpha.NetworkExtension match AdHoc com.duckduckgo.mobile.ios.alpha.NetworkExtension + com.duckduckgo.mobile.ios.alpha.CredentialExtension + match AdHoc com.duckduckgo.mobile.ios.alpha.CredentialExtension diff --git a/appStoreExportOptions.plist b/appStoreExportOptions.plist index ff60631b5c..ed346823e8 100644 --- a/appStoreExportOptions.plist +++ b/appStoreExportOptions.plist @@ -18,6 +18,8 @@ match AppStore com.duckduckgo.mobile.ios.Widgets com.duckduckgo.mobile.ios.NetworkExtension match AppStore com.duckduckgo.mobile.ios.NetworkExtension + com.duckduckgo.mobile.ios.CredentialExtension + match AppStore com.duckduckgo.mobile.ios.CredentialExtension diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 1d0d12d932..d0da0e60c7 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -1,209 +1,217 @@ default_platform :ios -before_all do - setup_ci if is_ci -end +platform :ios do -################################################# -# Public lanes -################################################# + before_all do + setup_ci if is_ci + end -desc 'Fetches and updates certificates and provisioning profiles for App Store distribution' -lane :sync_signing do |options| - do_sync_signing(options) -end + ################################################# + # Public lanes + ################################################# -desc 'Fetches and updates certificates and provisioning profiles for Ad-Hoc distribution' -lane :sync_signing_adhoc do |options| - do_sync_signing(options) -end + desc 'Fetches and updates certificates and provisioning profiles for App Store distribution' + lane :sync_signing do |options| + do_sync_signing(options) + end -desc 'Fetches and updates certificates and provisioning profiles for Alpha distribution' -lane :sync_signing_alpha do |options| - do_sync_signing(options) -end + desc 'Fetches and updates certificates and provisioning profiles for Ad-Hoc distribution' + lane :sync_signing_adhoc do |options| + do_sync_signing(options) + end -desc 'Fetches and updates certificates and provisioning profiles for Ad-Hoc distribution' -lane :sync_signing_alpha_adhoc do |options| - do_sync_signing(options) -end + desc 'Fetches and updates certificates and provisioning profiles for Alpha distribution' + lane :sync_signing_alpha do |options| + do_sync_signing(options) + end -desc 'Makes Ad-Hoc build with a specified name and alpha bundle ID in a given directory' -lane :adhoc do |options| - alpha_adhoc(options) -end + desc 'Fetches and updates certificates and provisioning profiles for Ad-Hoc distribution' + lane :sync_signing_alpha_adhoc do |options| + do_sync_signing(options) + end -desc 'Makes Ad-Hoc build with a specified name and release bundle ID in a given directory' -lane :release_adhoc do |options| - - # Workaround for match + gym failing at build phase https://forums.swift.org/t/xcode-14-beta-code-signing-issues-when-spm-targets-include-resources/59685/32 - if is_ci - configurations = [ - { - targets: ["DuckDuckGo"], - profile_name: "match AdHoc com.duckduckgo.mobile.ios" - }, - { - targets: ["ShareExtension"], - profile_name: "match AdHoc com.duckduckgo.mobile.ios.ShareExtension" - }, - { - targets: ["OpenAction"], - profile_name: "match AdHoc com.duckduckgo.mobile.ios.OpenAction2" - }, - { - targets: ["WidgetsExtension"], - profile_name: "match AdHoc com.duckduckgo.mobile.ios.Widgets" - }, - { - targets: ["PacketTunnelProvider"], - profile_name: "match AdHoc com.duckduckgo.mobile.ios.NetworkExtension" - } - ] - - configurations.each do |config| - update_code_signing_settings( - use_automatic_signing: false, - build_configurations: ["Release"], - code_sign_identity: "iPhone Distribution", - **config - ) - end + desc 'Makes Ad-Hoc build with a specified name and alpha bundle ID in a given directory' + lane :adhoc do |options| + alpha_adhoc(options) end - sync_signing_adhoc(options) + desc 'Makes Ad-Hoc build with a specified name and release bundle ID in a given directory' + lane :release_adhoc do |options| + + # Workaround for match + gym failing at build phase https://forums.swift.org/t/xcode-14-beta-code-signing-issues-when-spm-targets-include-resources/59685/32 + if is_ci + configurations = [ + { + targets: ["DuckDuckGo"], + profile_name: "match AdHoc com.duckduckgo.mobile.ios" + }, + { + targets: ["ShareExtension"], + profile_name: "match AdHoc com.duckduckgo.mobile.ios.ShareExtension" + }, + { + targets: ["OpenAction"], + profile_name: "match AdHoc com.duckduckgo.mobile.ios.OpenAction2" + }, + { + targets: ["WidgetsExtension"], + profile_name: "match AdHoc com.duckduckgo.mobile.ios.Widgets" + }, + { + targets: ["PacketTunnelProvider"], + profile_name: "match AdHoc com.duckduckgo.mobile.ios.NetworkExtension" + }, + { + targets: ["AutofillCredentialProvider"], + profile_name: "match AdHoc com.duckduckgo.mobile.ios.CredentialExtension" + } + ] + + configurations.each do |config| + update_code_signing_settings( + use_automatic_signing: false, + build_configurations: ["Release"], + code_sign_identity: "iPhone Distribution", + **config + ) + end + end - suffix = "" - if options[:suffix] - suffix = "#{options[:suffix]}-" - end + sync_signing_adhoc(options) - timestamp = Time.now.strftime("%Y-%m-%d-%H-%M") - output_name = "DuckDuckGo-#{suffix}#{timestamp}" - - build_app( - output_directory: options[:output], - output_name: output_name, - export_method: "ad-hoc", - scheme: "DuckDuckGo", - export_options: "adhocExportOptions.plist", - derived_data_path: "DerivedData", - xcargs: "-skipPackagePluginValidation -skipMacroValidation" - ) - - if is_ci - sh("echo output_name=#{output_name} >> $GITHUB_ENV") - end + suffix = "" + if options[:suffix] + suffix = "#{options[:suffix]}-" + end - Dir.chdir("..") do - sh("open", "#{options[:output]}") unless is_ci - end -end + timestamp = Time.now.strftime("%Y-%m-%d-%H-%M") + output_name = "DuckDuckGo-#{suffix}#{timestamp}" + + build_app( + output_directory: options[:output], + output_name: output_name, + export_method: "ad-hoc", + scheme: "DuckDuckGo", + export_options: "adhocExportOptions.plist", + derived_data_path: "DerivedData", + xcargs: "-skipPackagePluginValidation -skipMacroValidation" + ) -desc 'Makes Ad-Hoc build for alpha with a specified name and alpha bundle ID in a given directory' -lane :alpha_adhoc do |options| - - # Workaround for match + gym failing at build phase https://forums.swift.org/t/xcode-14-beta-code-signing-issues-when-spm-targets-include-resources/59685/32 - if is_ci - configurations = [ - { - targets: ["DuckDuckGo"], - profile_name: "match AdHoc com.duckduckgo.mobile.ios.alpha" - }, - { - targets: ["ShareExtension"], - profile_name: "match AdHoc com.duckduckgo.mobile.ios.alpha.ShareExtension" - }, - { - targets: ["OpenAction"], - profile_name: "match AdHoc com.duckduckgo.mobile.ios.alpha.OpenAction2" - }, - { - targets: ["WidgetsExtension"], - profile_name: "match AdHoc com.duckduckgo.mobile.ios.alpha.Widgets" - }, - { - targets: ["PacketTunnelProvider"], - profile_name: "match AdHoc com.duckduckgo.mobile.ios.alpha.NetworkExtension" - } - ] - - configurations.each do |config| - update_code_signing_settings( - use_automatic_signing: false, - build_configurations: ["Alpha"], - code_sign_identity: "iPhone Distribution", - **config - ) + if is_ci + sh("echo output_name=#{output_name} >> $GITHUB_ENV") + end + + Dir.chdir("..") do + sh("open", "#{options[:output]}") unless is_ci end end - sync_signing_alpha_adhoc(options) + desc 'Makes Ad-Hoc build for alpha with a specified name and alpha bundle ID in a given directory' + lane :alpha_adhoc do |options| + + # Workaround for match + gym failing at build phase https://forums.swift.org/t/xcode-14-beta-code-signing-issues-when-spm-targets-include-resources/59685/32 + if is_ci + configurations = [ + { + targets: ["DuckDuckGo"], + profile_name: "match AdHoc com.duckduckgo.mobile.ios.alpha" + }, + { + targets: ["ShareExtension"], + profile_name: "match AdHoc com.duckduckgo.mobile.ios.alpha.ShareExtension" + }, + { + targets: ["OpenAction"], + profile_name: "match AdHoc com.duckduckgo.mobile.ios.alpha.OpenAction2" + }, + { + targets: ["WidgetsExtension"], + profile_name: "match AdHoc com.duckduckgo.mobile.ios.alpha.Widgets" + }, + { + targets: ["PacketTunnelProvider"], + profile_name: "match AdHoc com.duckduckgo.mobile.ios.alpha.NetworkExtension" + }, + { + targets: ["AutofillCredentialProvider"], + profile_name: "match AdHoc com.duckduckgo.mobile.ios.alpha.CredentialExtension" + } + ] + + configurations.each do |config| + update_code_signing_settings( + use_automatic_signing: false, + build_configurations: ["Alpha"], + code_sign_identity: "iPhone Distribution", + **config + ) + end + end - suffix = "" - if options[:suffix] - suffix = "#{options[:suffix]}-" - end + sync_signing_alpha_adhoc(options) - timestamp = Time.now.strftime("%Y-%m-%d-%H-%M") - output_name = "DuckDuckGo-Alpha-#{suffix}#{timestamp}" - - build_app( - output_directory: options[:output], - output_name: output_name, - export_method: "ad-hoc", - configuration: "Alpha", - scheme: "DuckDuckGo-Alpha", - export_options: "alphaAdhocExportOptions.plist", - derived_data_path: "DerivedData", - xcargs: "-skipPackagePluginValidation" - ) - - if is_ci - sh("echo output_name=#{output_name} >> $GITHUB_ENV") - end + suffix = "" + if options[:suffix] + suffix = "#{options[:suffix]}-" + end - Dir.chdir("..") do - sh("open", "#{options[:output]}") unless is_ci - end -end + timestamp = Time.now.strftime("%Y-%m-%d-%H-%M") + output_name = "DuckDuckGo-Alpha-#{suffix}#{timestamp}" + + build_app( + output_directory: options[:output], + output_name: output_name, + export_method: "ad-hoc", + configuration: "Alpha", + scheme: "DuckDuckGo-Alpha", + export_options: "alphaAdhocExportOptions.plist", + derived_data_path: "DerivedData", + xcargs: "-skipPackagePluginValidation" + ) + if is_ci + sh("echo output_name=#{output_name} >> $GITHUB_ENV") + end + Dir.chdir("..") do + sh("open", "#{options[:output]}") unless is_ci + end + end -desc 'Promotes the latest TestFlight build to App Store without submitting for review' -lane :promote_latest_testflight_to_appstore do |options| + desc 'Promotes the latest TestFlight build to App Store without submitting for review' + lane :promote_latest_testflight_to_appstore do |options| - app_identifier = options[:alpha] ? "com.duckduckgo.mobile.ios.alpha" : "com.duckduckgo.mobile.ios" + app_identifier = options[:alpha] ? "com.duckduckgo.mobile.ios.alpha" : "com.duckduckgo.mobile.ios" - latest_testflight_build_number( - api_key: get_api_key, - username: get_username(options), - platform: 'ios', - app_identifier: app_identifier - ) + latest_testflight_build_number( + api_key: get_api_key, + username: get_username(options), + platform: 'ios', + app_identifier: app_identifier + ) - latest_build_number = lane_context[SharedValues::LATEST_TESTFLIGHT_BUILD_NUMBER] - latest_build_version = lane_context[SharedValues::LATEST_TESTFLIGHT_VERSION] + latest_build_number = lane_context[SharedValues::LATEST_TESTFLIGHT_BUILD_NUMBER] + latest_build_version = lane_context[SharedValues::LATEST_TESTFLIGHT_VERSION] - UI.message("The latest build number #{latest_build_number} of the latest version: #{latest_build_version} for app identifier: #{app_identifier}") + UI.message("The latest build number #{latest_build_number} of the latest version: #{latest_build_version} for app identifier: #{app_identifier}") - upload_metadata({ - build_number: latest_build_number.to_s, - app_version: latest_build_version.to_s, - app_identifier: app_identifier - }) -end + upload_metadata({ + build_number: latest_build_number.to_s, + app_version: latest_build_version.to_s, + app_identifier: app_identifier + }) + end -desc 'Makes App Store release build and uploads it to App Store Connect' -lane :release_appstore do |options| - build_release(options) + desc 'Makes App Store release build and uploads it to App Store Connect' + lane :release_appstore do |options| + build_release(options) - deliver(common_deliver_arguments.merge(options)) + deliver(common_deliver_arguments.merge(options)) - begin - upload_metadata(options) - rescue => exception - UI.user_error! %{Failed to upload metadata: #{exception} + begin + upload_metadata(options) + rescue => exception + UI.user_error! %{Failed to upload metadata: #{exception} 1. Your build has been successfully uploaded, it's only a problem with App Store metadata. 2. It's possible that there is a submission for another platform (macOS) in a non-editable state (e.g. Pending Developer Release, Developer Rejected, Rejected or Metadata Rejected). @@ -212,153 +220,155 @@ lane :release_appstore do |options| to update metadata manually. 4. Use upload_metadata lane to only handle metadata (without building the release and uploading a build): $ bundle exec fastlane upload_metadata - } + } + end end -end - -desc 'Updates App Store metadata' -lane :upload_metadata do |options| - deliver(common_deliver_arguments.merge(options).merge({ - skip_binary_upload: true, - skip_metadata: false, - version_check_wait_retry_limit: 1 - })) -end -desc 'Makes App Store release build and uploads it to TestFlight' -lane :release_testflight do - build_release + desc 'Updates App Store metadata' + lane :upload_metadata do |options| + deliver(common_deliver_arguments.merge(options).merge({ + skip_binary_upload: true, + skip_metadata: false, + version_check_wait_retry_limit: 1 + })) + end - upload_to_testflight( - api_key: get_api_key - ) -end + desc 'Makes App Store release build and uploads it to TestFlight' + lane :release_testflight do + build_release -desc 'Makes Alpha release build and uploads it to TestFlight' -lane :release_alpha do |options| - build_alpha(options) + upload_to_testflight( + api_key: get_api_key + ) + end - upload_to_testflight( - api_key: get_api_key, - groups: options[:groups], - skip_waiting_for_build_processing: true - ) -end + desc 'Makes Alpha release build and uploads it to TestFlight' + lane :release_alpha do |options| + build_alpha(options) -desc 'Latest build number for version' -lane :latest_build_number_for_version do |options| - if options[:app_identifier] - app_identifier = options[:app_identifier] + upload_to_testflight( + api_key: get_api_key, + groups: options[:groups], + skip_waiting_for_build_processing: true + ) end - build_number = latest_testflight_build_number( - api_key: get_api_key, - version: options[:version], - initial_build_number: -1, - username: get_username(options)) - if options[:file_name] - File.write(options[:file_name], build_number) + + desc 'Latest build number for version' + lane :latest_build_number_for_version do |options| + if options[:app_identifier] + app_identifier = options[:app_identifier] + end + build_number = latest_testflight_build_number( + api_key: get_api_key, + version: options[:version], + initial_build_number: -1, + username: get_username(options)) + if options[:file_name] + File.write(options[:file_name], build_number) + end end -end -desc 'Increment build number based on version in App Store Connect' -lane :increment_build_number_for_version do |options| - if options[:app_identifier] - app_identifier = options[:app_identifier] + desc 'Increment build number based on version in App Store Connect' + lane :increment_build_number_for_version do |options| + if options[:app_identifier] + app_identifier = options[:app_identifier] + end + increment_build_number({ + build_number: + latest_testflight_build_number( + api_key: get_api_key, + version: options[:version], + app_identifier: app_identifier, + initial_build_number: -1, + username: get_username(options)) + 1, + skip_info_plist: "true" + }) end - increment_build_number({ - build_number: - latest_testflight_build_number( - api_key: get_api_key, - version: options[:version], - app_identifier: app_identifier, - initial_build_number: -1, - username: get_username(options)) + 1, - skip_info_plist: "true" - }) -end -################################################# -# Private lanes -################################################# + ################################################# + # Private lanes + ################################################# -private_lane :build_release do |options| - sync_signing(options) + private_lane :build_release do |options| + sync_signing(options) - build_app( - export_method: "app-store", - scheme: "DuckDuckGo", - export_options: "appStoreExportOptions.plist", - derived_data_path: "DerivedData", - xcargs: "-skipPackagePluginValidation -skipMacroValidation" - ) -end + build_app( + export_method: "app-store", + scheme: "DuckDuckGo", + export_options: "appStoreExportOptions.plist", + derived_data_path: "DerivedData", + xcargs: "-skipPackagePluginValidation -skipMacroValidation" + ) + end -private_lane :build_alpha do |options| - sync_signing_alpha(options) - - build_app( - export_method: "app-store", - configuration: "Alpha", - scheme: "DuckDuckGo-Alpha", - export_options: "alphaExportOptions.plist", - derived_data_path: "DerivedData", - xcargs: "-skipPackagePluginValidation -skipMacroValidation" - ) -end + private_lane :build_alpha do |options| + sync_signing_alpha(options) -private_lane :get_api_key do - has_api_key = [ - "APPLE_API_KEY_ID", - "APPLE_API_KEY_ISSUER", - "APPLE_API_KEY_BASE64" - ].map {|x| ENV.has_key? x}.reduce(&:&) - - if has_api_key - app_store_connect_api_key( - key_id: ENV["APPLE_API_KEY_ID"], - issuer_id: ENV["APPLE_API_KEY_ISSUER"], - key_content: ENV["APPLE_API_KEY_BASE64"], - is_key_content_base64: true + build_app( + export_method: "app-store", + configuration: "Alpha", + scheme: "DuckDuckGo-Alpha", + export_options: "alphaExportOptions.plist", + derived_data_path: "DerivedData", + xcargs: "-skipPackagePluginValidation -skipMacroValidation" ) - else - nil end -end -private_lane :get_username do |options| - if options[:username] - options[:username] - elsif is_ci - nil # don't make assumptions in CI - else - git_user_email = Action.sh("git", "config", "user.email").chomp - if git_user_email.end_with? "@duckduckgo.com" - git_user_email + private_lane :get_api_key do + has_api_key = [ + "APPLE_API_KEY_ID", + "APPLE_API_KEY_ISSUER", + "APPLE_API_KEY_BASE64" + ].map {|x| ENV.has_key? x}.reduce(&:&) + + if has_api_key + app_store_connect_api_key( + key_id: ENV["APPLE_API_KEY_ID"], + issuer_id: ENV["APPLE_API_KEY_ISSUER"], + key_content: ENV["APPLE_API_KEY_BASE64"], + is_key_content_base64: true + ) + else + nil end end -end -private_lane :do_sync_signing do |options| - is_adhoc = lane_context[SharedValues::LANE_NAME].include?("adhoc") - sync_code_signing( - api_key: get_api_key, - username: get_username(options), - readonly: is_ci && !is_adhoc - ) -end + private_lane :get_username do |options| + if options[:username] + options[:username] + elsif is_ci + nil # don't make assumptions in CI + else + git_user_email = Action.sh("git", "config", "user.email").chomp + if git_user_email.end_with? "@duckduckgo.com" + git_user_email + end + end + end -def common_deliver_arguments - { - api_key: get_api_key, - submit_for_review: false, - automatic_release: false, - phased_release: true, - force: true, - skip_screenshots: true, - skip_metadata: true, - precheck_include_in_app_purchases: false, - submission_information: { - add_id_info_uses_idfa: false + private_lane :do_sync_signing do |options| + is_adhoc = lane_context[SharedValues::LANE_NAME].include?("adhoc") + sync_code_signing( + api_key: get_api_key, + username: get_username(options), + readonly: is_ci && !is_adhoc + ) + end + + def common_deliver_arguments + { + api_key: get_api_key, + submit_for_review: false, + automatic_release: false, + phased_release: true, + force: true, + skip_screenshots: true, + skip_metadata: true, + precheck_include_in_app_purchases: false, + submission_information: { + add_id_info_uses_idfa: false + } } - } + end + end diff --git a/fastlane/Matchfile b/fastlane/Matchfile index 022ff22969..f577d97171 100644 --- a/fastlane/Matchfile +++ b/fastlane/Matchfile @@ -4,7 +4,7 @@ git_branch "ios" platform "ios" type "appstore" -app_identifier ["com.duckduckgo.mobile.ios", "com.duckduckgo.mobile.ios.ShareExtension", "com.duckduckgo.mobile.ios.OpenAction2", "com.duckduckgo.mobile.ios.Widgets", "com.duckduckgo.mobile.ios.NetworkExtension"] +app_identifier ["com.duckduckgo.mobile.ios", "com.duckduckgo.mobile.ios.ShareExtension", "com.duckduckgo.mobile.ios.OpenAction2", "com.duckduckgo.mobile.ios.Widgets", "com.duckduckgo.mobile.ios.NetworkExtension", "com.duckduckgo.mobile.ios.CredentialExtension"] generate_apple_certs false for_lane :sync_signing_adhoc do @@ -14,14 +14,14 @@ end for_lane :sync_signing_alpha_adhoc do type "adhoc" - app_identifier ["com.duckduckgo.mobile.ios.alpha", "com.duckduckgo.mobile.ios.alpha.ShareExtension", "com.duckduckgo.mobile.ios.alpha.OpenAction2", "com.duckduckgo.mobile.ios.alpha.Widgets", "com.duckduckgo.mobile.ios.alpha.NetworkExtension"] + app_identifier ["com.duckduckgo.mobile.ios.alpha", "com.duckduckgo.mobile.ios.alpha.ShareExtension", "com.duckduckgo.mobile.ios.alpha.OpenAction2", "com.duckduckgo.mobile.ios.alpha.Widgets", "com.duckduckgo.mobile.ios.alpha.NetworkExtension", "com.duckduckgo.mobile.ios.alpha.CredentialExtension"] force_for_new_devices true template_name "Default Web Browser iOS (Dist)" end for_lane :adhoc do type "adhoc" - app_identifier ["com.duckduckgo.mobile.ios.alpha", "com.duckduckgo.mobile.ios.alpha.ShareExtension", "com.duckduckgo.mobile.ios.alpha.OpenAction2", "com.duckduckgo.mobile.ios.alpha.Widgets", "com.duckduckgo.mobile.ios.alpha.NetworkExtension"] + app_identifier ["com.duckduckgo.mobile.ios.alpha", "com.duckduckgo.mobile.ios.alpha.ShareExtension", "com.duckduckgo.mobile.ios.alpha.OpenAction2", "com.duckduckgo.mobile.ios.alpha.Widgets", "com.duckduckgo.mobile.ios.alpha.NetworkExtension", "com.duckduckgo.mobile.ios.alpha.CredentialExtension"] force_for_new_devices true template_name "Default Web Browser iOS (Dist)" end @@ -34,17 +34,17 @@ end for_lane :alpha_adhoc do type "adhoc" - app_identifier ["com.duckduckgo.mobile.ios.alpha", "com.duckduckgo.mobile.ios.alpha.ShareExtension", "com.duckduckgo.mobile.ios.alpha.OpenAction2", "com.duckduckgo.mobile.ios.alpha.Widgets", "com.duckduckgo.mobile.ios.alpha.NetworkExtension"] + app_identifier ["com.duckduckgo.mobile.ios.alpha", "com.duckduckgo.mobile.ios.alpha.ShareExtension", "com.duckduckgo.mobile.ios.alpha.OpenAction2", "com.duckduckgo.mobile.ios.alpha.Widgets", "com.duckduckgo.mobile.ios.alpha.NetworkExtension", "com.duckduckgo.mobile.ios.alpha.CredentialExtension"] force_for_new_devices true template_name "Default Web Browser iOS (Dist)" end for_lane :sync_signing_alpha do - app_identifier ["com.duckduckgo.mobile.ios.alpha", "com.duckduckgo.mobile.ios.alpha.ShareExtension", "com.duckduckgo.mobile.ios.alpha.OpenAction2", "com.duckduckgo.mobile.ios.alpha.Widgets", "com.duckduckgo.mobile.ios.alpha.NetworkExtension"] + app_identifier ["com.duckduckgo.mobile.ios.alpha", "com.duckduckgo.mobile.ios.alpha.ShareExtension", "com.duckduckgo.mobile.ios.alpha.OpenAction2", "com.duckduckgo.mobile.ios.alpha.Widgets", "com.duckduckgo.mobile.ios.alpha.NetworkExtension", "com.duckduckgo.mobile.ios.alpha.CredentialExtension"] template_name "Default Web Browser iOS (Dist)" end for_lane :release_alpha do - app_identifier ["com.duckduckgo.mobile.ios.alpha", "com.duckduckgo.mobile.ios.alpha.ShareExtension", "com.duckduckgo.mobile.ios.alpha.OpenAction2", "com.duckduckgo.mobile.ios.alpha.Widgets", "com.duckduckgo.mobile.ios.alpha.NetworkExtension"] + app_identifier ["com.duckduckgo.mobile.ios.alpha", "com.duckduckgo.mobile.ios.alpha.ShareExtension", "com.duckduckgo.mobile.ios.alpha.OpenAction2", "com.duckduckgo.mobile.ios.alpha.Widgets", "com.duckduckgo.mobile.ios.alpha.NetworkExtension", "com.duckduckgo.mobile.ios.alpha.CredentialExtension"] template_name "Default Web Browser iOS (Dist)" end diff --git a/fastlane/metadata/default/release_notes.txt b/fastlane/metadata/default/release_notes.txt index 098fd1666f..58bb2b4a63 100644 --- a/fastlane/metadata/default/release_notes.txt +++ b/fastlane/metadata/default/release_notes.txt @@ -1 +1 @@ -- Bug fixes and other improvements. \ No newline at end of file + - If you use dark mode, you'll notice the app icon has an improved look, whether you've stuck with the classic orange or picked a custom icon color. \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 1ce6b11107..35881dad6d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "name": "ios", "version": "1.0.0", "dependencies": { - "@duckduckgo/autoconsent": "^12.1.0" + "@duckduckgo/autoconsent": "^12.3.0" }, "devDependencies": { "@rollup/plugin-json": "^4.1.0", @@ -121,9 +121,9 @@ } }, "node_modules/@duckduckgo/autoconsent": { - "version": "12.1.0", - "resolved": "https://registry.npmjs.org/@duckduckgo/autoconsent/-/autoconsent-12.1.0.tgz", - "integrity": "sha512-tWWYDYyzNKR2L1eah2FdIvytd5+4ymAWBwdAsd3WL22txfZ3iqsQgSZGMkFZSC1NMtamVjFudadyTCi/MqWHbA==", + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/@duckduckgo/autoconsent/-/autoconsent-12.3.0.tgz", + "integrity": "sha512-mOW11Ve9DRKDkjFtAjXAP3Jd5E22YqI+4+6NTPNnOud2/02QyhM8l1vK//hTR6cmXXWZiFknpuv6qckuwMKivw==", "license": "MPL-2.0", "dependencies": { "@ghostery/adblocker": "^2.0.4", diff --git a/package.json b/package.json index 2ab5a58d7c..fa1f5cef7e 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,6 @@ "rollup-plugin-terser": "^7.0.2" }, "dependencies": { - "@duckduckgo/autoconsent": "^12.1.0" + "@duckduckgo/autoconsent": "^12.3.0" } }