diff --git a/.github/workflows/firefox-ios-ui-tests.yml b/.github/workflows/firefox-ios-ui-tests.yml index 3b71fc9270ca..437c06a105ac 100644 --- a/.github/workflows/firefox-ios-ui-tests.yml +++ b/.github/workflows/firefox-ios-ui-tests.yml @@ -40,7 +40,8 @@ jobs: -scheme ${{ env.xcodebuild_scheme }} \ -target ${{ env.xcodebuild_target }} \ -derivedDataPath ~/DerivedData \ - -destination 'platform=iOS Simulator,name=${{ env.ios_simulator_default }},OS=${{ env.ios_version }}' + -destination 'platform=iOS Simulator,name=${{ env.ios_simulator_default }},OS=${{ env.ios_version }}' \ + COMPILER_INDEX_STORE_ENABLE=NO CODE_SIGN_IDENTITY= CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO ARCHS="arm64" working-directory: ${{ env.browser }} - name: Compress Derived Data id: compress-dd @@ -61,7 +62,7 @@ jobs: strategy: fail-fast: false matrix: - ios_simulator: ['iPhone 15 Plus'] + ios_simulator: ['iPhone 15 Plus', 'iPad Pro 13-inch (M4)'] steps: - name: Check out source code uses: actions/checkout@v4.1.7 @@ -70,7 +71,7 @@ jobs: run: | gem install xcpretty -v 0.3.0 pip install blockkit==1.9.1 - npm install -g junit-report-merger@7.0.0 + npm install -g junit-report-merger@7.0.0 - name: Setup Xcode id: xcode run: | @@ -99,7 +100,7 @@ jobs: -destination 'platform=iOS Simulator,name=${{ matrix.ios_simulator }},OS=${{ env.ios_version }}' \ -testPlan Smoketest1 \ -resultBundlePath ${{ env.test_results_directory }}/results-smoketest1 \ - | tee xcodebuild.log | xcpretty -r junit --output ./junit-smoketest1.xml && exit ${PIPESTATUS[0]} + | tee xcodebuild-smoketest1.log | xcpretty -r junit --output ./junit-smoketest1.xml && exit ${PIPESTATUS[0]} working-directory: ${{ env.browser }} continue-on-error: true - name: Run Smoketest2 @@ -113,7 +114,7 @@ jobs: -destination 'platform=iOS Simulator,name=${{ matrix.ios_simulator }},OS=${{ env.ios_version }}' \ -testPlan Smoketest2 \ -resultBundlePath ${{ env.test_results_directory }}/results-smoketest2 \ - | tee xcodebuild.log | xcpretty -r junit --output ./junit-smoketest2.xml && exit ${PIPESTATUS[0]} + | tee xcodebuild-smoketest2.log | xcpretty -r junit --output ./junit-smoketest2.xml && exit ${PIPESTATUS[0]} working-directory: ${{ env.browser }} continue-on-error: true - name: Run Smoketest3 @@ -127,7 +128,7 @@ jobs: -destination 'platform=iOS Simulator,name=${{ matrix.ios_simulator }},OS=${{ env.ios_version }}' \ -testPlan Smoketest3 \ -resultBundlePath ${{ env.test_results_directory }}/results-smoketest3 \ - | tee xcodebuild.log | xcpretty -r junit --output ./junit-smoketest3.xml && exit ${PIPESTATUS[0]} + | tee xcodebuild-smoketest3.log | xcpretty -r junit --output ./junit-smoketest3.xml && exit ${PIPESTATUS[0]} working-directory: ${{ env.browser }} continue-on-error: true - name: Run Smoketest4 @@ -141,7 +142,7 @@ jobs: -destination 'platform=iOS Simulator,name=${{ matrix.ios_simulator }},OS=${{ env.ios_version }}' \ -testPlan Smoketest4 \ -resultBundlePath ${{ env.test_results_directory }}/results-smoketest4 \ - | tee xcodebuild.log | xcpretty -r junit --output ./junit-smoketest4.xml && exit ${PIPESTATUS[0]} + | tee xcodebuild-smoketest4.log | xcpretty -r junit --output ./junit-smoketest4.xml && exit ${PIPESTATUS[0]} working-directory: ${{ env.browser }} continue-on-error: true - name: Determine pass/fail status @@ -167,20 +168,21 @@ jobs: python ../test-fixtures/ci/convert_junit_to_markdown.py --smoke --github --${{ env.browser }} ./combined.xml ./github.md cat github.md >> $GITHUB_STEP_SUMMARY python ../test-fixtures/ci/convert_junit_to_markdown.py --smoke --slack --${{ env.browser }} ./combined.xml ./slack.json + mv ./combined.xml "junit-smoketests-${{ matrix.ios_simulator }}-`date +"%Y-%m-%d"`.xml" working-directory: ${{ env.browser }} - name: Upload junit files id: upload-junit uses: actions/upload-artifact@v4.3.3 with: name: ${{ env.browser }}-smoketests-${{ matrix.ios_simulator }}-junit-${{ github.run_number }} - path: ${{ env.browser }}/junit-*.xml + path: ${{ env.browser }}/junit-smoketests-*.xml retention-days: 90 - name: Upload log file id: upload-log uses: actions/upload-artifact@v4.3.3 with: name: ${{ env.browser }}-smoketests-${{ matrix.ios_simulator }}-xcodebuildlog-${{ github.run_number }} - path: ${{ env.browser }}/xcodebuild.log + path: ${{ env.browser }}/xcodebuild-smoketest*.log retention-days: 90 - name: Report to Slack id: slack @@ -203,81 +205,83 @@ jobs: exit ${{ steps.passfail.outcome == 'success' && '0' || '1' }} run-fullfunctionaltests: - name: Run full functional tests - runs-on: macos-14 - needs: compile - strategy: - fail-fast: false - matrix: - ios_simulator: ['iPhone 15 Plus'] - steps: - - name: Check out source code - uses: actions/checkout@v4.1.7 - - name: Install packages - id: packages - run: | - gem install xcpretty -v 0.3.0 - pip install blockkit==1.9.1 - - name: Get derived data - id: download-derived-data - uses: actions/download-artifact@v4 - with: - name: xcode-cache-deriveddata-${{ github.workflow }}-${{ github.sha }} - - name: Decompress derived data - id: decompress-dd - run: | - tar xzf derived-data.tar.gz -C / - - name: Run tests - id: run-tests - run: | - xcodebuild \ - test-without-building \ - -scheme ${{ env.xcodebuild_scheme }} \ - -target ${{ env.xcodebuild_target }} \ - -derivedDataPath ~/DerivedData \ - -destination 'platform=iOS Simulator,name=${{ matrix.ios_simulator }},OS=${{ env.ios_version }}' \ - -testPlan FullFunctionalTestPlan \ - -resultBundlePath ${{ env.test_results_directory }}/results \ - | tee xcodebuild.log | xcpretty -r junit && exit ${PIPESTATUS[0]} - working-directory: ${{ env.browser }} - continue-on-error: true - - name: Print test report - id: test-report - run: | - python ../test-fixtures/ci/convert_junit_to_markdown.py --github --${{ env.browser }} ./build/reports/junit.xml ./github.md - cat github.md >> $GITHUB_STEP_SUMMARY - python ../test-fixtures/ci/convert_junit_to_markdown.py --slack --${{ env.browser }} ./build/reports/junit.xml ./slack.json - working-directory: ${{ env.browser }} - - name: Upload junit files - id: upload-junit - uses: actions/upload-artifact@v4.3.3 - with: - name: ${{ env.browser }}-fullfunctional-${{ matrix.ios_simulator }}-junit-${{ github.run_number }} - path: ${{ env.browser }}/junit-*.xml - retention-days: 90 - - name: Upload log file - id: upload-log - uses: actions/upload-artifact@v4.3.3 - with: - name: ${{ env.browser }}-fullfunctional-${{ matrix.ios_simulator }}-xcodebuildlog-${{ github.run_number }} - path: ${{ env.browser }}/xcodebuild.log - retention-days: 90 - - name: Report to Slack - id: slack - uses: slackapi/slack-github-action@v1.26.0 - with: - payload-file-path: ${{ env.browser }}/slack.json - env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} - SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK - ios_simulator: ${{ matrix.ios_simulator }} - pass_fail: ${{ steps.run-tests.outcome == 'success' && ':white_check_mark:' || ':x:' }} - xcodebuild_test_plan: FullFunctionalTestPlan - ref_name: ${{ github.ref_name }} - repository: ${{ github.repository }} - run_id: ${{ github.run_id }} - server_url: ${{ github.server_url }} - sha: ${{ github.sha }} - - name: Return fail status if a test fails - run: | - exit ${{ steps.run-tests.outcome == 'success' && '0' || '1' }} \ No newline at end of file + name: Run full functional tests + runs-on: macos-14 + needs: compile + strategy: + fail-fast: false + matrix: + ios_simulator: ['iPhone 15 Pro Max'] + steps: + - name: Check out source code + uses: actions/checkout@v4.1.7 + - name: Install packages + id: packages + run: | + gem install xcpretty -v 0.3.0 + pip install blockkit==1.9.1 + - name: Get derived data + id: download-derived-data + uses: actions/download-artifact@v4 + with: + name: xcode-cache-deriveddata-${{ github.workflow }}-${{ github.sha }} + - name: Decompress derived data + id: decompress-dd + run: | + tar xzf derived-data.tar.gz -C / + - name: Run tests + id: run-tests + run: | + xcrun simctl boot '${{ matrix.ios_simulator }}' + xcodebuild \ + test-without-building \ + -scheme ${{ env.xcodebuild_scheme }} \ + -target ${{ env.xcodebuild_target }} \ + -derivedDataPath ~/DerivedData \ + -destination 'platform=iOS Simulator,name=${{ matrix.ios_simulator }},OS=${{ env.ios_version }}' \ + -testPlan FullFunctionalTestPlan \ + -resultBundlePath ${{ env.test_results_directory }}/results \ + | tee xcodebuild.log | xcpretty -r junit && exit ${PIPESTATUS[0]} + working-directory: ${{ env.browser }} + continue-on-error: true + - name: Print test report + id: test-report + run: | + python ../test-fixtures/ci/convert_junit_to_markdown.py --github --${{ env.browser }} ./build/reports/junit.xml ./github.md + cat github.md >> $GITHUB_STEP_SUMMARY + python ../test-fixtures/ci/convert_junit_to_markdown.py --slack --${{ env.browser }} ./build/reports/junit.xml ./slack.json + mv ./build/reports/junit.xml "junit-fullfunctional-${{ matrix.ios_simulator }}-`date +"%Y-%m-%d"`.xml" + working-directory: ${{ env.browser }} + - name: Upload junit files + id: upload-junit + uses: actions/upload-artifact@v4.3.3 + with: + name: ${{ env.browser }}-fullfunctional-${{ matrix.ios_simulator }}-junit-${{ github.run_number }} + path: ${{ env.browser }}/junit-*.xml + retention-days: 90 + - name: Upload log file + id: upload-log + uses: actions/upload-artifact@v4.3.3 + with: + name: ${{ env.browser }}-fullfunctional-${{ matrix.ios_simulator }}-xcodebuildlog-${{ github.run_number }} + path: ${{ env.browser }}/xcodebuild.log + retention-days: 90 + - name: Report to Slack + id: slack + uses: slackapi/slack-github-action@v1.26.0 + with: + payload-file-path: ${{ env.browser }}/slack.json + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} + SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK + ios_simulator: ${{ matrix.ios_simulator }} + pass_fail: ${{ steps.run-tests.outcome == 'success' && ':white_check_mark:' || ':x:' }} + xcodebuild_test_plan: FullFunctionalTestPlan + ref_name: ${{ github.ref_name }} + repository: ${{ github.repository }} + run_id: ${{ github.run_id }} + server_url: ${{ github.server_url }} + sha: ${{ github.sha }} + - name: Return fail status if a test fails + run: | + exit ${{ steps.run-tests.outcome == 'success' && '0' || '1' }} \ No newline at end of file diff --git a/.swiftlint.yml b/.swiftlint.yml index 760c7222b5a7..f24c1f9155fa 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -82,6 +82,8 @@ only_rules: # Only enforce these rules, ignore all others - vertical_whitespace_closing_braces - vertical_whitespace_opening_braces - yoda_condition + - accessibility_label_for_image + - accessibility_trait_for_button # These rules were originally opted into. Disabling for now to get # Swiftlint up and running. diff --git a/.taskcluster.yml b/.taskcluster.yml index d053e622797a..d6aa1d32a9ac 100644 --- a/.taskcluster.yml +++ b/.taskcluster.yml @@ -108,7 +108,7 @@ tasks: $if: > tasks_for in ["action", "cron"] || (isPullRequest && pullRequestAction in ["opened", "reopened", "synchronize"]) - || (tasks_for == "github-push" && (short_head_branch == "main" || short_head_branch[:8] == "releases")) + || (tasks_for == "github-push" && short_head_branch == "main") || (tasks_for == "github-release" && releaseAction == "published") then: taskId: diff --git a/BrowserKit/Package.resolved b/BrowserKit/Package.resolved index 0d8d7a515d83..c97fbc9996a9 100644 --- a/BrowserKit/Package.resolved +++ b/BrowserKit/Package.resolved @@ -41,8 +41,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/getsentry/sentry-cocoa.git", "state" : { - "revision" : "38f4f70d07117b9f958a76b1bff278c2f29ffe0e", - "version" : "8.21.0" + "revision" : "d2ced2d961b34573ebd2ea0567a2f1408e90f0ae", + "version" : "8.34.0" } }, { diff --git a/BrowserKit/Package.swift b/BrowserKit/Package.swift index 107755d44bd9..717844cf1b8a 100644 --- a/BrowserKit/Package.swift +++ b/BrowserKit/Package.swift @@ -30,6 +30,9 @@ let package = Package( .library( name: "ToolbarKit", targets: ["ToolbarKit"]), + .library( + name: "MenuKit", + targets: ["MenuKit"]), .library( name: "ContentBlockingGenerator", targets: ["ContentBlockingGenerator"]), @@ -52,7 +55,7 @@ let package = Package( exact: "2.0.0"), .package( url: "https://github.com/getsentry/sentry-cocoa.git", - exact: "8.21.0"), + exact: "8.34.0"), .package(url: "https://github.com/nbhasin2/GCDWebServer.git", branch: "master") ], @@ -109,6 +112,13 @@ let package = Package( .testTarget( name: "ToolbarKitTests", dependencies: ["ToolbarKit"]), + .target( + name: "MenuKit", + dependencies: ["Common"], + swiftSettings: [.unsafeFlags(["-enable-testing"])]), + .testTarget( + name: "MenuKitTests", + dependencies: ["MenuKit"]), .target( name: "ContentBlockingGenerator", swiftSettings: [.unsafeFlags(["-enable-testing"])]), diff --git a/BrowserKit/Sources/Common/Constants/StandardImageIdentifiers.swift b/BrowserKit/Sources/Common/Constants/StandardImageIdentifiers.swift index 558e24127856..a94b3e8106a8 100644 --- a/BrowserKit/Sources/Common/Constants/StandardImageIdentifiers.swift +++ b/BrowserKit/Sources/Common/Constants/StandardImageIdentifiers.swift @@ -10,6 +10,7 @@ import Foundation public struct StandardImageIdentifiers { // Icon size 16x16 public struct Small { + public static let externalLink = "externalLinkSmall" public static let notificationDot = "notificationDotSmall" public static let notificationDotFill = "notificationDotFillSmall" public static let pinBadgeFill = "pinBadgeFillSmall" @@ -95,7 +96,7 @@ public struct StandardImageIdentifiers { public static let readingListAdd = "readingListAddLarge" public static let search = "searchLarge" public static let settings = "settingsLarge" - public static let shareApple = "shareAppleLarge" + public static let share = "shareLarge" public static let shield = "shieldLarge" public static let shieldSlash = "shieldSlashLarge" public static let shipping = "shippingLarge" diff --git a/BrowserKit/Sources/Common/Extensions/StringExtension.swift b/BrowserKit/Sources/Common/Extensions/StringExtension.swift index a2e9b0335c30..6894e206fca3 100644 --- a/BrowserKit/Sources/Common/Extensions/StringExtension.swift +++ b/BrowserKit/Sources/Common/Extensions/StringExtension.swift @@ -11,3 +11,15 @@ extension StringProtocol { return replacingOccurrences(of: target, with: "") } } + +public extension String { + /// Returns a new string made by removing the leading String characters contained + /// in a given character set. + func stringByTrimmingLeadingCharactersInSet(_ set: CharacterSet) -> String { + var trimmed = self + while trimmed.rangeOfCharacter(from: set)?.lowerBound == trimmed.startIndex { + trimmed.remove(at: trimmed.startIndex) + } + return trimmed + } +} diff --git a/BrowserKit/Sources/Common/Theming/DarkTheme.swift b/BrowserKit/Sources/Common/Theming/DarkTheme.swift index 94678d11f43d..1606c4972de2 100644 --- a/BrowserKit/Sources/Common/Theming/DarkTheme.swift +++ b/BrowserKit/Sources/Common/Theming/DarkTheme.swift @@ -47,6 +47,7 @@ private struct DarkColourPalette: ThemeColourPalette { var layerConfirmation: UIColor = FXColors.Green80 var layerWarning: UIColor = FXColors.Yellow70.withAlphaComponent(0.77) var layerError: UIColor = FXColors.Pink80 + var layerSelectedText: UIColor = FXColors.LightGrey05.withAlphaComponent(0.34) var layerSearch: UIColor = FXColors.DarkGrey80 var layerGradientURL = Gradient(colors: [ FXColors.DarkGrey80.withAlphaComponent(0), diff --git a/BrowserKit/Sources/Common/Theming/LightTheme.swift b/BrowserKit/Sources/Common/Theming/LightTheme.swift index 5f69658b0435..e12d4a554d78 100644 --- a/BrowserKit/Sources/Common/Theming/LightTheme.swift +++ b/BrowserKit/Sources/Common/Theming/LightTheme.swift @@ -36,6 +36,7 @@ private struct LightColourPalette: ThemeColourPalette { var layerConfirmation: UIColor = FXColors.Green20 var layerWarning: UIColor = FXColors.Yellow20 var layerError: UIColor = FXColors.Red10 + var layerSelectedText: UIColor = FXColors.DarkGrey05.withAlphaComponent(0.73) var layerSearch: UIColor = FXColors.LightGrey30 var layerGradientURL = Gradient(colors: [ FXColors.LightGrey30.withAlphaComponent(0), diff --git a/BrowserKit/Sources/Common/Theming/PrivateModeTheme.swift b/BrowserKit/Sources/Common/Theming/PrivateModeTheme.swift index fd3fa90d0e7b..38d3d1d25426 100644 --- a/BrowserKit/Sources/Common/Theming/PrivateModeTheme.swift +++ b/BrowserKit/Sources/Common/Theming/PrivateModeTheme.swift @@ -38,6 +38,7 @@ private struct PrivateModeColorPalette: ThemeColourPalette { var layerConfirmation: UIColor = FXColors.Green80 var layerWarning: UIColor = FXColors.Yellow70.withAlphaComponent(0.77) var layerError: UIColor = FXColors.Pink80 + var layerSelectedText: UIColor = FXColors.Violet60 var layerSearch: UIColor = FXColors.Ink90 var layerGradientURL = Gradient(colors: [ FXColors.Ink90.withAlphaComponent(0), diff --git a/BrowserKit/Sources/Common/Theming/ThemeColourPalette.swift b/BrowserKit/Sources/Common/Theming/ThemeColourPalette.swift index 9ecb5da8aecc..c627c495ec95 100644 --- a/BrowserKit/Sources/Common/Theming/ThemeColourPalette.swift +++ b/BrowserKit/Sources/Common/Theming/ThemeColourPalette.swift @@ -27,6 +27,7 @@ public protocol ThemeColourPalette { var layerConfirmation: UIColor { get } var layerWarning: UIColor { get } var layerError: UIColor { get } + var layerSelectedText: UIColor { get } var layerSearch: UIColor { get } var layerGradientURL: Gradient { get } diff --git a/BrowserKit/Sources/ComponentLibrary/BottomSheet/BottomSheetViewController.swift b/BrowserKit/Sources/ComponentLibrary/BottomSheet/BottomSheetViewController.swift index 85fda3bc2af9..8b361322e922 100644 --- a/BrowserKit/Sources/ComponentLibrary/BottomSheet/BottomSheetViewController.swift +++ b/BrowserKit/Sources/ComponentLibrary/BottomSheet/BottomSheetViewController.swift @@ -60,11 +60,13 @@ public class BottomSheetViewController: UIViewController, private lazy var scrollContentView: UIView = .build { _ in } private var contentViewBottomConstraint: NSLayoutConstraint! private var viewTranslation = CGPoint(x: 0, y: 0) + private let windowUUID: WindowUUID // MARK: Init public init(viewModel: BottomSheetViewModel, childViewController: BottomSheetChild, usingDimmedBackground: Bool = false, + windowUUID: WindowUUID, notificationCenter: NotificationProtocol = NotificationCenter.default, themeManager: ThemeManager = AppContainer.shared.resolve()) { self.viewModel = viewModel @@ -72,6 +74,7 @@ public class BottomSheetViewController: UIViewController, self.notificationCenter = notificationCenter self.themeManager = themeManager self.useDimmedBackground = usingDimmedBackground + self.windowUUID = windowUUID super.init(nibName: nil, bundle: nil) @@ -136,8 +139,7 @@ public class BottomSheetViewController: UIViewController, // MARK: - Theme public func applyTheme() { - guard let uuid = (self.view as? ThemeUUIDIdentifiable)?.currentWindowUUID else { return } - contentView.backgroundColor = themeManager.getCurrentTheme(for: uuid).colors.layer1 + contentView.backgroundColor = themeManager.getCurrentTheme(for: windowUUID).colors.layer1 sheetView.layer.shadowOpacity = viewModel.shadowOpacity if useDimmedBackground { @@ -147,7 +149,7 @@ public class BottomSheetViewController: UIViewController, } public var currentWindowUUID: WindowUUID? { - return (self.view as? ThemeUUIDIdentifiable)?.currentWindowUUID + return windowUUID } // MARK: - UIGestureRecognizerDelegate diff --git a/BrowserKit/Sources/ComponentLibrary/BottomSheet/BottomSheetViewModel.swift b/BrowserKit/Sources/ComponentLibrary/BottomSheet/BottomSheetViewModel.swift index bd758995d8ee..4d3a0819c823 100644 --- a/BrowserKit/Sources/ComponentLibrary/BottomSheet/BottomSheetViewModel.swift +++ b/BrowserKit/Sources/ComponentLibrary/BottomSheet/BottomSheetViewModel.swift @@ -4,13 +4,14 @@ import Foundation import UIKit +import Common /// The view model used to configure a `BottomSheetViewController` public struct BottomSheetViewModel { - private struct UX { - static let cornerRadius: CGFloat = 8 - static let animationTransitionDuration: CGFloat = 0.3 - static let shadowOpacity: Float = 0.3 + public struct UX { + public static let cornerRadius: CGFloat = 8 + public static let animationTransitionDuration: CGFloat = 0.3 + public static let shadowOpacity: Float = 0.3 } public var cornerRadius: CGFloat @@ -21,23 +22,15 @@ public struct BottomSheetViewModel { public var closeButtonA11yLabel: String public var closeButtonA11yIdentifier: String - public init(closeButtonA11yLabel: String, closeButtonA11yIdentifier: String) { - cornerRadius = BottomSheetViewModel.UX.cornerRadius - animationTransitionDuration = BottomSheetViewModel.UX.animationTransitionDuration - backgroundColor = .clear - shouldDismissForTapOutside = true - shadowOpacity = BottomSheetViewModel.UX.shadowOpacity - self.closeButtonA11yLabel = closeButtonA11yLabel - self.closeButtonA11yIdentifier = closeButtonA11yIdentifier - } - - public init(cornerRadius: CGFloat, - animationTransitionDuration: TimeInterval, - backgroundColor: UIColor, - shouldDismissForTapOutside: Bool, - shadowOpacity: Float, - closeButtonA11yLabel: String, - closeButtonA11yIdentifier: String) { + public init( + cornerRadius: CGFloat = BottomSheetViewModel.UX.cornerRadius, + animationTransitionDuration: TimeInterval = BottomSheetViewModel.UX.animationTransitionDuration, + backgroundColor: UIColor = .clear, + shouldDismissForTapOutside: Bool = true, + shadowOpacity: Float = BottomSheetViewModel.UX.shadowOpacity, + closeButtonA11yLabel: String, + closeButtonA11yIdentifier: String + ) { self.cornerRadius = cornerRadius self.animationTransitionDuration = animationTransitionDuration self.backgroundColor = backgroundColor diff --git a/BrowserKit/Sources/MenuKit/MenuElement.swift b/BrowserKit/Sources/MenuKit/MenuElement.swift new file mode 100644 index 000000000000..cd0a62284134 --- /dev/null +++ b/BrowserKit/Sources/MenuKit/MenuElement.swift @@ -0,0 +1,33 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import UIKit + +public struct MenuElement: Equatable { + let iconName: String + let isEnabled: Bool + let isActive: Bool + let hasSubmenu: Bool + let a11yLabel: String + let a11yHint: String? + let a11yId: String + + // We need this init as by default the init generated by the compiler + // for the struct will be internal and can not be used outside of MenuKit + public init(iconName: String, + isEnabled: Bool, + isActive: Bool, + hasSubmenu: Bool, + a11yLabel: String, + a11yHint: String?, + a11yId: String) { + self.iconName = iconName + self.isEnabled = isEnabled + self.isActive = isActive + self.hasSubmenu = hasSubmenu + self.a11yLabel = a11yLabel + self.a11yHint = a11yHint + self.a11yId = a11yId + } +} diff --git a/BrowserKit/Sources/SiteImageView/ImageProcessing/HeroImageFetcher/ImageProvider.swift b/BrowserKit/Sources/SiteImageView/ImageProcessing/HeroImageFetcher/ImageProvider.swift index be856e194ffd..a22d1c67d25d 100644 --- a/BrowserKit/Sources/SiteImageView/ImageProcessing/HeroImageFetcher/ImageProvider.swift +++ b/BrowserKit/Sources/SiteImageView/ImageProcessing/HeroImageFetcher/ImageProvider.swift @@ -13,6 +13,7 @@ protocol ImageProvider { } extension NSItemProvider: ImageProvider { + @MainActor func loadObject(ofClass aClass: NSItemProviderReading.Type) async throws -> UIImage { return try await withCheckedThrowingContinuation { continuation in loadObject(ofClass: aClass) { image, error in diff --git a/BrowserKit/Sources/TabDataStore/TabSessionStore.swift b/BrowserKit/Sources/TabDataStore/TabSessionStore.swift index 4e15de0cd2ba..59ce84658605 100644 --- a/BrowserKit/Sources/TabDataStore/TabSessionStore.swift +++ b/BrowserKit/Sources/TabDataStore/TabSessionStore.swift @@ -10,21 +10,22 @@ public protocol TabSessionStore { /// - Parameters: /// - tabID: an ID that uniquely identifies the tab /// - sessionData: the data associated with a session, encoded as a Data object - func saveTabSession(tabID: UUID, sessionData: Data) async + func saveTabSession(tabID: UUID, sessionData: Data) /// Fetches the session data associated with a tab /// - Parameter tabID: an ID that uniquely identifies the tab /// - Returns: the data associated with a session, encoded as a Data object - func fetchTabSession(tabID: UUID) async -> Data? + func fetchTabSession(tabID: UUID) -> Data? /// Cleans up any tab session data files for tabs that are no longer open. func deleteUnusedTabSessionData(keeping: [UUID]) async } -public actor DefaultTabSessionStore: TabSessionStore { +public class DefaultTabSessionStore: TabSessionStore { let fileManager: TabFileManager let logger: Logger let filePrefix = "tab-" + private let lock = NSRecursiveLock() public init(fileManager: TabFileManager = DefaultTabFileManager(), logger: Logger = DefaultLogger.shared) { @@ -32,7 +33,7 @@ public actor DefaultTabSessionStore: TabSessionStore { self.logger = logger } - public func saveTabSession(tabID: UUID, sessionData: Data) async { + public func saveTabSession(tabID: UUID, sessionData: Data) { guard let directory = fileManager.tabSessionDataDirectory() else { return } if !fileManager.fileExists(atPath: directory) { @@ -41,6 +42,8 @@ public actor DefaultTabSessionStore: TabSessionStore { let path = directory.appendingPathComponent(filePrefix + tabID.uuidString) do { + lock.lock() + defer { lock.unlock() } try sessionData.write(to: path, options: .atomicWrite) } catch { logger.log("Failed to save session data with error: \(error.localizedDescription)", @@ -49,11 +52,13 @@ public actor DefaultTabSessionStore: TabSessionStore { } } - public func fetchTabSession(tabID: UUID) async -> Data? { + public func fetchTabSession(tabID: UUID) -> Data? { guard let path = fileManager.tabSessionDataDirectory()?.appendingPathComponent(filePrefix + tabID.uuidString) else { return nil } do { + lock.lock() + defer { lock.unlock() } return try Data(contentsOf: path) } catch { logger.log("Failed to decode session data with error: \(error.localizedDescription)", diff --git a/BrowserKit/Sources/ToolbarKit/AddressToolbar/AddressToolbar.swift b/BrowserKit/Sources/ToolbarKit/AddressToolbar/AddressToolbar.swift index 6a9ad4848c3e..96ac8d355525 100644 --- a/BrowserKit/Sources/ToolbarKit/AddressToolbar/AddressToolbar.swift +++ b/BrowserKit/Sources/ToolbarKit/AddressToolbar/AddressToolbar.swift @@ -10,4 +10,6 @@ public protocol AddressToolbar { toolbarDelegate: AddressToolbarDelegate, leadingSpace: CGFloat?, trailingSpace: CGFloat?) + + func setAutocompleteSuggestion(_ suggestion: String?) } diff --git a/BrowserKit/Sources/ToolbarKit/AddressToolbar/AddressToolbarDelegate.swift b/BrowserKit/Sources/ToolbarKit/AddressToolbar/AddressToolbarDelegate.swift index 962705a0421f..180ecc2bb4a0 100644 --- a/BrowserKit/Sources/ToolbarKit/AddressToolbar/AddressToolbarDelegate.swift +++ b/BrowserKit/Sources/ToolbarKit/AddressToolbar/AddressToolbarDelegate.swift @@ -10,4 +10,5 @@ public protocol AddressToolbarDelegate: AnyObject { func openBrowser(searchTerm: String) func openSuggestions(searchTerm: String) func addressToolbarAccessibilityActions() -> [UIAccessibilityCustomAction]? + func configureContextualHint(_ addressToolbar: BrowserAddressToolbar, for button: UIButton) } diff --git a/BrowserKit/Sources/ToolbarKit/AddressToolbar/BrowserAddressToolbar.swift b/BrowserKit/Sources/ToolbarKit/AddressToolbar/BrowserAddressToolbar.swift index 0bcdfb5b28ae..cf21eafb72bc 100644 --- a/BrowserKit/Sources/ToolbarKit/AddressToolbar/BrowserAddressToolbar.swift +++ b/BrowserKit/Sources/ToolbarKit/AddressToolbar/BrowserAddressToolbar.swift @@ -64,8 +64,8 @@ public class BrowserAddressToolbar: UIView, AddressToolbar, ThemeApplicable, Loc toolbarDelegate: any AddressToolbarDelegate, leadingSpace: CGFloat?, trailingSpace: CGFloat?) { - configure(state: state) self.toolbarDelegate = toolbarDelegate + configure(state: state) updateSpacing(leading: leadingSpace, trailing: trailingSpace) } @@ -79,6 +79,10 @@ public class BrowserAddressToolbar: UIView, AddressToolbar, ThemeApplicable, Loc layoutIfNeeded() } + public func setAutocompleteSuggestion(_ suggestion: String?) { + locationView.setAutocompleteSuggestion(suggestion) + } + override public func becomeFirstResponder() -> Bool { return locationView.becomeFirstResponder() } @@ -214,6 +218,10 @@ public class BrowserAddressToolbar: UIView, AddressToolbar, ThemeApplicable, Loc // As we recreate the buttons we need to apply the theme for them to be displayed correctly button.applyTheme(theme: theme) } + + if toolbarElement.hasContextualHint == true { + toolbarDelegate?.configureContextualHint(self, for: button) + } } } @@ -247,6 +255,10 @@ public class BrowserAddressToolbar: UIView, AddressToolbar, ThemeApplicable, Loc } // MARK: - LocationViewDelegate + func locationViewDidRestoreSearchTerm(_ text: String) { + toolbarDelegate?.openSuggestions(searchTerm: text) + } + func locationViewDidEnterText(_ text: String) { toolbarDelegate?.searchSuggestions(searchTerm: text) } @@ -255,7 +267,7 @@ public class BrowserAddressToolbar: UIView, AddressToolbar, ThemeApplicable, Loc toolbarDelegate?.openSuggestions(searchTerm: text.lowercased()) } - func locationViewShouldSearchFor(_ text: String) { + func locationViewDidSubmitText(_ text: String) { guard !text.isEmpty else { return } toolbarDelegate?.openBrowser(searchTerm: text.lowercased()) diff --git a/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/Callback.swift b/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/Callback.swift new file mode 100644 index 000000000000..416c44b5ed72 --- /dev/null +++ b/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/Callback.swift @@ -0,0 +1,19 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +// Encapsulate a callback in a way that we can use it with NSTimer. +final class Callback { + private let handler: () -> Void + + init(handler: @escaping () -> Void) { + self.handler = handler + } + + @objc + func go() { + handler() + } +} diff --git a/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationTextField.swift b/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationTextField.swift index f785e9634040..2ea00227b2b4 100644 --- a/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationTextField.swift +++ b/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationTextField.swift @@ -5,14 +5,67 @@ import Common import UIKit -class LocationTextField: UITextField, ThemeApplicable { +/// Delegate for the text field events. Since LocationTextField owns the UITextFieldDelegate, +/// callers must use this instead. +protocol LocationTextFieldDelegate: AnyObject { + func locationTextField(_ textField: LocationTextField, didEnterText text: String) + func locationTextFieldShouldReturn(_ textField: LocationTextField) -> Bool + func locationTextFieldShouldClear(_ textField: LocationTextField) -> Bool + func locationPasteAndGo(_ textField: LocationTextField) + func locationTextFieldDidBeginEditing(_ textField: UITextField) + func locationTextFieldDidEndEditing(_ textField: UITextField) +} + +class LocationTextField: UITextField, UITextFieldDelegate, ThemeApplicable { private var tintedClearImage: UIImage? private var clearButtonTintColor: UIColor? + var autocompleteDelegate: LocationTextFieldDelegate? + + // This variable is a solution to get the right behaviour for refocusing + // the LocationTextField. The initial transition into Overlay Mode + // doesn't involve the user interacting with LocationTextField. + // Thus, we update shouldApplyCompletion in touchesBegin() to reflect whether + // the highlight is active and then the text field is updated accordingly + // in touchesEnd() (eg. applyCompletion() is called or not) + private var notifyTextChanged: (() -> Void)? + + // The last string used as a replacement in shouldChangeCharactersInRange. + private var lastReplacement: String? + private var hideCursor = false + private var isSettingMarkedText = false + + private let copyShortcutKey = "c" + // MARK: - Init override init(frame: CGRect) { super.init(frame: .zero) + super.addTarget(self, action: #selector(LocationTextField.textDidChange), for: .editingChanged) + + font = UIFont.preferredFont(forTextStyle: .body) + adjustsFontForContentSizeCategory = true clearButtonMode = .whileEditing + keyboardType = .webSearch + autocorrectionType = .no + autocapitalizationType = .none + returnKeyType = .go + tintAdjustmentMode = .normal + delegate = self + + // Disable dragging urls on iPhones because it conflicts with editing the text + if UIDevice.current.userInterfaceIdiom != .pad { + textDragInteraction?.isEnabled = false + } + + notifyTextChanged = debounce(0.1, + action: { + if self.isEditing { + self.autocompleteDelegate?.locationTextField( + self, + didEnterText: self.normalizeString(self.textWithoutSuggestion() ?? "") + ) + } + }) } required init?(coder: NSCoder) { @@ -34,25 +87,232 @@ class LocationTextField: UITextField, ThemeApplicable { override func layoutSubviews() { super.layoutSubviews() - guard let image = UIImage(named: StandardImageIdentifiers.Large.crossCircleFill) else { return } if tintedClearImage == nil { - if let clearButtonTintColor { - tintedClearImage = image.withTintColor(clearButtonTintColor) - } + tintClearButton() } + } - // Since we're unable to change the tint color of the clear image, we need to iterate through the - // subviews, find the clear button, and tint it ourselves. - // https://stackoverflow.com/questions/55046917/clear-button-on-text-field-not-accessible-with-voice-over-swift - if let clearButton = value(forKey: "_clearButton") as? UIButton { - clearButton.setImage(tintedClearImage, for: []) + override func deleteBackward() { + lastReplacement = nil + hideCursor = false + + guard markedTextRange == nil else { + // If we have an active completion, delete it without deleting any user-typed characters. + removeCompletion() + forceResetCursor() + return + } + + super.deleteBackward() + } + + override func touchesBegan(_ touches: Set, with event: UIEvent?) { + applyCompletion() + super.touchesBegan(touches, with: event) + } + + override open func caretRect(for position: UITextPosition) -> CGRect { + return hideCursor ? CGRect.zero : super.caretRect(for: position) + } + + override open func setMarkedText(_ markedText: String?, selectedRange: NSRange) { + isSettingMarkedText = true + removeCompletion() + super.setMarkedText(markedText, selectedRange: selectedRange) + isSettingMarkedText = false + } + + func setAutocompleteSuggestion(_ suggestion: String?) { + let searchText = text ?? "" + + guard let suggestion = suggestion, isEditing && markedTextRange == nil else { + hideCursor = false + return + } + + let normalized = normalizeString(searchText) + guard suggestion.hasPrefix(normalized) && normalized.count < suggestion.count else { + hideCursor = false + return + } + + let suggestionText = String(suggestion.dropFirst(normalized.count)) + setMarkedText(suggestionText, selectedRange: NSRange()) + + // Only call forceResetCursor() if `hideCursor` changes. + // Because forceResetCursor() auto accept iOS user's text replacement + // (e.g. mu->μ) which makes user unable to type "mu". + if !hideCursor { + hideCursor = true + forceResetCursor() } } // MARK: - ThemeApplicable func applyTheme(theme: Theme) { let colors = theme.colors + tintColor = colors.layerSelectedText clearButtonTintColor = colors.iconPrimary - textColor = colors.textPrimary + markedTextStyle = [NSAttributedString.Key.backgroundColor: colors.layerSelectedText] + + if isEditing { + textColor = colors.textPrimary + } + + tintClearButton() } + + // MARK: - Private + @objc + private func textDidChange(_ textField: UITextField) { + // When marked text (autocomplete suggestion) is set this method is called + // in this case we don't need to + guard !isSettingMarkedText else { return } + + hideCursor = markedTextRange != nil + removeCompletion() + + let isKeyboardReplacingText = lastReplacement != nil + if isKeyboardReplacingText, markedTextRange == nil { + notifyTextChanged?() + } else { + hideCursor = false + } + } + + /// Commits the completion by setting the text and removing the highlight. + private func applyCompletion() { + // Clear the current completion, then set the text without the attributed style. + let text = (self.text ?? "") + let didRemoveCompletion = removeCompletion() + self.text = text + hideCursor = false + + // Move the cursor to the end of the completion. + if didRemoveCompletion { + selectedTextRange = textRange(from: endOfDocument, to: endOfDocument) + } + } + + /// Removes the autocomplete-highlighted. Returns true if a completion was actually removed + @objc + @discardableResult + private func removeCompletion() -> Bool { + guard markedTextRange != nil else { return false } + + text = textWithoutSuggestion() + return true + } + + private func textWithoutSuggestion() -> String? { + guard let markedTextRange else { return text } + + let location = offset(from: beginningOfDocument, to: markedTextRange.start) + let length = offset(from: markedTextRange.start, to: markedTextRange.end) + let range = NSRange(location: location, length: length) + return (text as NSString?)?.replacingCharacters(in: range, with: "") + } + + @objc + private func clear() { + text = "" + removeCompletion() + autocompleteDelegate?.locationTextField(self, didEnterText: "") + } + + private func normalizeString(_ string: String) -> String { + return string.lowercased().stringByTrimmingLeadingCharactersInSet(CharacterSet.whitespaces) + } + + // Reset the cursor to the end of the text field. + // This forces `caretRect(for position: UITextPosition)` to be called which will decide if we should show the cursor + // This exists because `caretRect(for position: UITextPosition)` is not called after we apply an autocompletion. + private func forceResetCursor() { + selectedTextRange = nil + selectedTextRange = textRange(from: endOfDocument, to: endOfDocument) + } + + private func tintClearButton() { + // Since we're unable to change the tint color of the clear image, we need to use KVO to + // find the clear button, and tint it ourselves. + // https://stackoverflow.com/questions/27944781/how-to-change-the-tint-color-of-the-clear-button-on-a-uitextfield + guard let image = UIImage(named: StandardImageIdentifiers.Large.crossCircleFill), + let clearButtonTintColor, + let clearButton = value(forKey: "_clearButton") as? UIButton + else { return } + + tintedClearImage = image.withTintColor(clearButtonTintColor) + clearButton.setImage(tintedClearImage, for: []) + } + + // MARK: - UITextFieldDelegate + public func textFieldDidBeginEditing(_ textField: UITextField) { + autocompleteDelegate?.locationTextFieldDidBeginEditing(self) + } + + func textFieldShouldEndEditing(_ textField: UITextField) -> Bool { + applyCompletion() + return true + } + + public func textFieldDidEndEditing(_ textField: UITextField) { + lastReplacement = nil + autocompleteDelegate?.locationTextFieldDidEndEditing(self) + } + + // `shouldChangeCharactersInRange` is called before the text changes, and textDidChange is called after. + // Since the text has changed, remove the completion here, and textDidChange will fire the callback to + // get the new autocompletion. + func textField( + _ textField: UITextField, + shouldChangeCharactersIn range: NSRange, + replacementString string: String + ) -> Bool { + // This happens when you begin typing overtop the old highlighted + // text immediately after focusing the text field. We need to trigger + // a `didEnterText` that looks like a `clear()` so that the SearchLoader + // can reset itself since it will only lookup results if the new text is + // longer than the previous text. + if lastReplacement == nil { + autocompleteDelegate?.locationTextField(self, didEnterText: "") + } + + lastReplacement = string + return true + } + + func textFieldShouldReturn(_ textField: UITextField) -> Bool { + applyCompletion() + return autocompleteDelegate?.locationTextFieldShouldReturn(self) ?? true + } + + func textFieldShouldClear(_ textField: UITextField) -> Bool { + removeCompletion() + return autocompleteDelegate?.locationTextFieldShouldClear(self) ?? true + } + + // MARK: - Debounce + /** + * Taken from http://stackoverflow.com/questions/27116684/how-can-i-debounce-a-method-call + * Allows creating a block that will fire after a delay. Resets the timer if called again before the delay expires. + **/ + private func debounce(_ delay: TimeInterval, action: @escaping () -> Void) -> () -> Void { + let callback = Callback(handler: action) + var timer: Timer? + + return { + // If calling again, invalidate the last timer. + if let timer = timer { + timer.invalidate() + } + timer = Timer( + timeInterval: delay, + target: callback, + selector: #selector(Callback.go), + userInfo: nil, + repeats: false + ) + RunLoop.current.add(timer!, forMode: RunLoop.Mode.default) + } + } } diff --git a/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationView.swift b/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationView.swift index 64940bdb67d7..bb71dcb4ab76 100644 --- a/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationView.swift +++ b/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationView.swift @@ -5,7 +5,7 @@ import UIKit import Common -final class LocationView: UIView, UITextFieldDelegate, ThemeApplicable, AccessibilityActionsSource { +final class LocationView: UIView, LocationTextFieldDelegate, ThemeApplicable, AccessibilityActionsSource { // MARK: - Properties private enum UX { static let horizontalSpace: CGFloat = 8 @@ -43,12 +43,14 @@ final class LocationView: UIView, UITextFieldDelegate, ThemeApplicable, Accessib return CGFloat(width) } + private lazy var urlTextFieldColor: UIColor = .black private lazy var urlTextFieldSubdomainColor: UIColor = .clear private lazy var gradientLayer = CAGradientLayer() private lazy var gradientView: UIView = .build() private var clearButtonWidthConstraint: NSLayoutConstraint? private var urlTextFieldLeadingConstraint: NSLayoutConstraint? + private var lockIconWidthAnchor: NSLayoutConstraint? private lazy var iconContainerStackView: UIStackView = .build { view in view.axis = .horizontal @@ -73,7 +75,7 @@ final class LocationView: UIView, UITextFieldDelegate, ThemeApplicable, Accessib urlTextField.backgroundColor = .clear urlTextField.font = FXFontStyles.Regular.body.scaledFont() urlTextField.adjustsFontForContentSizeCategory = true - urlTextField.delegate = self + urlTextField.autocompleteDelegate = self urlTextField.accessibilityActionsSource = self } @@ -120,6 +122,10 @@ final class LocationView: UIView, UITextFieldDelegate, ThemeApplicable, Accessib onLongPress = state.onLongPress } + func setAutocompleteSuggestion(_ suggestion: String?) { + urlTextField.setAutocompleteSuggestion(suggestion) + } + // MARK: - Layout override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { super.traitCollectionDidChange(previousTraitCollection) @@ -235,22 +241,45 @@ final class LocationView: UIView, UITextFieldDelegate, ThemeApplicable, Accessib updateGradient() } + private func updateWidthForLockIcon(_ width: CGFloat) { + lockIconWidthAnchor?.isActive = false + lockIconWidthAnchor = lockIconButton.widthAnchor.constraint(equalToConstant: width) + lockIconWidthAnchor?.isActive = true + } + // MARK: - `urlTextField` Configuration private func configureURLTextField(_ state: LocationViewState) { - urlTextField.resignFirstResponder() - urlTextField.text = state.url?.absoluteString + if state.isEditing { + urlTextField.text = (state.searchTerm != nil) ? state.searchTerm : state.url?.absoluteString + } else { + urlTextField.text = state.url?.absoluteString + } + urlTextField.placeholder = state.urlTextFieldPlaceholder - urlAbsolutePath = urlTextField.text + urlAbsolutePath = state.url?.absoluteString + + _ = state.isEditing ? becomeFirstResponder() : resignFirstResponder() + + // Start overlay mode & select text when in edit mode with a search term + if state.isEditing, state.shouldSelectSearchTerm { + DispatchQueue.main.async { + self.urlTextField.selectAll(nil) + } + } } private func formatAndTruncateURLTextField() { + guard !urlTextField.isEditing else { return } + let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineBreakMode = .byTruncatingHead let urlString = urlAbsolutePath ?? "" let (subdomain, normalizedHost) = URL.getSubdomainAndHost(from: urlString) - let attributedString = NSMutableAttributedString(string: normalizedHost) + let attributedString = NSMutableAttributedString( + string: normalizedHost, + attributes: [.foregroundColor: urlTextFieldColor]) if let subdomain { let range = NSRange(location: 0, length: subdomain.count) @@ -269,7 +298,13 @@ final class LocationView: UIView, UITextFieldDelegate, ThemeApplicable, Accessib // MARK: - `lockIconButton` Configuration private func configureLockIconButton(_ state: LocationViewState) { - let lockImage = UIImage(named: state.lockIconImageName)?.withRenderingMode(.alwaysTemplate) + guard let lockIconImageName = state.lockIconImageName else { + updateWidthForLockIcon(0) + return + } + updateWidthForLockIcon(UX.lockIconImageViewSize.width) + + let lockImage = UIImage(named: lockIconImageName)?.withRenderingMode(.alwaysTemplate) lockIconButton.setImage(lockImage, for: .normal) onTapLockIcon = state.onTapLockIcon } @@ -298,20 +333,45 @@ final class LocationView: UIView, UITextFieldDelegate, ThemeApplicable, Accessib } } - // MARK: - UITextFieldDelegate - public func textFieldDidBeginEditing(_ textField: UITextField) { + // MARK: - LocationTextFieldDelegate + func locationTextField(_ textField: LocationTextField, didEnterText text: String) { + delegate?.locationViewDidEnterText(text) + } + + func locationTextFieldShouldReturn(_ textField: LocationTextField) -> Bool { + guard let text = textField.text else { return true } + if !text.trimmingCharacters(in: .whitespaces).isEmpty { + delegate?.locationViewDidSubmitText(text) + textField.resignFirstResponder() + return true + } else { + return false + } + } + + func locationTextFieldShouldClear(_ textField: LocationTextField) -> Bool { + delegate?.locationViewDidEnterText("") + return true + } + + func locationPasteAndGo(_ textField: LocationTextField) { + if let pasteboardContents = UIPasteboard.general.string { + delegate?.locationViewDidSubmitText(pasteboardContents) + } + } + + func locationTextFieldDidBeginEditing(_ textField: UITextField) { updateUIForSearchEngineDisplay() DispatchQueue.main.async { [self] in // `attributedText` property is set to nil to remove all formatting and truncation set before. textField.attributedText = nil textField.text = searchTerm != nil ? searchTerm : urlAbsolutePath - textField.selectAll(nil) } delegate?.locationViewDidBeginEditing(textField.text ?? "") } - public func textFieldDidEndEditing(_ textField: UITextField) { + func locationTextFieldDidEndEditing(_ textField: UITextField) { formatAndTruncateURLTextField() if isURLTextFieldEmpty { updateGradient() @@ -320,14 +380,6 @@ final class LocationView: UIView, UITextFieldDelegate, ThemeApplicable, Accessib } } - public func textFieldShouldReturn(_ textField: UITextField) -> Bool { - guard let searchText = textField.text?.lowercased(), !searchText.isEmpty else { return false } - - delegate?.locationViewShouldSearchFor(searchText) - textField.resignFirstResponder() - return true - } - // MARK: - Accessibility private func configureA11y(_ state: LocationViewState) { lockIconButton.accessibilityIdentifier = state.lockIconButtonA11yId @@ -350,6 +402,7 @@ final class LocationView: UIView, UITextFieldDelegate, ThemeApplicable, Accessib // MARK: - ThemeApplicable func applyTheme(theme: Theme) { let colors = theme.colors + urlTextFieldColor = colors.textPrimary urlTextFieldSubdomainColor = colors.textSecondary gradientLayer.colors = colors.layerGradientURL.cgColors.reversed() searchEngineImageView.backgroundColor = colors.iconPrimary diff --git a/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationViewDelegate.swift b/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationViewDelegate.swift index efeb791701f2..bf2670dbb614 100644 --- a/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationViewDelegate.swift +++ b/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationViewDelegate.swift @@ -21,7 +21,7 @@ protocol LocationViewDelegate: AnyObject { /// Called when the location view should perform a search based on the entered text. /// /// - Parameter text: The text for which the location view should search. - func locationViewShouldSearchFor(_ text: String) + func locationViewDidSubmitText(_ text: String) /// Called when requesting custom accessibility actions to be performed on the location view. /// diff --git a/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationViewState.swift b/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationViewState.swift index c0ce76531c19..2d5a8bf6810c 100644 --- a/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationViewState.swift +++ b/BrowserKit/Sources/ToolbarKit/AddressToolbar/LocationView/LocationViewState.swift @@ -16,9 +16,11 @@ public struct LocationViewState { public let urlTextFieldA11yLabel: String public let searchEngineImage: UIImage? - public let lockIconImageName: String + public let lockIconImageName: String? public let url: URL? public let searchTerm: String? + public let isEditing: Bool + public let shouldSelectSearchTerm: Bool public var onTapLockIcon: (() -> Void)? public var onLongPress: (() -> Void)? @@ -31,9 +33,11 @@ public struct LocationViewState { urlTextFieldA11yId: String, urlTextFieldA11yLabel: String, searchEngineImage: UIImage?, - lockIconImageName: String, + lockIconImageName: String?, url: URL?, searchTerm: String?, + isEditing: Bool, + shouldSelectSearchTerm: Bool, onTapLockIcon: (() -> Void)? = nil, onLongPress: (() -> Void)? = nil ) { @@ -48,6 +52,8 @@ public struct LocationViewState { self.lockIconImageName = lockIconImageName self.url = url self.searchTerm = searchTerm + self.isEditing = isEditing + self.shouldSelectSearchTerm = shouldSelectSearchTerm self.onTapLockIcon = onTapLockIcon self.onLongPress = onLongPress } diff --git a/BrowserKit/Sources/ToolbarKit/NavigationToolbar/BrowserNavigationToolbar.swift b/BrowserKit/Sources/ToolbarKit/NavigationToolbar/BrowserNavigationToolbar.swift index 1b2ae0c90f16..aee530f2c1c7 100644 --- a/BrowserKit/Sources/ToolbarKit/NavigationToolbar/BrowserNavigationToolbar.swift +++ b/BrowserKit/Sources/ToolbarKit/NavigationToolbar/BrowserNavigationToolbar.swift @@ -5,6 +5,10 @@ import UIKit import Common +public protocol BrowserNavigationToolbarDelegate: AnyObject { + func configureContextualHint(for button: UIButton) +} + /// Navigation toolbar implementation. public class BrowserNavigationToolbar: UIView, NavigationToolbar, ThemeApplicable { private enum UX { @@ -13,6 +17,7 @@ public class BrowserNavigationToolbar: UIView, NavigationToolbar, ThemeApplicabl static let borderHeight: CGFloat = 1 } + private weak var toolbarDelegate: BrowserNavigationToolbarDelegate? private lazy var actionStack: UIStackView = .build { view in view.distribution = .equalSpacing } @@ -29,7 +34,9 @@ public class BrowserNavigationToolbar: UIView, NavigationToolbar, ThemeApplicabl fatalError("init(coder:) has not been implemented") } - public func configure(state: NavigationToolbarState) { + public func configure(state: NavigationToolbarState, toolbarDelegate: BrowserNavigationToolbarDelegate) { + self.toolbarDelegate = toolbarDelegate + updateActionStack(toolbarElements: state.actions) // Update border @@ -72,6 +79,10 @@ public class BrowserNavigationToolbar: UIView, NavigationToolbar, ThemeApplicabl // As we recreate the buttons we need to apply the theme for them to be displayed correctly button.applyTheme(theme: theme) } + + if toolbarElement.hasContextualHint == true { + toolbarDelegate?.configureContextualHint(for: button) + } } } diff --git a/BrowserKit/Sources/ToolbarKit/NavigationToolbar/NavigationToolbar.swift b/BrowserKit/Sources/ToolbarKit/NavigationToolbar/NavigationToolbar.swift index b99d6a9f2a21..bfbf8c51cb12 100644 --- a/BrowserKit/Sources/ToolbarKit/NavigationToolbar/NavigationToolbar.swift +++ b/BrowserKit/Sources/ToolbarKit/NavigationToolbar/NavigationToolbar.swift @@ -6,5 +6,5 @@ import Foundation /// Protocol representing an navigation toolbar. public protocol NavigationToolbar { - func configure(state: NavigationToolbarState) + func configure(state: NavigationToolbarState, toolbarDelegate: BrowserNavigationToolbarDelegate) } diff --git a/BrowserKit/Sources/ToolbarKit/ToolbarButton.swift b/BrowserKit/Sources/ToolbarKit/ToolbarButton.swift index e225723d04af..a7be39e1fb25 100644 --- a/BrowserKit/Sources/ToolbarKit/ToolbarButton.swift +++ b/BrowserKit/Sources/ToolbarKit/ToolbarButton.swift @@ -14,8 +14,6 @@ class ToolbarButton: UIButton, ThemeApplicable { private struct UX { static let verticalInset: CGFloat = 10 static let horizontalInset: CGFloat = 10 - static let badgeImageViewBorderWidth: CGFloat = 1 - static let badgeImageViewCornerRadius: CGFloat = 10 static let badgeIconSize = CGSize(width: 20, height: 20) } @@ -25,8 +23,9 @@ class ToolbarButton: UIButton, ThemeApplicable { private(set) var backgroundColorNormal: UIColor = .clear private var badgeImageView: UIImageView? - private var shouldDisplayAsHighlighted = false + private var maskImageView: UIImageView? + private var shouldDisplayAsHighlighted = false private var onLongPress: ((UIButton) -> Void)? override init(frame: CGRect) { @@ -40,14 +39,13 @@ class ToolbarButton: UIButton, ThemeApplicable { } open func configure(element: ToolbarElement) { - guard var config = configuration else { - return - } + guard var config = configuration else { return } removeAllGestureRecognizers() configureLongPressGestureRecognizerIfNeeded(for: element) + configureCustomA11yActionIfNeeded(for: element) shouldDisplayAsHighlighted = element.shouldDisplayAsHighlighted - let image = UIImage(named: element.iconName)?.withRenderingMode(.alwaysTemplate) + let image = imageConfiguredForRTL(for: element) let action = UIAction(title: element.a11yLabel, image: image, handler: { _ in @@ -68,6 +66,9 @@ class ToolbarButton: UIButton, ThemeApplicable { configuration = config if let badgeName = element.badgeImageName { addBadgeIcon(imageName: badgeName) + if let maskImageName = element.maskImageName { + addMaskIcon(maskImageName: maskImageName) + } } layoutIfNeeded() } @@ -77,9 +78,7 @@ class ToolbarButton: UIButton, ThemeApplicable { } override public func updateConfiguration() { - guard var updatedConfiguration = configuration else { - return - } + guard var updatedConfiguration = configuration else { return } switch state { case .highlighted: @@ -99,18 +98,28 @@ class ToolbarButton: UIButton, ThemeApplicable { private func addBadgeIcon(imageName: String) { badgeImageView = UIImageView(image: UIImage(named: imageName)) guard let badgeImageView, configuration?.image != nil else { return } - - badgeImageView.layer.borderWidth = UX.badgeImageViewBorderWidth - badgeImageView.layer.cornerRadius = UX.badgeImageViewCornerRadius - badgeImageView.clipsToBounds = true badgeImageView.translatesAutoresizingMaskIntoConstraints = false imageView?.addSubview(badgeImageView) + applyBadgeConstraints(to: badgeImageView) + } + + private func addMaskIcon(maskImageName: String) { + maskImageView = UIImageView(image: UIImage(named: maskImageName)) + guard let maskImageView, let badgeImageView else { return } + maskImageView.translatesAutoresizingMaskIntoConstraints = false + + maskImageView.addSubview(badgeImageView) + imageView?.addSubview(maskImageView) + applyBadgeConstraints(to: maskImageView) + } + + private func applyBadgeConstraints(to imageView: UIImageView) { NSLayoutConstraint.activate([ - badgeImageView.widthAnchor.constraint(equalToConstant: UX.badgeIconSize.width), - badgeImageView.heightAnchor.constraint(equalToConstant: UX.badgeIconSize.height), - badgeImageView.leadingAnchor.constraint(equalTo: centerXAnchor), - badgeImageView.bottomAnchor.constraint(equalTo: centerYAnchor) + imageView.widthAnchor.constraint(equalToConstant: UX.badgeIconSize.width), + imageView.heightAnchor.constraint(equalToConstant: UX.badgeIconSize.height), + imageView.leadingAnchor.constraint(equalTo: centerXAnchor), + imageView.bottomAnchor.constraint(equalTo: centerYAnchor) ]) } @@ -124,6 +133,21 @@ class ToolbarButton: UIButton, ThemeApplicable { addGestureRecognizer(longPressRecognizer) } + private func configureCustomA11yActionIfNeeded(for element: ToolbarElement) { + guard let a11yCustomActionName = element.a11yCustomActionName, + let a11yCustomAction = element.a11yCustomAction else { return } + let a11yAction = UIAccessibilityCustomAction(name: a11yCustomActionName) { _ in + a11yCustomAction() + return true + } + accessibilityCustomActions = [a11yAction] + } + + private func imageConfiguredForRTL(for element: ToolbarElement) -> UIImage? { + let image = UIImage(named: element.iconName)?.withRenderingMode(.alwaysTemplate) + return element.isFlippedForRTL ? image?.imageFlippedForRightToLeftLayoutDirection() : image + } + private func removeAllGestureRecognizers() { guard let gestureRecognizers else { return } for recognizer in gestureRecognizers { @@ -143,12 +167,17 @@ class ToolbarButton: UIButton, ThemeApplicable { // MARK: - ThemeApplicable public func applyTheme(theme: Theme) { - foregroundColorNormal = theme.colors.iconPrimary - foregroundColorHighlighted = theme.colors.actionPrimary - foregroundColorDisabled = theme.colors.iconDisabled - badgeImageView?.layer.borderColor = theme.colors.layer1.cgColor - badgeImageView?.backgroundColor = theme.colors.layer1 + let colors = theme.colors + foregroundColorNormal = colors.iconPrimary + foregroundColorHighlighted = colors.actionPrimary + foregroundColorDisabled = colors.iconDisabled backgroundColorNormal = .clear + + badgeImageView?.layer.borderColor = colors.layer1.cgColor + badgeImageView?.backgroundColor = maskImageView == nil ? colors.layer1 : .clear + badgeImageView?.tintColor = maskImageView == nil ? .clear : colors.actionInfo + maskImageView?.tintColor = colors.layer1 + setNeedsUpdateConfiguration() } } diff --git a/BrowserKit/Sources/ToolbarKit/ToolbarElement.swift b/BrowserKit/Sources/ToolbarKit/ToolbarElement.swift index e2b7558ebf7e..853d6eff69e3 100644 --- a/BrowserKit/Sources/ToolbarKit/ToolbarElement.swift +++ b/BrowserKit/Sources/ToolbarKit/ToolbarElement.swift @@ -11,15 +11,24 @@ public struct ToolbarElement: Equatable { /// Badge name of the toolbar element let badgeImageName: String? + /// Mask name of the badge's toolbar element + let maskImageName: String? + /// Number of open tabs let numberOfTabs: Int? /// Whether the toolbar element can be interacted with let isEnabled: Bool + /// Indicates whether the toolbar element's image should be flipped for right-to-left layout direction + let isFlippedForRTL: Bool + /// Indicates if the element should be displayed as highlighted let shouldDisplayAsHighlighted: Bool + /// Indicates that there is an associated contextual hint + let hasContextualHint: Bool? + /// Accessibility label of the toolbar element let a11yLabel: String @@ -29,6 +38,12 @@ public struct ToolbarElement: Equatable { /// Accessibility identifier of the toolbar element let a11yId: String + /// Name for the custom accessibility action + let a11yCustomActionName: String? + + /// Action to be performed for custom accessibility action + let a11yCustomAction: (() -> Void)? + /// Closure that is executed when the toolbar element is tapped let onSelected: ((UIButton) -> Void)? @@ -39,31 +54,43 @@ public struct ToolbarElement: Equatable { // can therefor not be used outside of the ToolbarKit public init(iconName: String, badgeImageName: String? = nil, + maskImageName: String? = nil, numberOfTabs: Int? = nil, isEnabled: Bool, + isFlippedForRTL: Bool = false, shouldDisplayAsHighlighted: Bool = false, + hasContextualHint: Bool = false, a11yLabel: String, a11yHint: String?, a11yId: String, + a11yCustomActionName: String? = nil, + a11yCustomAction: (() -> Void)? = nil, onSelected: ((UIButton) -> Void)?, onLongPress: ((UIButton) -> Void)? = nil) { self.iconName = iconName self.badgeImageName = badgeImageName + self.maskImageName = maskImageName self.numberOfTabs = numberOfTabs self.isEnabled = isEnabled + self.isFlippedForRTL = isFlippedForRTL self.shouldDisplayAsHighlighted = shouldDisplayAsHighlighted + self.hasContextualHint = hasContextualHint self.onSelected = onSelected self.onLongPress = onLongPress self.a11yLabel = a11yLabel self.a11yHint = a11yHint self.a11yId = a11yId + self.a11yCustomActionName = a11yCustomActionName + self.a11yCustomAction = a11yCustomAction } public static func == (lhs: ToolbarElement, rhs: ToolbarElement) -> Bool { lhs.iconName == rhs.iconName && lhs.badgeImageName == rhs.badgeImageName && + lhs.maskImageName == rhs.maskImageName && lhs.numberOfTabs == rhs.numberOfTabs && lhs.isEnabled == rhs.isEnabled && + lhs.isFlippedForRTL == rhs.isFlippedForRTL && lhs.shouldDisplayAsHighlighted == rhs.shouldDisplayAsHighlighted && lhs.a11yLabel == rhs.a11yLabel && lhs.a11yHint == rhs.a11yHint && diff --git a/BrowserKit/Tests/MenuKitTests/MenuKitTests.swift b/BrowserKit/Tests/MenuKitTests/MenuKitTests.swift new file mode 100644 index 000000000000..060b84dd9ea8 --- /dev/null +++ b/BrowserKit/Tests/MenuKitTests/MenuKitTests.swift @@ -0,0 +1,31 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import MenuKit + +final class MenuKitTests: XCTestCase { + func testMenuElement_initializesCorrectly() { + let subject = MenuElement( + iconName: "test", + isEnabled: true, + isActive: true, + hasSubmenu: false, + a11yLabel: "test", + a11yHint: nil, + a11yId: "test" + ) + let expectedResult = MenuElement( + iconName: "test", + isEnabled: true, + isActive: true, + hasSubmenu: false, + a11yLabel: "test", + a11yHint: nil, + a11yId: "test" + ) + + XCTAssertEqual(subject, expectedResult) + } +} diff --git a/BrowserKit/Tests/TabDataStoreTests/TabSessionStoreTests.swift b/BrowserKit/Tests/TabDataStoreTests/TabSessionStoreTests.swift index 2967077eb662..c7f23ec9cbc8 100644 --- a/BrowserKit/Tests/TabDataStoreTests/TabSessionStoreTests.swift +++ b/BrowserKit/Tests/TabDataStoreTests/TabSessionStoreTests.swift @@ -21,24 +21,24 @@ final class TabSessionStoreTests: XCTestCase { // MARK: Save - func testSaveWithoutDirectory() async { + func testSaveWithoutDirectory() { let subject = createSubject() let uuid = UUID() let dataFile = Data(count: 100) - await subject.saveTabSession(tabID: uuid, sessionData: dataFile) + subject.saveTabSession(tabID: uuid, sessionData: dataFile) XCTAssertEqual(mockFileManager.tabSessionDataDirectoryCalledCount, 1) XCTAssertEqual(mockFileManager.fileExistsCalledCount, 0) XCTAssertEqual(mockFileManager.createDirectoryAtPathCalledCount, 0) } - func testSaveTabSession() async { + func testSaveTabSession() { let subject = createSubject() let uuid = UUID() let dataFile = Data(count: 100) mockFileManager.primaryDirectoryURL = URL(string: "some/directory") mockFileManager.fileExists = false - await subject.saveTabSession(tabID: uuid, sessionData: dataFile) + subject.saveTabSession(tabID: uuid, sessionData: dataFile) XCTAssertEqual(mockFileManager.tabSessionDataDirectoryCalledCount, 1) XCTAssertEqual(mockFileManager.fileExistsCalledCount, 1) @@ -47,21 +47,21 @@ final class TabSessionStoreTests: XCTestCase { // MARK: Fetch - func testFetchTabSessionWithoutDirectory() async { + func testFetchTabSessionWithoutDirectory() { let subject = createSubject() let uuid = UUID() - _ = await subject.fetchTabSession(tabID: uuid) + _ = subject.fetchTabSession(tabID: uuid) XCTAssertEqual(mockFileManager.tabSessionDataDirectoryCalledCount, 1) } - func testFetchTabSession() async { + func testFetchTabSession() { let subject = createSubject() let uuid = UUID() mockFileManager.primaryDirectoryURL = URL(string: "some/directory") - _ = await subject.fetchTabSession(tabID: uuid) + _ = subject.fetchTabSession(tabID: uuid) XCTAssertEqual(mockFileManager.tabSessionDataDirectoryCalledCount, 1) } diff --git a/SampleBrowser/SampleBrowser.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/SampleBrowser/SampleBrowser.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 0d8d7a515d83..c97fbc9996a9 100644 --- a/SampleBrowser/SampleBrowser.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/SampleBrowser/SampleBrowser.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -41,8 +41,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/getsentry/sentry-cocoa.git", "state" : { - "revision" : "38f4f70d07117b9f958a76b1bff278c2f29ffe0e", - "version" : "8.21.0" + "revision" : "d2ced2d961b34573ebd2ea0567a2f1408e90f0ae", + "version" : "8.34.0" } }, { diff --git a/SampleComponentLibraryApp/SampleComponentLibraryApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/SampleComponentLibraryApp/SampleComponentLibraryApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 0d8d7a515d83..c97fbc9996a9 100644 --- a/SampleComponentLibraryApp/SampleComponentLibraryApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/SampleComponentLibraryApp/SampleComponentLibraryApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -41,8 +41,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/getsentry/sentry-cocoa.git", "state" : { - "revision" : "38f4f70d07117b9f958a76b1bff278c2f29ffe0e", - "version" : "8.21.0" + "revision" : "d2ced2d961b34573ebd2ea0567a2f1408e90f0ae", + "version" : "8.34.0" } }, { diff --git a/SampleComponentLibraryApp/SampleComponentLibraryApp/BottomSheet/BottomSheetComponentViewModel.swift b/SampleComponentLibraryApp/SampleComponentLibraryApp/BottomSheet/BottomSheetComponentViewModel.swift index f13eb00c5b91..d391f1ef173b 100644 --- a/SampleComponentLibraryApp/SampleComponentLibraryApp/BottomSheet/BottomSheetComponentViewModel.swift +++ b/SampleComponentLibraryApp/SampleComponentLibraryApp/BottomSheet/BottomSheetComponentViewModel.swift @@ -12,13 +12,16 @@ struct BottomSheetComponentViewModel: ComponentViewModel { private var viewModel: BottomSheetViewModel init() { - viewModel = BottomSheetViewModel(closeButtonA11yLabel: "Close button", closeButtonA11yIdentifier: "a11yCloseButton") + viewModel = BottomSheetViewModel( + closeButtonA11yLabel: "Close button", + closeButtonA11yIdentifier: "a11yCloseButton") viewModel.shouldDismissForTapOutside = true viewController = BottomSheetViewController( viewModel: viewModel, childViewController: BottomSheetChildViewController(), - usingDimmedBackground: true + usingDimmedBackground: true, + windowUUID: defaultSampleComponentUUID ) } diff --git a/bitrise.yml b/bitrise.yml index 5a32e1618367..c9e8eb65b254 100644 --- a/bitrise.yml +++ b/bitrise.yml @@ -2123,17 +2123,17 @@ app: BITRISE_NIGHTLY_VERSION: '9000' - opts: is_expand: false - BITRISE_RELEASE_VERSION: '130.0' + BITRISE_RELEASE_VERSION: '131.0' - opts: is_expand: false - BITRISE_BETA_VERSION: '130.0' + BITRISE_BETA_VERSION: '131.0' trigger_map: - push_branch: main pipeline: pipeline_build_and_test - push_branch: epic-branch/* pipeline: pipeline_build_and_test -- push_branch: release/v130 +- push_branch: release/v131 pipeline: pipeline_build_and_test - pull_request_target_branch: main pipeline: pipeline_build_and_test diff --git a/firefox-ios/Client.xcodeproj/project.pbxproj b/firefox-ios/Client.xcodeproj/project.pbxproj index 322992fdd704..8000a4a5aba1 100644 --- a/firefox-ios/Client.xcodeproj/project.pbxproj +++ b/firefox-ios/Client.xcodeproj/project.pbxproj @@ -15,9 +15,16 @@ 047F9B3224E1FE1F00CD7DF7 /* WidgetKitExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 047F9B2724E1FE1C00CD7DF7 /* WidgetKitExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 047F9B3E24E1FF4000CD7DF7 /* SearchQuickLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 047F9B3A24E1FF4000CD7DF7 /* SearchQuickLinks.swift */; }; 047F9B4224E1FF4000CD7DF7 /* ImageButtonWithLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 047F9B3C24E1FF4000CD7DF7 /* ImageButtonWithLabel.swift */; }; + 0A49784A2C53E63200B1E82A /* TrackingProtectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A4978492C53E63200B1E82A /* TrackingProtectionViewController.swift */; }; + 0A7693612C7DD19600103A6D /* CertificatesViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A7693602C7DD19500103A6D /* CertificatesViewModelTests.swift */; }; 0AC659272BF35854005C614A /* FxAWebViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AC659262BF35854005C614A /* FxAWebViewModelTests.swift */; }; 0AC659292BF493CE005C614A /* MockFxAWebViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AC659282BF493CE005C614A /* MockFxAWebViewModel.swift */; }; 0AD3EEAC2C2485A7001044E5 /* ThemedCenteredTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AD3EEAB2C2485A7001044E5 /* ThemedCenteredTableViewCell.swift */; }; + 0AFF7F642C7784D600265214 /* MockDataCleaner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AFF7F632C7784D600265214 /* MockDataCleaner.swift */; }; + 0AFF7F662C7784F100265214 /* TrackingProtectionModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AFF7F652C7784F000265214 /* TrackingProtectionModelTests.swift */; }; + 0AFF7F6C2C7C7BBA00265214 /* CertificatesCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AFF7F692C7C7BB800265214 /* CertificatesCell.swift */; }; + 0AFF7F6D2C7C7BBA00265214 /* CertificatesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AFF7F6A2C7C7BB900265214 /* CertificatesViewController.swift */; }; + 0AFF7F6E2C7C7BBA00265214 /* CertificatesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0AFF7F6B2C7C7BB900265214 /* CertificatesViewModel.swift */; }; 0B305E1B1E3A98A900BE0767 /* BookmarksTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B305E1A1E3A98A900BE0767 /* BookmarksTests.swift */; }; 0B3D670E1E09B90B00C1EFC7 /* AuthenticationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B3D670D1E09B90B00C1EFC7 /* AuthenticationTest.swift */; }; 0B54BD191B698B7C004C822C /* SuggestedSites.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B54BD181B698B7C004C822C /* SuggestedSites.swift */; }; @@ -358,6 +365,7 @@ 437A857827E43FE100E42764 /* FxAWebViewTelemetry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 437A857727E43FE100E42764 /* FxAWebViewTelemetry.swift */; }; 437A9B682681256800FB41C1 /* LegacyInactiveTabCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 437A9B672681256800FB41C1 /* LegacyInactiveTabCell.swift */; }; 437A9B6A2681257F00FB41C1 /* LegacyInactiveTabViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 437A9B692681257F00FB41C1 /* LegacyInactiveTabViewModel.swift */; }; + 437AE3102C7C9A0C0053F7F3 /* NativeErrorPage.strings in Resources */ = {isa = PBXBuildFile; fileRef = 437AE30E2C7C9A0C0053F7F3 /* NativeErrorPage.strings */; }; 438FE8642988ABA600155B10 /* CreditCardTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 438FE8632988ABA600155B10 /* CreditCardTableViewController.swift */; }; 43903A892C20502A00ACF76E /* ActivityStream.strings in Resources */ = {isa = PBXBuildFile; fileRef = 43903A872C20502A00ACF76E /* ActivityStream.strings */; }; 43903A8C2C20502A00ACF76E /* AddressToolbar.strings in Resources */ = {isa = PBXBuildFile; fileRef = 43903A8A2C20502A00ACF76E /* AddressToolbar.strings */; }; @@ -420,6 +428,7 @@ 43DF945A292258C300590FE3 /* SearchHeaderTitle.strings in Resources */ = {isa = PBXBuildFile; fileRef = 43DF9458292258C300590FE3 /* SearchHeaderTitle.strings */; }; 43E69EC3254D081D00B591C2 /* SimpleTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43E69EAF254D064E00B591C2 /* SimpleTab.swift */; }; 43E69ED7254D081F00B591C2 /* SimpleTab.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43E69EAF254D064E00B591C2 /* SimpleTab.swift */; }; + 43E6BE162C57AF7600C2152C /* Toolbar.strings in Resources */ = {isa = PBXBuildFile; fileRef = 43E6BE142C57AF7600C2152C /* Toolbar.strings */; }; 43F119002A52E42400C44C6C /* CustomizeFirefoxHome.strings in Resources */ = {isa = PBXBuildFile; fileRef = 43F118FE2A52E42400C44C6C /* CustomizeFirefoxHome.strings */; }; 43F7952525795F69005AEE40 /* SearchTelemetry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43F7952425795F69005AEE40 /* SearchTelemetry.swift */; }; 43F92B3829E9F52B000C0F17 /* AutofillAllFramesAtDocumentStart.js in Resources */ = {isa = PBXBuildFile; fileRef = 43F92B3729E9F52B000C0F17 /* AutofillAllFramesAtDocumentStart.js */; }; @@ -798,6 +807,8 @@ 8A95FF642B1E969E00AC303D /* TelemetryContextualIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A95FF632B1E969E00AC303D /* TelemetryContextualIdentifier.swift */; }; 8A95FF672B1E97A800AC303D /* TelemetryContextualIdentifierTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A95FF652B1E977E00AC303D /* TelemetryContextualIdentifierTests.swift */; }; 8A96C4BB28F9E7B300B75884 /* XCTestCaseRootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A96C4BA28F9E7B300B75884 /* XCTestCaseRootViewController.swift */; }; + 8A97E6EA2C58487900F94793 /* AddressLocaleFeatureValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A97E6E92C58487900F94793 /* AddressLocaleFeatureValidator.swift */; }; + 8A97E6EF2C584C4900F94793 /* AddressLocaleFeatureValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A97E6ED2C584AC300F94793 /* AddressLocaleFeatureValidatorTests.swift */; }; 8A9AC465276CEC4E0047F5B0 /* JumpBackInCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A9AC464276CEC4E0047F5B0 /* JumpBackInCell.swift */; }; 8A9AC46B276D11280047F5B0 /* PocketViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A9AC46A276D11280047F5B0 /* PocketViewModel.swift */; }; 8A9B87AD2C1B39100042B894 /* SearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A9B87AC2C1B39100042B894 /* SearchViewModel.swift */; }; @@ -813,6 +824,7 @@ 8AAAB0592C1B7240008830B3 /* MockRustFirefoxSuggest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AAAB0582C1B723F008830B3 /* MockRustFirefoxSuggest.swift */; }; 8AAAB05B2C1B7268008830B3 /* ClientTabsSearchWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AAAB05A2C1B7268008830B3 /* ClientTabsSearchWrapper.swift */; }; 8AAAB05F2C1B72C9008830B3 /* SearchHighlightItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AAAB05E2C1B72C9008830B3 /* SearchHighlightItem.swift */; }; + 8AABB92D2C64F77000F1FE51 /* LoginProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AABB92C2C64F77000F1FE51 /* LoginProvider.swift */; }; 8AABBCFC2A0010900089941E /* GleanWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AABBCFB2A0010900089941E /* GleanWrapper.swift */; }; 8AABBCFF2A0017960089941E /* MockGleanWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AABBCFD2A0017560089941E /* MockGleanWrapper.swift */; }; 8AABBD012A001ADF0089941E /* ApplicationHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AABBD002A001ADF0089941E /* ApplicationHelper.swift */; }; @@ -840,6 +852,10 @@ 8AB8574627D97CB00075C173 /* HomepageContextMenuProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB8574527D97CB00075C173 /* HomepageContextMenuProtocol.swift */; }; 8AB8574827D97CD40075C173 /* HomePanelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB8574727D97CD40075C173 /* HomePanelType.swift */; }; 8AB8574A27D97CE90075C173 /* HomePanelDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB8574927D97CE90075C173 /* HomePanelDelegate.swift */; }; + 8AB893A32C73AF5200DAEED7 /* MockCreditCardProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB893A22C73AF5200DAEED7 /* MockCreditCardProvider.swift */; }; + 8AB893A62C73AFBA00DAEED7 /* MockLoginProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB893A52C73AFBA00DAEED7 /* MockLoginProvider.swift */; }; + 8AB893A72C73AFCC00DAEED7 /* MockLoginViewModelDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A880C432C63CFE200B77F23 /* MockLoginViewModelDelegate.swift */; }; + 8AB893A92C73CBBD00DAEED7 /* CreditCardProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AB893A82C73CBBD00DAEED7 /* CreditCardProvider.swift */; }; 8ABA9C8D28931223002C0077 /* MockDispatchQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ABA9C8C28931223002C0077 /* MockDispatchQueue.swift */; }; 8ABA9C8E28931288002C0077 /* JumpBackInDataAdaptorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ABA9C8A28931207002C0077 /* JumpBackInDataAdaptorTests.swift */; }; 8ABC5AEE284532C900FEA552 /* PocketDiscoverCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ABC5AED284532C900FEA552 /* PocketDiscoverCell.swift */; }; @@ -1039,6 +1055,7 @@ B12DDFED2A8DE825008CE9CF /* ToolbarMenuTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B12DDFEC2A8DE825008CE9CF /* ToolbarMenuTests.swift */; }; B15058812AA0A878008B7382 /* OpeningScreenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B15058802AA0A878008B7382 /* OpeningScreenTests.swift */; }; B1664E9E2B163B7A005D4C71 /* CreditCardsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1664E9D2B163B7A005D4C71 /* CreditCardsTests.swift */; }; + B1CA62822C0DB43600D31625 /* MultiWindowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1CA62812C0DB43600D31625 /* MultiWindowTests.swift */; }; B1F90EC12BB3F6B600A4D431 /* ZoomingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1F90EC02BB3F6B600A4D431 /* ZoomingTests.swift */; }; B236204B2B851FE1000B1DE7 /* AddressAutoFillBottomSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B236204A2B851FE0000B1DE7 /* AddressAutoFillBottomSheetView.swift */; }; B236204D2B8673DE000B1DE7 /* AddressScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B236204C2B8673DE000B1DE7 /* AddressScrollView.swift */; }; @@ -1511,6 +1528,7 @@ E15DE7C4293A7B0F00B32667 /* PhotonActionSheetTitleHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E15DE7C3293A7B0F00B32667 /* PhotonActionSheetTitleHeaderView.swift */; }; E16258EF2A83BE0800522742 /* FakespotLoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E16258EE2A83BE0800522742 /* FakespotLoadingView.swift */; }; E16941B42C4E4A2E00FF5F4E /* BrowserViewControllerStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E16941B32C4E4A2E00FF5F4E /* BrowserViewControllerStateTests.swift */; }; + E16941B82C5119A200FF5F4E /* Autocompletable.swift in Sources */ = {isa = PBXBuildFile; fileRef = E16941B72C5119A200FF5F4E /* Autocompletable.swift */; }; E169C6E82979CA0E0017B8D7 /* URLMailTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E169C6E72979CA0E0017B8D7 /* URLMailTests.swift */; }; E16AD22C2A8A7AE800F0AA58 /* FakespotHighlightsCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E16AD22B2A8A7AE800F0AA58 /* FakespotHighlightsCardView.swift */; }; E16C76812ABDC0DB00172DB5 /* FakespotHighlightsCardViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E16C76802ABDC0DB00172DB5 /* FakespotHighlightsCardViewModelTests.swift */; }; @@ -2138,14 +2156,21 @@ 09D94B5E8CF4C06397168F78 /* my */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = my; path = "my.lproj/Default Browser.strings"; sourceTree = ""; }; 09F14D989587092C60A0078F /* ga */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ga; path = ga.lproj/Shared.strings; sourceTree = ""; }; 0A3A4A9EB784F7903FB13B7A /* ms */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ms; path = ms.lproj/LoginManager.strings; sourceTree = ""; }; + 0A4978492C53E63200B1E82A /* TrackingProtectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackingProtectionViewController.swift; sourceTree = ""; }; 0A574D09BF8D9E37D6C9C654 /* bn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bn; path = bn.lproj/ClearHistoryConfirm.strings; sourceTree = ""; }; 0A5B4CE9B0996AE804491134 /* an */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = an; path = an.lproj/Shared.strings; sourceTree = ""; }; 0A734328A164466314ECE4BE /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/Menu.strings; sourceTree = ""; }; + 0A7693602C7DD19500103A6D /* CertificatesViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CertificatesViewModelTests.swift; sourceTree = ""; }; 0A7D41DB98DDB127A2B8C544 /* ms */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ms; path = ms.lproj/Menu.strings; sourceTree = ""; }; 0AC659262BF35854005C614A /* FxAWebViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FxAWebViewModelTests.swift; sourceTree = ""; }; 0AC659282BF493CE005C614A /* MockFxAWebViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockFxAWebViewModel.swift; sourceTree = ""; }; 0AD3EEAB2C2485A7001044E5 /* ThemedCenteredTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemedCenteredTableViewCell.swift; sourceTree = ""; }; 0AE9462E8A8E05CE07D4973D /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoPlist.strings; sourceTree = ""; }; + 0AFF7F632C7784D600265214 /* MockDataCleaner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockDataCleaner.swift; sourceTree = ""; }; + 0AFF7F652C7784F000265214 /* TrackingProtectionModelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrackingProtectionModelTests.swift; sourceTree = ""; }; + 0AFF7F692C7C7BB800265214 /* CertificatesCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CertificatesCell.swift; sourceTree = ""; }; + 0AFF7F6A2C7C7BB900265214 /* CertificatesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CertificatesViewController.swift; sourceTree = ""; }; + 0AFF7F6B2C7C7BB900265214 /* CertificatesViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CertificatesViewModel.swift; sourceTree = ""; }; 0B305E1A1E3A98A900BE0767 /* BookmarksTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarksTests.swift; sourceTree = ""; }; 0B3D670D1E09B90B00C1EFC7 /* AuthenticationTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticationTest.swift; sourceTree = ""; }; 0B414433840C8AA60829B6FB /* ms */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ms; path = ms.lproj/Intro.strings; sourceTree = ""; }; @@ -2820,6 +2845,15 @@ 42CC4F75B4E9E31A4754A9FE /* az */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = az; path = az.lproj/3DTouchActions.strings; sourceTree = ""; }; 42D84A3EBD583A4797682E74 /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/PrivateBrowsing.strings; sourceTree = ""; }; 430057C029E4293100FE51CA /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = "uk.lproj/Edit Card.strings"; sourceTree = ""; }; + 4300B0D92C57B06E00F609C1 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/ActivityStream.strings"; sourceTree = ""; }; + 4300B0DA2C57B06E00F609C1 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/EditAddress.strings"; sourceTree = ""; }; + 4300B0DB2C57B06E00F609C1 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/EnhancedTrackingProtection.strings"; sourceTree = ""; }; + 4300B0DC2C57B06E00F609C1 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/Microsurvey.strings"; sourceTree = ""; }; + 4300B0DD2C57B06E00F609C1 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/ScanQRCode.strings"; sourceTree = ""; }; + 4300B0DE2C57B06E00F609C1 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/SelectCreditCard.strings"; sourceTree = ""; }; + 4300B0DF2C57B06E00F609C1 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/TabLocation.strings"; sourceTree = ""; }; + 4300B0E02C57B06E00F609C1 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/Toolbar.strings"; sourceTree = ""; }; + 4300B0E12C57B06E00F609C1 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/UpdateCard.strings"; sourceTree = ""; }; 4300B70A2C298D9C0099DEEC /* lo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lo; path = lo.lproj/ActivityStream.strings; sourceTree = ""; }; 4300B70B2C298D9C0099DEEC /* lo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lo; path = lo.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; 4300B70C2C298D9C0099DEEC /* lo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lo; path = lo.lproj/LoginsHelper.strings; sourceTree = ""; }; @@ -2922,6 +2956,7 @@ 430405402C298AEC00EB6E76 /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/QRCode.strings; sourceTree = ""; }; 43041686293E0B3100BBFD8A /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Alerts.strings; sourceTree = ""; }; 43041687293E0B3100BBFD8A /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/TabsTray.strings; sourceTree = ""; }; + 430439F42C57B34D005518C3 /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/Toolbar.strings; sourceTree = ""; }; 43045C9729E428A800B5BD9B /* si */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = si; path = si.lproj/Alert.strings; sourceTree = ""; }; 43045C9829E428A800B5BD9B /* si */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = si; path = "si.lproj/Edit Card.strings"; sourceTree = ""; }; 43045C9929E428A800B5BD9B /* si */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = si; path = si.lproj/EditCard.strings; sourceTree = ""; }; @@ -2930,6 +2965,8 @@ 43045C9C29E428A800B5BD9B /* si */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = si; path = si.lproj/ResearchSurface.strings; sourceTree = ""; }; 43045C9D29E428A800B5BD9B /* si */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = si; path = si.lproj/Settings.strings; sourceTree = ""; }; 43045C9E29E428A800B5BD9B /* si */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = si; path = si.lproj/SnackBar.strings; sourceTree = ""; }; + 4304BD432C7C9DCB0063AF05 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/NativeErrorPage.strings; sourceTree = ""; }; + 4304EDF32C60EABA00F3C9DE /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/Toolbar.strings; sourceTree = ""; }; 43050A002ACAE09E002684A4 /* pa-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pa-IN"; path = "pa-IN.lproj/Share.strings"; sourceTree = ""; }; 430514F52AD4195A00188114 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Share.strings; sourceTree = ""; }; 430514F62AD4195A00188114 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/TabLocation.strings; sourceTree = ""; }; @@ -2952,6 +2989,7 @@ 4307A24229F69BC400570352 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/Notification.strings; sourceTree = ""; }; 4307A24329F69BC400570352 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/ZoomPageBar.strings; sourceTree = ""; }; 4308262A2BF506EC002C58F9 /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/Microsurvey.strings; sourceTree = ""; }; + 430832542C7C9E4100F58087 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/NativeErrorPage.strings; sourceTree = ""; }; 43087EE12BF50875005EAA6C /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Microsurvey.strings; sourceTree = ""; }; 4308A4E52A52E38C001D652E /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/SelectCreditCard.strings; sourceTree = ""; }; 430901D52C3C00DB00D5F3AC /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/ActivityStream.strings; sourceTree = ""; }; @@ -2971,9 +3009,11 @@ 430A4A422A0BCCB0009D4128 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Upgrade.strings; sourceTree = ""; }; 430AA4CE29FFD516005AB95C /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Onboarding.strings; sourceTree = ""; }; 430AA4CF29FFD516005AB95C /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/ResearchSurface.strings; sourceTree = ""; }; + 430ADCC62C57B00800B0A55B /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/Toolbar.strings"; sourceTree = ""; }; 430B5C042C20544900652135 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/ActivityStream.strings; sourceTree = ""; }; 430B5C052C20544900652135 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/AddressToolbar.strings; sourceTree = ""; }; 430B5C062C20544900652135 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; + 430B81E02C57B198008C994B /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/Toolbar.strings; sourceTree = ""; }; 430B8F7A2AC1A2B500CDB6CC /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/Share.strings; sourceTree = ""; }; 430B8F7B2AC1A2B500CDB6CC /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/TabLocation.strings; sourceTree = ""; }; 430B9F052A24C0E200DC4B8E /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/DisplayCard.strings; sourceTree = ""; }; @@ -3005,6 +3045,7 @@ 430E7BBF293E0D2B005A10AD /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/TabsTray.strings"; sourceTree = ""; }; 430E91272B5549E300F90C14 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/TabToolbar.strings; sourceTree = ""; }; 430E9CD329225D8300DC418A /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/SearchHeaderTitle.strings; sourceTree = ""; }; + 430EC8982C7C9B5100759193 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/NativeErrorPage.strings; sourceTree = ""; }; 430EE26429BA6968009B5023 /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/EngagementNotification.strings"; sourceTree = ""; }; 430EE26529BA6968009B5023 /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/Onboarding.strings"; sourceTree = ""; }; 430EE26629BA6968009B5023 /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/ResearchSurface.strings"; sourceTree = ""; }; @@ -3074,6 +3115,7 @@ 4314C48E2C2051D0009E4D70 /* hsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hsb; path = hsb.lproj/AddressToolbar.strings; sourceTree = ""; }; 4314C48F2C2051D0009E4D70 /* hsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hsb; path = hsb.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; 4314C4902C2051D0009E4D70 /* hsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hsb; path = hsb.lproj/QRCode.strings; sourceTree = ""; }; + 4315028A2C57B23B005BB18E /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/Toolbar.strings; sourceTree = ""; }; 4315209A29BA68CF00D729C1 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/EngagementNotification.strings; sourceTree = ""; }; 4315209B29BA68CF00D729C1 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Onboarding.strings; sourceTree = ""; }; 4315209C29BA68CF00D729C1 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/ResearchSurface.strings; sourceTree = ""; }; @@ -3123,6 +3165,7 @@ 43177EC82A49A96800A22B9C /* eo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eo; path = eo.lproj/Upgrade.strings; sourceTree = ""; }; 4317873D2A655B9100A38B47 /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/SelectCreditCard.strings"; sourceTree = ""; }; 4317CE012B0B76F500BD4D5B /* hsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hsb; path = hsb.lproj/FirefoxHomepage.strings; sourceTree = ""; }; + 431813AE2C57B017007C3268 /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/Toolbar.strings"; sourceTree = ""; }; 43183D2F293E0C44009F788D /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Alerts.strings"; sourceTree = ""; }; 43183D30293E0C44009F788D /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/TabsTray.strings"; sourceTree = ""; }; 431853AA2A0911440099B0E0 /* gl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gl; path = "gl.lproj/Default Browser.strings"; sourceTree = ""; }; @@ -3190,6 +3233,8 @@ 431D38832A49AADF00A8BE49 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/UpdateCard.strings; sourceTree = ""; }; 431D44862AEFC97A00B92311 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Shopping.strings; sourceTree = ""; }; 431D65DB292B94F800BB9A90 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/SearchHeaderTitle.strings; sourceTree = ""; }; + 431D82792C57B3F600062D71 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Toolbar.strings; sourceTree = ""; }; + 431D8FEE2C7C9B61001E041F /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/NativeErrorPage.strings; sourceTree = ""; }; 431DACF12A24BF0C00D7CE85 /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/DisplayCard.strings"; sourceTree = ""; }; 431DE38F2B023BC600BF06E3 /* eo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eo; path = eo.lproj/Shopping.strings; sourceTree = ""; }; 431DF6C52C20532100ADD05A /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/ActivityStream.strings; sourceTree = ""; }; @@ -3234,6 +3279,8 @@ 4320E17E2A16E5A9009A4B5F /* CreditCardExtras.ios.mjs */ = {isa = PBXFileReference; lastKnownFileType = text; path = CreditCardExtras.ios.mjs; sourceTree = ""; }; 43211DCD2C3C045A00E4CA4D /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/EditAddress.strings; sourceTree = ""; }; 43211DCE2C3C045A00E4CA4D /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/ScanQRCode.strings; sourceTree = ""; }; + 43211E6B2C57B11F003F9FAF /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/Toolbar.strings; sourceTree = ""; }; + 4321F0522C7C9BE00001D0DD /* hsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hsb; path = hsb.lproj/NativeErrorPage.strings; sourceTree = ""; }; 4321FAB82B8CA9A70046DCA0 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/KeyboardAccessory.strings; sourceTree = ""; }; 4321FAB92B8CA9A70046DCA0 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/PasswordAutofill.strings; sourceTree = ""; }; 4322680229225CA3008F8C47 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/SearchHeaderTitle.strings; sourceTree = ""; }; @@ -3320,6 +3367,8 @@ 4328FAD229D1B35100DABB9A /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/SnackBar.strings; sourceTree = ""; }; 4329591628D8852300D8BC68 /* eo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eo; path = eo.lproj/JumpBackIn.strings; sourceTree = ""; }; 4329591728D8852400D8BC68 /* eo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eo; path = eo.lproj/ToolbarLocation.strings; sourceTree = ""; }; + 432985492C57B47B00FE7CA2 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Toolbar.strings; sourceTree = ""; }; + 4329BDC32C57B1BF007F18C7 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/Toolbar.strings; sourceTree = ""; }; 432A14A0292B93900059D21F /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/SearchHeaderTitle.strings; sourceTree = ""; }; 432A50F52B14B165003EE621 /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/FirefoxHomepage.strings; sourceTree = ""; }; 432A91F328B39EAE006CE202 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/JumpBackIn.strings"; sourceTree = ""; }; @@ -3387,6 +3436,8 @@ 432FFF5B2C2051FC00ADC8E7 /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/AddressToolbar.strings; sourceTree = ""; }; 432FFF5C2C2051FC00ADC8E7 /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; 432FFF5D2C2051FC00ADC8E7 /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/QRCode.strings; sourceTree = ""; }; + 433020F72C57B40600AF5EE2 /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/Credentials.strings"; sourceTree = ""; }; + 433020F82C57B40700AF5EE2 /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/EditAddress.strings"; sourceTree = ""; }; 4330773429F69CC7006BC636 /* pa-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pa-IN"; path = "pa-IN.lproj/Notification.strings"; sourceTree = ""; }; 4330773529F69CC7006BC636 /* pa-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pa-IN"; path = "pa-IN.lproj/ZoomPageBar.strings"; sourceTree = ""; }; 4330958B2B83917C00FAD61B /* hsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hsb; path = hsb.lproj/KeyboardAccessory.strings; sourceTree = ""; }; @@ -3420,6 +3471,7 @@ 433204162B68010A00ECE7AC /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/TabToolbar.strings; sourceTree = ""; }; 4332247E2A5C203D00B07818 /* pa-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pa-IN"; path = "pa-IN.lproj/CustomizeFirefoxHome.strings"; sourceTree = ""; }; 4332247F2A5C203D00B07818 /* pa-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pa-IN"; path = "pa-IN.lproj/SelectCreditCard.strings"; sourceTree = ""; }; + 43328A002C57B38800444F7E /* pa-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pa-IN"; path = "pa-IN.lproj/Toolbar.strings"; sourceTree = ""; }; 4332AE0B2B305F6200885C18 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/ContextualHints.strings; sourceTree = ""; }; 4332AE0C2B305F6200885C18 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/CredentialProvider.strings; sourceTree = ""; }; 4332AE0D2B305F6200885C18 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Credentials.strings; sourceTree = ""; }; @@ -3444,6 +3496,7 @@ 433429FC29BA6CB1005B05B0 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/ResearchSurface.strings"; sourceTree = ""; }; 43349476294745560027B85C /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sq; path = sq.lproj/Alerts.strings; sourceTree = ""; }; 43349477294745560027B85C /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sq; path = sq.lproj/TabsTray.strings; sourceTree = ""; }; + 4334C4F02C7C9EDE00FBEFCF /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/NativeErrorPage.strings; sourceTree = ""; }; 4334CA5C2BB195DE00B29D5B /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/BottomSheet.strings; sourceTree = ""; }; 4334DC2B29225A0B0064F9CD /* es-AR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-AR"; path = "es-AR.lproj/SearchHeaderTitle.strings"; sourceTree = ""; }; 4335A79E2B554A630087D279 /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/TabToolbar.strings; sourceTree = ""; }; @@ -3474,6 +3527,9 @@ 4337C9D62A5C211300987DEB /* tt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tt; path = tt.lproj/SelectCreditCard.strings; sourceTree = ""; }; 4337C9D72A5C211300987DEB /* tt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tt; path = tt.lproj/UpdateCard.strings; sourceTree = ""; }; 4337E1142AEFC993007579AC /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/Shopping.strings"; sourceTree = ""; }; + 43383DE92C57AF94001BCD0E /* co */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = co; path = co.lproj/EditAddress.strings; sourceTree = ""; }; + 43383DEA2C57AF94001BCD0E /* co */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = co; path = co.lproj/ScanQRCode.strings; sourceTree = ""; }; + 43383DEB2C57AF94001BCD0E /* co */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = co; path = co.lproj/Toolbar.strings; sourceTree = ""; }; 4338633128CF4C5100564CA4 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/JumpBackIn.strings; sourceTree = ""; }; 4338633228CF4C5100564CA4 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/ToolbarLocation.strings; sourceTree = ""; }; 4338F50C2C453D8800851075 /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/EditAddress.strings; sourceTree = ""; }; @@ -3484,6 +3540,7 @@ 4339390B28B39F8F001B8301 /* fil */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fil; path = fil.lproj/BookmarkPanel.strings; sourceTree = ""; }; 433942802AC1A24E009B3F3C /* gl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gl; path = gl.lproj/Share.strings; sourceTree = ""; }; 433942812AC1A24E009B3F3C /* gl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gl; path = gl.lproj/TabLocation.strings; sourceTree = ""; }; + 433954F62C57B0A900EC1EAE /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Toolbar.strings; sourceTree = ""; }; 4339D0B32BA85A7400521234 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/BottomSheet.strings"; sourceTree = ""; }; 4339E2A02A49A9250071F5CF /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Footer.strings; sourceTree = ""; }; 4339E2A12A49A9250071F5CF /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/RememberCard.strings; sourceTree = ""; }; @@ -3528,6 +3585,8 @@ 433D51742C3C0478005D19E3 /* ug */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ug; path = ug.lproj/EditAddress.strings; sourceTree = ""; }; 433D51752C3C0478005D19E3 /* ug */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ug; path = ug.lproj/ScanQRCode.strings; sourceTree = ""; }; 433D53CB2B0B798700F5786C /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/FirefoxHomepage.strings; sourceTree = ""; }; + 433D64C62C60EE1100CEC389 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/ScanQRCode.strings; sourceTree = ""; }; + 433D64C72C60EE1100CEC389 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Toolbar.strings; sourceTree = ""; }; 433DD5FB2AEFC6EB00C80D13 /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/Shopping.strings; sourceTree = ""; }; 433E0DEC26FCE37C00B78CC5 /* ne-NP */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "ne-NP"; path = "ne-NP.lproj/Default Browser.strings"; sourceTree = ""; }; 433E11402A124A2C00B4C650 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/FirefoxSync.strings; sourceTree = ""; }; @@ -3549,6 +3608,7 @@ 433FD5CD2A52E55400E7B4CB /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/SelectCreditCard.strings; sourceTree = ""; }; 433FF82028BCD74B006A70C8 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/JumpBackIn.strings; sourceTree = ""; }; 433FF82128BCD74B006A70C8 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/ToolbarLocation.strings; sourceTree = ""; }; + 43400BC22C7C9C7100D8ECE9 /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/NativeErrorPage.strings; sourceTree = ""; }; 434073A929E4271900458D21 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = "hu.lproj/Edit Card.strings"; sourceTree = ""; }; 434094D1293E0B6000CAD2F5 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Alerts.strings; sourceTree = ""; }; 434094D2293E0B6000CAD2F5 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/TabsTray.strings; sourceTree = ""; }; @@ -3595,6 +3655,7 @@ 4344D02F292259CE00B12BF8 /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/SearchHeaderTitle.strings"; sourceTree = ""; }; 4345055729E4289200F137B6 /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/Settings.strings"; sourceTree = ""; }; 4345441C26D2E52600D5EEAA /* SearchTermGroupsUtility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTermGroupsUtility.swift; sourceTree = ""; }; + 4345E88F2C7C9A5B00FC0D7D /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/NativeErrorPage.strings; sourceTree = ""; }; 43464A7329E4264400C3CA87 /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/Edit Card.strings"; sourceTree = ""; }; 4346FF07295BA6A200F4D220 /* CreditCardSettingsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreditCardSettingsViewController.swift; sourceTree = ""; }; 4347022C2AC1A19200BEB809 /* dsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = dsb; path = dsb.lproj/Share.strings; sourceTree = ""; }; @@ -3664,12 +3725,15 @@ 434E1FDD299A549B00F79A7B /* fil */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fil; path = fil.lproj/Localizable.strings; sourceTree = ""; }; 434E733625EED32E006D3BDE /* BrowserViewController+URLBarDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BrowserViewController+URLBarDelegate.swift"; sourceTree = ""; }; 434EA84429225CDE0039FBE1 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/SearchHeaderTitle.strings; sourceTree = ""; }; + 434ED7EC2C57B1D4000C86E4 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Toolbar.strings; sourceTree = ""; }; 434EE0B429225A31005D030A /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/SearchHeaderTitle.strings"; sourceTree = ""; }; 434F05982A0BCD36008B1057 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/FirefoxSync.strings; sourceTree = ""; }; 434F05992A0BCD36008B1057 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Upgrade.strings; sourceTree = ""; }; 434F78E429225CD10037862C /* si */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = si; path = si.lproj/SearchHeaderTitle.strings; sourceTree = ""; }; + 434FE6142C7C9C7F00C44560 /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/NativeErrorPage.strings; sourceTree = ""; }; 434FFA2E2AEFC8EE008CAD12 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/Shopping.strings; sourceTree = ""; }; 434FFF292ACAE1C000680A93 /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/Share.strings"; sourceTree = ""; }; + 43500A6B2C7C9EFB008E385F /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/NativeErrorPage.strings"; sourceTree = ""; }; 43509CB3291BF65400DF631E /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/JumpBackIn.strings; sourceTree = ""; }; 43509CB4291BF65400DF631E /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/ToolbarLocation.strings; sourceTree = ""; }; 4350E75F2AC1A19C004BA97D /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/Share.strings; sourceTree = ""; }; @@ -3704,6 +3768,7 @@ 4353DE762AC1A267005E189E /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/TabLocation.strings; sourceTree = ""; }; 4354415E29EF0682004EDDAF /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = "eu.lproj/Edit Card.strings"; sourceTree = ""; }; 4354485A2B0B767600F22F17 /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/FirefoxHomepage.strings; sourceTree = ""; }; + 43548AB92C7C9A6A0004C846 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/NativeErrorPage.strings; sourceTree = ""; }; 43548D222A2DFAB400E28766 /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/Footer.strings; sourceTree = ""; }; 43548D232A2DFAB400E28766 /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/RememberCard.strings; sourceTree = ""; }; 43548D242A2DFAB400E28766 /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/UpdateCard.strings; sourceTree = ""; }; @@ -3737,6 +3802,7 @@ 43569A322A0BCB0E00BCDA77 /* es-AR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-AR"; path = "es-AR.lproj/Upgrade.strings"; sourceTree = ""; }; 4356A5C82BE8F2C300A39580 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Microsurvey.strings; sourceTree = ""; }; 4356DB4F2AEFC6FF00ADF289 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Shopping.strings; sourceTree = ""; }; + 43571C312C57B52200A2DD89 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Toolbar.strings; sourceTree = ""; }; 43571D57293E0A2000EF42C5 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Alerts.strings; sourceTree = ""; }; 43571D58293E0A2000EF42C5 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/TabsTray.strings; sourceTree = ""; }; 435784DC2A24C0C80046746E /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/DisplayCard.strings"; sourceTree = ""; }; @@ -3781,6 +3847,8 @@ 435A759129E4285900778D0C /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Edit Card.strings"; sourceTree = ""; }; 435AC01A2A52E65800B0156A /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/CustomizeFirefoxHome.strings; sourceTree = ""; }; 435AC01B2A52E65800B0156A /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/SelectCreditCard.strings; sourceTree = ""; }; + 435B35652C57AFA200F1C0BE /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/ScanQRCode.strings; sourceTree = ""; }; + 435B35662C57AFA200F1C0BE /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Toolbar.strings; sourceTree = ""; }; 435B625129D1B3D2005FBA5D /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Alert.strings; sourceTree = ""; }; 435B625229D1B3D2005FBA5D /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/BiometricAuthentication.strings; sourceTree = ""; }; 435B625329D1B3D2005FBA5D /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/EditCard.strings; sourceTree = ""; }; @@ -3877,6 +3945,7 @@ 4361333D2A091002009C8BBC /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Notification.strings; sourceTree = ""; }; 4361333E2A091002009C8BBC /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Settings.strings; sourceTree = ""; }; 4361333F2A091002009C8BBC /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/SnackBar.strings; sourceTree = ""; }; + 4361916A2C7C9D72005B31E3 /* pa-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pa-IN"; path = "pa-IN.lproj/NativeErrorPage.strings"; sourceTree = ""; }; 4361DF292C453DB200CE1295 /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/BottomSheet.strings"; sourceTree = ""; }; 4361DF2A2C453DB200CE1295 /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/ContextualHints.strings"; sourceTree = ""; }; 4361DF2B2C453DB200CE1295 /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/CredentialProvider.strings"; sourceTree = ""; }; @@ -3894,6 +3963,7 @@ 4363AC042A0BCE1D00A18A25 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/Upgrade.strings"; sourceTree = ""; }; 4363B6662BE8F3C2004D0CED /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/Microsurvey.strings; sourceTree = ""; }; 4363FDF12AEFC70A00FC80F2 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Shopping.strings; sourceTree = ""; }; + 4364018E2C7C9AE700FF64C6 /* es-AR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-AR"; path = "es-AR.lproj/NativeErrorPage.strings"; sourceTree = ""; }; 436403C428EAFBD6008A7F9B /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/JumpBackIn.strings; sourceTree = ""; }; 436403C528EAFBD6008A7F9B /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/ToolbarLocation.strings; sourceTree = ""; }; 43645E3629BA68D900CE6033 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/EngagementNotification.strings; sourceTree = ""; }; @@ -3953,6 +4023,7 @@ 4366A0B429DAEDF500DA8329 /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/ResearchSurface.strings; sourceTree = ""; }; 4366A0B529DAEDF500DA8329 /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/Settings.strings; sourceTree = ""; }; 4366A0B629DAEDF500DA8329 /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/SnackBar.strings; sourceTree = ""; }; + 436705DA2C57B3D7002C7FE6 /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/Toolbar.strings; sourceTree = ""; }; 43680DD7293E0A160013269F /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/Alerts.strings; sourceTree = ""; }; 43680DD8293E0A160013269F /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/TabsTray.strings; sourceTree = ""; }; 43682B4D28B39CFF00F2C865 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/JumpBackIn.strings; sourceTree = ""; }; @@ -3972,6 +4043,7 @@ 4368ED3A28B39C1300A50B64 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/JumpBackIn.strings"; sourceTree = ""; }; 4368ED3B28B39C1300A50B64 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/ToolbarLocation.strings"; sourceTree = ""; }; 4368EE5B292B95C0005A0673 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/SearchHeaderTitle.strings; sourceTree = ""; }; + 436957A82C57AFB100B079FE /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/ScanQRCode.strings; sourceTree = ""; }; 43696A66292B9719002C3EAD /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/SearchHeaderTitle.strings"; sourceTree = ""; }; 436985E428B39FFE00198055 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/JumpBackIn.strings"; sourceTree = ""; }; 436985E528B39FFE00198055 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/ToolbarLocation.strings"; sourceTree = ""; }; @@ -4058,6 +4130,7 @@ 436F42FD2A52E6E100E4C2A2 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/SelectCreditCard.strings; sourceTree = ""; }; 436F68302AE68E3700BF46C9 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Share.strings; sourceTree = ""; }; 436F84692A4070C800279DAE /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/Onboarding.strings; sourceTree = ""; }; + 436FC4912C7C9D810083D324 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/NativeErrorPage.strings; sourceTree = ""; }; 436FC6A82A3734F30010789D /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Footer.strings; sourceTree = ""; }; 436FC6A92A3734F30010789D /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/RememberCard.strings; sourceTree = ""; }; 436FC6AA2A3734F40010789D /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/UpdateCard.strings; sourceTree = ""; }; @@ -4095,6 +4168,7 @@ 437327D62B305F6E0090FCBC /* dsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = dsb; path = dsb.lproj/FirefoxLogins.strings; sourceTree = ""; }; 437327D72B305F6E0090FCBC /* dsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = dsb; path = dsb.lproj/LoginsHelper.strings; sourceTree = ""; }; 437330442B554BE600BB1AFC /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/TabToolbar.strings"; sourceTree = ""; }; + 4373440E2C7C9AF500457D4D /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/NativeErrorPage.strings"; sourceTree = ""; }; 43736FDD2A406F2900546F4A /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/Footer.strings; sourceTree = ""; }; 43736FDE2A406F2900546F4A /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/RememberCard.strings; sourceTree = ""; }; 43736FDF2A406F2900546F4A /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/UpdateCard.strings; sourceTree = ""; }; @@ -4117,6 +4191,7 @@ 4374AB3C2C205468004C030B /* su */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = su; path = su.lproj/QRCode.strings; sourceTree = ""; }; 4374AC0128B39AE700BE4998 /* bo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bo; path = bo.lproj/BookmarkPanel.strings; sourceTree = ""; }; 4374C6AF2AEFC75A00907BB4 /* hsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hsb; path = hsb.lproj/Shopping.strings; sourceTree = ""; }; + 437511122C60EA6E001E649A /* br */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = br; path = br.lproj/Toolbar.strings; sourceTree = ""; }; 437579372B0B773B005F2178 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/FirefoxHomepage.strings; sourceTree = ""; }; 437582642BE8F37A006F43C6 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Microsurvey.strings; sourceTree = ""; }; 43758DC32B0B78E300DA928C /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/FirefoxHomepage.strings; sourceTree = ""; }; @@ -4158,6 +4233,9 @@ 4378BD402B14B2C5004CB18D /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/FirefoxHomepage.strings; sourceTree = ""; }; 4378C733293E09C200D0C1C7 /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/Alerts.strings; sourceTree = ""; }; 4378C736293E09C200D0C1C7 /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/TabsTray.strings; sourceTree = ""; }; + 4378EFA72C57AFBF0031897A /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/EditAddress.strings; sourceTree = ""; }; + 4378EFA82C57AFBF0031897A /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/ScanQRCode.strings; sourceTree = ""; }; + 4378EFA92C57AFBF0031897A /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Toolbar.strings; sourceTree = ""; }; 4378F1B329BA6AD200D37BDC /* lo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lo; path = lo.lproj/EngagementNotification.strings; sourceTree = ""; }; 4378F1B429BA6AD200D37BDC /* lo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lo; path = lo.lproj/Onboarding.strings; sourceTree = ""; }; 4378F1B529BA6AD200D37BDC /* lo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lo; path = lo.lproj/ResearchSurface.strings; sourceTree = ""; }; @@ -4169,6 +4247,7 @@ 43790BF4292B94C500968A85 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/SearchHeaderTitle.strings; sourceTree = ""; }; 4379F313293E0A5A0029C76D /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/Alerts.strings"; sourceTree = ""; }; 4379F314293E0A5A0029C76D /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/TabsTray.strings"; sourceTree = ""; }; + 437A0B5A2C57B22500FCF751 /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/Toolbar.strings; sourceTree = ""; }; 437A857727E43FE100E42764 /* FxAWebViewTelemetry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FxAWebViewTelemetry.swift; sourceTree = ""; }; 437A88922C2054C100031657 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; 437A88932C2054C100031657 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/QRCode.strings; sourceTree = ""; }; @@ -4183,6 +4262,7 @@ 437AA7E72C32C753001A7826 /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; 437ABC202B8393050027444C /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/KeyboardAccessory.strings"; sourceTree = ""; }; 437ABC212B8393050027444C /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/PasswordAutofill.strings"; sourceTree = ""; }; + 437AE30F2C7C9A0C0053F7F3 /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/NativeErrorPage.strings; sourceTree = ""; }; 437B0B2C2A6E941500DD9F66 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/Alert.strings; sourceTree = ""; }; 437B0B2D2A6E941500DD9F66 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/Alerts.strings; sourceTree = ""; }; 437B0B2E2A6E941500DD9F66 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/CustomizeFirefoxHome.strings; sourceTree = ""; }; @@ -4223,6 +4303,7 @@ 437CC8E02BE8F1D100CB4385 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Microsurvey.strings; sourceTree = ""; }; 437CCDC82A124D7000A10106 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/FirefoxSync.strings; sourceTree = ""; }; 437CCDC92A124D7000A10106 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Upgrade.strings; sourceTree = ""; }; + 437CD9FF2C7C9A8B00A34E3E /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/NativeErrorPage.strings; sourceTree = ""; }; 437CFF60293E0AD6001F1948 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Alerts.strings; sourceTree = ""; }; 437CFF61293E0AD6001F1948 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/TabsTray.strings; sourceTree = ""; }; 437D59F62AC1A28E00D93351 /* hsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hsb; path = hsb.lproj/Share.strings; sourceTree = ""; }; @@ -4237,9 +4318,17 @@ 437DFCD12AEFC7B500F701AE /* ka */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ka; path = ka.lproj/Shopping.strings; sourceTree = ""; }; 437E193A29F69ABA00F743E0 /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/Notification.strings"; sourceTree = ""; }; 437E193B29F69ABB00F743E0 /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/ZoomPageBar.strings"; sourceTree = ""; }; + 437E27022C7C9D8F00CE7490 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/NativeErrorPage.strings"; sourceTree = ""; }; 437E34352C3C03870025BFA9 /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/ResearchSurface.strings; sourceTree = ""; }; 437E6394293E0BF6008C190A /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Alerts.strings; sourceTree = ""; }; 437E6395293E0BF6008C190A /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/TabsTray.strings; sourceTree = ""; }; + 437E67412C57B4AB00A8791A /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/ActivityStream.strings; sourceTree = ""; }; + 437E67422C57B4AB00A8791A /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/AddressToolbar.strings; sourceTree = ""; }; + 437E67432C57B4AB00A8791A /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/EditAddress.strings; sourceTree = ""; }; + 437E67442C57B4AB00A8791A /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; + 437E67452C57B4AB00A8791A /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/QRCode.strings; sourceTree = ""; }; + 437E67462C57B4AB00A8791A /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/ScanQRCode.strings; sourceTree = ""; }; + 437E67472C57B4AB00A8791A /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/Toolbar.strings; sourceTree = ""; }; 437EA6282B83915700DE9E71 /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/KeyboardAccessory.strings; sourceTree = ""; }; 437EA6292B83915700DE9E71 /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/PasswordAutofill.strings; sourceTree = ""; }; 437F339929EF08FC003E82BC /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = "rm.lproj/Edit Card.strings"; sourceTree = ""; }; @@ -4312,8 +4401,10 @@ 43858A832AF902E40010A4B7 /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/Shopping.strings; sourceTree = ""; }; 438605AE2C3C010600C4CD23 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/EditAddress.strings; sourceTree = ""; }; 438605AF2C3C010600C4CD23 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/ScanQRCode.strings; sourceTree = ""; }; + 438613FD2C7C9E05005E3A65 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/NativeErrorPage.strings; sourceTree = ""; }; 43866D292B8CAA5C00808188 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/KeyboardAccessory.strings; sourceTree = ""; }; 43866D2A2B8CAA5C00808188 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/PasswordAutofill.strings; sourceTree = ""; }; + 4386E32C2C57AFCF00C97DCD /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Toolbar.strings; sourceTree = ""; }; 438712062BCD42B6003E6870 /* eo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eo; path = eo.lproj/BottomSheet.strings; sourceTree = ""; }; 4387326C28B39ED800FB7454 /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/BookmarkPanel.strings; sourceTree = ""; }; 43879B6F2B399B6700015597 /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/CredentialProvider.strings; sourceTree = ""; }; @@ -4353,6 +4444,7 @@ 438998AF2B42D59F00591121 /* hy-AM */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "hy-AM"; path = "hy-AM.lproj/Credentials.strings"; sourceTree = ""; }; 438998B02B42D59F00591121 /* hy-AM */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "hy-AM"; path = "hy-AM.lproj/FirefoxLogins.strings"; sourceTree = ""; }; 438998B12B42D59F00591121 /* hy-AM */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "hy-AM"; path = "hy-AM.lproj/LoginsHelper.strings"; sourceTree = ""; }; + 4389B08A2C57B27500C52A68 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Toolbar.strings; sourceTree = ""; }; 4389EEC42AC1A40100C748E8 /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/TabLocation.strings; sourceTree = ""; }; 438A4B202C20535600621E8F /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/ActivityStream.strings; sourceTree = ""; }; 438A4B212C20535600621E8F /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/AddressToolbar.strings; sourceTree = ""; }; @@ -4364,12 +4456,17 @@ 438AEF732AC1A29C00908F58 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/TabLocation.strings; sourceTree = ""; }; 438B138E28B39FB900E53858 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/JumpBackIn.strings; sourceTree = ""; }; 438B138F28B39FB900E53858 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/ToolbarLocation.strings; sourceTree = ""; }; + 438B16422C57B39C007BD1A3 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/EditAddress.strings; sourceTree = ""; }; + 438B16432C57B39C007BD1A3 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/ScanQRCode.strings; sourceTree = ""; }; + 438B16442C57B39C007BD1A3 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Toolbar.strings; sourceTree = ""; }; 438B41BA2B5E865F00BA2E52 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/CredentialProvider.strings; sourceTree = ""; }; 438B41BB2B5E865F00BA2E52 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Credentials.strings; sourceTree = ""; }; 438B41BC2B5E865F00BA2E52 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/FirefoxLogins.strings; sourceTree = ""; }; 438B41BD2B5E865F00BA2E52 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/LoginsHelper.strings; sourceTree = ""; }; 438B41BE2B5E865F00BA2E52 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/TabToolbar.strings; sourceTree = ""; }; + 438C39022C57B042005CD2CC /* es-AR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-AR"; path = "es-AR.lproj/Toolbar.strings"; sourceTree = ""; }; 438C427F29D1B1EB0088717D /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/SnackBar.strings; sourceTree = ""; }; + 438C433E2C7C9D9F005AB958 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/NativeErrorPage.strings"; sourceTree = ""; }; 438C789C2BE8F22600265DFC /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/Microsurvey.strings"; sourceTree = ""; }; 438C8BD62C3C039500F49EC2 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/EditAddress.strings; sourceTree = ""; }; 438C8BD72C3C039500F49EC2 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/ScanQRCode.strings; sourceTree = ""; }; @@ -4387,6 +4484,7 @@ 438DF00B2959B85B005E4C8F /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/SearchHeaderTitle.strings"; sourceTree = ""; }; 438DF00C2959B85B005E4C8F /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/TabsTray.strings"; sourceTree = ""; }; 438E69C22BA85A1200DE4241 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/BottomSheet.strings; sourceTree = ""; }; + 438E812A2C57B30F004E1492 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/EditAddress.strings; sourceTree = ""; }; 438EA1322C298BCA00EF793D /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/ActivityStream.strings; sourceTree = ""; }; 438EA1332C298BCA00EF793D /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/AddressToolbar.strings; sourceTree = ""; }; 438EA1342C298BCA00EF793D /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; @@ -4407,9 +4505,14 @@ 43907D302C2050FB00DA0338 /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/EnhancedTrackingProtection.strings"; sourceTree = ""; }; 43907D312C2050FB00DA0338 /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/QRCode.strings"; sourceTree = ""; }; 43907E4C2B42D4550053503C /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Credentials.strings; sourceTree = ""; }; + 4390F7E12C57B1E700F52D3C /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/EditAddress.strings; sourceTree = ""; }; + 4390F7E22C57B1E700F52D3C /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/ScanQRCode.strings; sourceTree = ""; }; + 4390F7E32C57B1E800F52D3C /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Toolbar.strings; sourceTree = ""; }; + 4391326B2C57B0BB00805AE1 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Toolbar.strings; sourceTree = ""; }; 43914B522B4C115A0028781C /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/ContextualHints.strings; sourceTree = ""; }; 43914B532B4C115B0028781C /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/Credentials.strings; sourceTree = ""; }; 43914B542B4C115B0028781C /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/FirefoxHomepage.strings; sourceTree = ""; }; + 4391631F2C60EA8C00263662 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Toolbar.strings; sourceTree = ""; }; 43922AF72BCD4442000D4B12 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/BottomSheet.strings; sourceTree = ""; }; 4392732B2A6E95B50014E253 /* su */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = su; path = su.lproj/Alert.strings; sourceTree = ""; }; 4392732C2A6E95B50014E253 /* su */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = su; path = su.lproj/DisplayCard.strings; sourceTree = ""; }; @@ -4445,6 +4548,8 @@ 43939B4F29E4277D00ADEA2A /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = "kab.lproj/Edit Card.strings"; sourceTree = ""; }; 4393C0C32A24BFE90013D0A2 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/DisplayCard.strings; sourceTree = ""; }; 439404FD29E4273000BCE280 /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = "ia.lproj/Edit Card.strings"; sourceTree = ""; }; + 43940EE22C7C9C0D000AA60B /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/NativeErrorPage.strings; sourceTree = ""; }; + 439410F22C7C9E1400328C59 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/NativeErrorPage.strings; sourceTree = ""; }; 43942A67293E0A64001BD183 /* eo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eo; path = eo.lproj/Alerts.strings; sourceTree = ""; }; 43942A68293E0A64001BD183 /* eo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eo; path = eo.lproj/SearchHeaderTitle.strings; sourceTree = ""; }; 43942A69293E0A64001BD183 /* eo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eo; path = eo.lproj/TabsTray.strings; sourceTree = ""; }; @@ -4454,6 +4559,7 @@ 439532AD28BE8D9B00B79237 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/ToolbarLocation.strings; sourceTree = ""; }; 4395FBDC2B5E85D200AA7AF3 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/TabToolbar.strings; sourceTree = ""; }; 43960B552ADD551700E3DF6B /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/Share.strings; sourceTree = ""; }; + 43961E7E2C57B15B00762948 /* hsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hsb; path = hsb.lproj/Toolbar.strings; sourceTree = ""; }; 43962D222A24C16600934337 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/DisplayCard.strings; sourceTree = ""; }; 4396410329C87A6700F74173 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/Alert.strings"; sourceTree = ""; }; 4396410429C87A6700F74173 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/BiometricAuthentication.strings"; sourceTree = ""; }; @@ -4461,9 +4567,11 @@ 4396410629C87A6700F74173 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/ErrorState.strings"; sourceTree = ""; }; 4396410729C87A6700F74173 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/Settings.strings"; sourceTree = ""; }; 4396410829C87A6700F74173 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/SnackBar.strings"; sourceTree = ""; }; + 43964FF82C7C9E89001C730A /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/NativeErrorPage.strings; sourceTree = ""; }; 439691A02AEFC7C900ED7ADA /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/Shopping.strings; sourceTree = ""; }; 4396BBE32A0BCCD200482ABA /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/FirefoxSync.strings; sourceTree = ""; }; 4396BBE42A0BCCD200482ABA /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/Upgrade.strings; sourceTree = ""; }; + 4396C29A2C57B42700837488 /* si */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = si; path = si.lproj/EditAddress.strings; sourceTree = ""; }; 4396CC192BCD424B000EB7C7 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/BottomSheet.strings; sourceTree = ""; }; 4396E5F72A373433008E15B7 /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/Footer.strings"; sourceTree = ""; }; 4396E5F82A373433008E15B7 /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/RememberCard.strings"; sourceTree = ""; }; @@ -4498,6 +4606,7 @@ 4398CDB82AC1A2FF00C9AA9E /* ka */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ka; path = ka.lproj/Share.strings; sourceTree = ""; }; 4398CDB92AC1A2FF00C9AA9E /* ka */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ka; path = ka.lproj/TabLocation.strings; sourceTree = ""; }; 4399EF912B7BEFC600E091BE /* kab */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kab; path = kab.lproj/TabToolbar.strings; sourceTree = ""; }; + 439A03F62C57B051003675A3 /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/Toolbar.strings"; sourceTree = ""; }; 439A0C6E29FFD5DA0084BD94 /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/Notification.strings; sourceTree = ""; }; 439A0C6F29FFD5DB0084BD94 /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/ZoomPageBar.strings; sourceTree = ""; }; 439A220E29F69A0C00F120EE /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/Notification.strings; sourceTree = ""; }; @@ -4517,6 +4626,7 @@ 439A9B0F2C2052310071A727 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/AddressToolbar.strings; sourceTree = ""; }; 439A9B102C2052310071A727 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; 439A9B112C2052310071A727 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/QRCode.strings; sourceTree = ""; }; + 439A9F662C57B3AF00448564 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Toolbar.strings"; sourceTree = ""; }; 439B78172A09721600CAAE37 /* FormAutofillHelperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormAutofillHelperTests.swift; sourceTree = ""; }; 439BC6462AC1A4E000AF4D58 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Share.strings; sourceTree = ""; }; 439BC6472AC1A4E000AF4D58 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/TabLocation.strings; sourceTree = ""; }; @@ -4591,6 +4701,7 @@ 43A03B3329E427890035374E /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = "kk.lproj/Edit Card.strings"; sourceTree = ""; }; 43A03D882A0BCAE30002CBFF /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/FirefoxSync.strings"; sourceTree = ""; }; 43A03D892A0BCAE30002CBFF /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/Upgrade.strings"; sourceTree = ""; }; + 43A04D712C73605C0052F8EB /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/Toolbar.strings; sourceTree = ""; }; 43A076362BCD41BC00149887 /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/BottomSheet.strings; sourceTree = ""; }; 43A076372BCD41BC00149887 /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/KeyboardAccessory.strings; sourceTree = ""; }; 43A173162A24C0B5009C10B1 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/DisplayCard.strings; sourceTree = ""; }; @@ -4618,6 +4729,7 @@ 43A49E682B30607700FC86A9 /* hsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hsb; path = hsb.lproj/Credentials.strings; sourceTree = ""; }; 43A49E692B30607700FC86A9 /* hsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hsb; path = hsb.lproj/FirefoxLogins.strings; sourceTree = ""; }; 43A49E6A2B30607700FC86A9 /* hsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hsb; path = hsb.lproj/LoginsHelper.strings; sourceTree = ""; }; + 43A4B26E2C57B43700EF1B91 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/Toolbar.strings; sourceTree = ""; }; 43A4FE5528B9069200497E4A /* si */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = si; path = si.lproj/JumpBackIn.strings; sourceTree = ""; }; 43A4FE5628B9069200497E4A /* si */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = si; path = si.lproj/ToolbarLocation.strings; sourceTree = ""; }; 43A5515C28B39F1E0077B8F9 /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/JumpBackIn.strings; sourceTree = ""; }; @@ -4647,12 +4759,14 @@ 43A7CA152A2DF99E00BCAA15 /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/RememberCard.strings"; sourceTree = ""; }; 43A7CA162A2DF99E00BCAA15 /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/UpdateCard.strings"; sourceTree = ""; }; 43A878162B838FF90039D6B7 /* br */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = br; path = br.lproj/PasswordAutofill.strings; sourceTree = ""; }; + 43A9086F2C57B3C300698450 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Toolbar.strings"; sourceTree = ""; }; 43A9B1E62A124A3800FB58C6 /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/FirefoxSync.strings; sourceTree = ""; }; 43A9B1E72A124A3800FB58C6 /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/Upgrade.strings; sourceTree = ""; }; 43A9DD9A29BA6B3A003D55A4 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/EngagementNotification.strings; sourceTree = ""; }; 43A9DD9B29BA6B3B003D55A4 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Onboarding.strings; sourceTree = ""; }; 43A9DD9C29BA6B3B003D55A4 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/ResearchSurface.strings; sourceTree = ""; }; 43AAE57528B39C790048A19E /* ga */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ga; path = ga.lproj/BookmarkPanel.strings; sourceTree = ""; }; + 43AB384A2C57B33900F19287 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Toolbar.strings; sourceTree = ""; }; 43AB6F9C25DC53D20016B015 /* GoogleTopSiteManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GoogleTopSiteManager.swift; sourceTree = ""; }; 43AB6F9E25DC53D20016B015 /* LabelButtonHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LabelButtonHeaderView.swift; sourceTree = ""; }; 43AB6FA125DC53D30016B015 /* TopSiteHistoryManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TopSiteHistoryManager.swift; sourceTree = ""; }; @@ -4687,6 +4801,7 @@ 43AD73D12C2051EC00C1442B /* hy-AM */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "hy-AM"; path = "hy-AM.lproj/AddressToolbar.strings"; sourceTree = ""; }; 43AD73D22C2051ED00C1442B /* hy-AM */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "hy-AM"; path = "hy-AM.lproj/EnhancedTrackingProtection.strings"; sourceTree = ""; }; 43AD73D32C2051ED00C1442B /* hy-AM */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "hy-AM"; path = "hy-AM.lproj/QRCode.strings"; sourceTree = ""; }; + 43ADBF222C7C9BAF006B6E34 /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/NativeErrorPage.strings; sourceTree = ""; }; 43ADC80B29F69B27004444BA /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Notification.strings; sourceTree = ""; }; 43ADC80C29F69B28004444BA /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/ZoomPageBar.strings; sourceTree = ""; }; 43ADE8FD2B9F21540025934D /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/KeyboardAccessory.strings; sourceTree = ""; }; @@ -4707,6 +4822,7 @@ 43AFD0D329F69A6E0001109D /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/ZoomPageBar.strings; sourceTree = ""; }; 43B083B729F69DC10093BE71 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Notification.strings; sourceTree = ""; }; 43B083B829F69DC10093BE71 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/ZoomPageBar.strings; sourceTree = ""; }; + 43B0B5692C7C9C2A00B2A5AE /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/NativeErrorPage.strings; sourceTree = ""; }; 43B0CCAC29F69BCF00CBDC1D /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Notification.strings; sourceTree = ""; }; 43B0CCAD29F69BCF00CBDC1D /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/ZoomPageBar.strings; sourceTree = ""; }; 43B0CE2328EAFC58000500A2 /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/JumpBackIn.strings; sourceTree = ""; }; @@ -4739,6 +4855,7 @@ 43B1BCFD2B9F1D8E00126EF0 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/PasswordAutofill.strings; sourceTree = ""; }; 43B1EF182C3C019F00648D5A /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/EditAddress.strings; sourceTree = ""; }; 43B1EF192C3C019F00648D5A /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/ScanQRCode.strings; sourceTree = ""; }; + 43B210482C7C9EA500B136AD /* ug */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ug; path = ug.lproj/NativeErrorPage.strings; sourceTree = ""; }; 43B296302B305F1E00A5AA9B /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/ContextualHints.strings; sourceTree = ""; }; 43B296332B305F1E00A5AA9B /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/CredentialProvider.strings; sourceTree = ""; }; 43B296362B305F1E00A5AA9B /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/Credentials.strings; sourceTree = ""; }; @@ -4749,6 +4866,9 @@ 43B2DC8A29BA6BF200810266 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/EngagementNotification.strings; sourceTree = ""; }; 43B2DC8B29BA6BF200810266 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/Onboarding.strings; sourceTree = ""; }; 43B2DC8C29BA6BF200810266 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/ResearchSurface.strings; sourceTree = ""; }; + 43B3869C2C57B446007728B6 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/ScanQRCode.strings; sourceTree = ""; }; + 43B3869D2C57B446007728B6 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/Toolbar.strings; sourceTree = ""; }; + 43B388562C57B1830004C424 /* hy-AM */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "hy-AM"; path = "hy-AM.lproj/Toolbar.strings"; sourceTree = ""; }; 43B3D33329DAEB53006B3FBC /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/Alert.strings"; sourceTree = ""; }; 43B3D33429DAEB53006B3FBC /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/BiometricAuthentication.strings"; sourceTree = ""; }; 43B3D33529DAEB53006B3FBC /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/EditCard.strings"; sourceTree = ""; }; @@ -4813,6 +4933,7 @@ 43B950CD2A2DFC2F0001A8D1 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/Footer.strings; sourceTree = ""; }; 43B950CE2A2DFC2F0001A8D1 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/RememberCard.strings; sourceTree = ""; }; 43B950CF2A2DFC2F0001A8D1 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/UpdateCard.strings; sourceTree = ""; }; + 43B956C12C57B4C800F0E77B /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Toolbar.strings; sourceTree = ""; }; 43B966AE2BE8F11D007448EA /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/Microsurvey.strings; sourceTree = ""; }; 43B9A3EB2A2DFA560078C977 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Footer.strings; sourceTree = ""; }; 43B9A3EC2A2DFA560078C977 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/RememberCard.strings; sourceTree = ""; }; @@ -4893,7 +5014,15 @@ 43BE95752A124BAD00DCFD17 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Upgrade.strings; sourceTree = ""; }; 43BED8DE2BE8F1F100E34361 /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/Microsurvey.strings; sourceTree = ""; }; 43BF382B2B8CA9C400EFDAF3 /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/KeyboardAccessory.strings; sourceTree = ""; }; + 43BF3C5F2C7C9C380017A926 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/NativeErrorPage.strings; sourceTree = ""; }; 43BFD4502C3C04B400B76DE5 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/EditAddress.strings; sourceTree = ""; }; + 43BFE5BA2C57B54100CFB4C5 /* zh-CN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-CN"; path = "zh-CN.lproj/Toolbar.strings"; sourceTree = ""; }; + 43C09A7A2C7C9EB400BCD71D /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/ActivityStream.strings; sourceTree = ""; }; + 43C09A7B2C7C9EB400BCD71D /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/AddressToolbar.strings; sourceTree = ""; }; + 43C09A7C2C7C9EB400BCD71D /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/EditAddress.strings; sourceTree = ""; }; + 43C09A7D2C7C9EB400BCD71D /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; + 43C09A7E2C7C9EB400BCD71D /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/ScanQRCode.strings; sourceTree = ""; }; + 43C09A7F2C7C9EB400BCD71D /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Toolbar.strings; sourceTree = ""; }; 43C0B1D82BE8F26100E527D7 /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/Microsurvey.strings"; sourceTree = ""; }; 43C0DAB929D1B25700827785 /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/Alert.strings; sourceTree = ""; }; 43C0DABA29D1B25700827785 /* kk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kk; path = kk.lproj/BiometricAuthentication.strings; sourceTree = ""; }; @@ -4909,11 +5038,13 @@ 43C19FC129F69DE0005214A6 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/Notification.strings"; sourceTree = ""; }; 43C19FC229F69DE0005214A6 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/ZoomPageBar.strings"; sourceTree = ""; }; 43C22F422BE8F4D80043046A /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/Microsurvey.strings; sourceTree = ""; }; + 43C2B2312C57B4560010E539 /* sq */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sq; path = sq.lproj/Toolbar.strings; sourceTree = ""; }; 43C335E028B39F6500858209 /* ta */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ta; path = ta.lproj/BookmarkPanel.strings; sourceTree = ""; }; 43C39FE42A24C0EB000FD072 /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/DisplayCard.strings"; sourceTree = ""; }; 43C4236D2AEFC88A00518309 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Shopping.strings"; sourceTree = ""; }; 43C426C42AEFC69B000D7D40 /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/Shopping.strings"; sourceTree = ""; }; 43C437A72B554B9C00FBA497 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/TabToolbar.strings; sourceTree = ""; }; + 43C4422D2C7C9C4700ED0870 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/NativeErrorPage.strings; sourceTree = ""; }; 43C4A8562C3C002C00379C52 /* dsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = dsb; path = dsb.lproj/EditAddress.strings; sourceTree = ""; }; 43C4A8572C3C002C00379C52 /* dsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = dsb; path = dsb.lproj/ScanQRCode.strings; sourceTree = ""; }; 43C4BFF329BF3DF1005359FB /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/EngagementNotification.strings"; sourceTree = ""; }; @@ -4962,6 +5093,15 @@ 43C993B42B30609900C813F4 /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/FirefoxLogins.strings; sourceTree = ""; }; 43C993B52B30609900C813F4 /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/LoginsHelper.strings; sourceTree = ""; }; 43C9AE552ACADF5500159B34 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Share.strings; sourceTree = ""; }; + 43C9CD422C6A26D8002F86B4 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/ContextualHints.strings; sourceTree = ""; }; + 43C9CD432C6A26D9002F86B4 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/Credentials.strings; sourceTree = ""; }; + 43C9CD442C6A26D9002F86B4 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/PasswordAutofill.strings; sourceTree = ""; }; + 43C9CD452C6A26D9002F86B4 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/QRCode.strings; sourceTree = ""; }; + 43C9CD462C6A26D9002F86B4 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/ResearchSurface.strings; sourceTree = ""; }; + 43C9CD472C6A26D9002F86B4 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/ScanQRCode.strings; sourceTree = ""; }; + 43C9CD482C6A26D9002F86B4 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/Share.strings; sourceTree = ""; }; + 43C9CD492C6A26D9002F86B4 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/TabToolbar.strings; sourceTree = ""; }; + 43C9CD4A2C6A26D9002F86B4 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/ToolbarLocation.strings; sourceTree = ""; }; 43CA0389293E0AB30056EF4B /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/Alerts.strings; sourceTree = ""; }; 43CA038A293E0AB30056EF4B /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/SearchHeaderTitle.strings; sourceTree = ""; }; 43CA038B293E0AB30056EF4B /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/TabsTray.strings; sourceTree = ""; }; @@ -5006,11 +5146,14 @@ 43CD8C0929F69B6900B3ED1C /* he */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = he; path = he.lproj/ZoomPageBar.strings; sourceTree = ""; }; 43CE859E2BCD467D00EFFAA4 /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/BottomSheet.strings; sourceTree = ""; }; 43CE90FA2BE8F36700FFB2D4 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/Microsurvey.strings; sourceTree = ""; }; + 43CE9F3D2C57B5500069B3AF /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/Toolbar.strings"; sourceTree = ""; }; 43CEB4582B680459000F85A9 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/ContextualHints.strings"; sourceTree = ""; }; 43CEB4592B680459000F85A9 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Credentials.strings"; sourceTree = ""; }; 43CEB45A2B680459000F85A9 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/TabToolbar.strings"; sourceTree = ""; }; 43CF280028B39A70008DF2C5 /* anp */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = anp; path = anp.lproj/BookmarkPanel.strings; sourceTree = ""; }; + 43CF316F2C7C9F0900D898EE /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/NativeErrorPage.strings"; sourceTree = ""; }; 43CF76D92B5E84B9001CA9CD /* eu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eu; path = eu.lproj/TabToolbar.strings; sourceTree = ""; }; + 43CFB14C2C7C9A2B003B700F /* co */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = co; path = co.lproj/NativeErrorPage.strings; sourceTree = ""; }; 43CFD03D2B9F1D1000E7EB9E /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/PasswordAutofill.strings; sourceTree = ""; }; 43D00492296FC48F00CB0F31 /* CreditCardSettingsEmptyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreditCardSettingsEmptyView.swift; sourceTree = ""; }; 43D04B0729E425A4007722F2 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = "de.lproj/Edit Card.strings"; sourceTree = ""; }; @@ -5117,6 +5260,7 @@ 43D6F6F32B554C0100328E70 /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/FirefoxLogins.strings; sourceTree = ""; }; 43D6F6F42B554C0100328E70 /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/LoginsHelper.strings; sourceTree = ""; }; 43D6F6F52B554C0100328E70 /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/TabToolbar.strings; sourceTree = ""; }; + 43D6F80C2C57B4E7004BFBB8 /* ug */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ug; path = ug.lproj/Toolbar.strings; sourceTree = ""; }; 43D72DB226FCE3550069BDE9 /* mr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = mr; path = "mr.lproj/Default Browser.strings"; sourceTree = ""; }; 43D73B492A77CD3300E8C4EB /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/CustomizeFirefoxHome.strings; sourceTree = ""; }; 43D74AE02BCD4286000D0251 /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/BottomSheet.strings"; sourceTree = ""; }; @@ -5212,6 +5356,7 @@ 43DE20222A52E453001EDCCF /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/SelectCreditCard.strings; sourceTree = ""; }; 43DE406A2A0BCB1900D6F533 /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/FirefoxSync.strings"; sourceTree = ""; }; 43DE406B2A0BCB1900D6F533 /* es-CL */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-CL"; path = "es-CL.lproj/Upgrade.strings"; sourceTree = ""; }; + 43DE88092C7C9A3B00A7D4E0 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/NativeErrorPage.strings; sourceTree = ""; }; 43DEDA8D2B0B75FF003E54D4 /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/FirefoxHomepage.strings"; sourceTree = ""; }; 43DEE39E296C2CD600D13D0A /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Alerts.strings; sourceTree = ""; }; 43DEE39F296C2CD600D13D0A /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/JumpBackIn.strings; sourceTree = ""; }; @@ -5223,6 +5368,7 @@ 43DF19022C20505300CB9A72 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/AddressToolbar.strings; sourceTree = ""; }; 43DF19032C20505300CB9A72 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; 43DF19042C20505300CB9A72 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/QRCode.strings; sourceTree = ""; }; + 43DF2A222C57AFDD0083E728 /* dsb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = dsb; path = dsb.lproj/Toolbar.strings; sourceTree = ""; }; 43DF2D8E29C8799F008CA78E /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/Alert.strings; sourceTree = ""; }; 43DF2D8F29C8799F008CA78E /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/BiometricAuthentication.strings; sourceTree = ""; }; 43DF2D9029C8799F008CA78E /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/EditCard.strings; sourceTree = ""; }; @@ -5247,6 +5393,24 @@ 43DF9459292258C300590FE3 /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/SearchHeaderTitle.strings; sourceTree = ""; }; 43DF990828B39FE2009A5541 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/JumpBackIn.strings; sourceTree = ""; }; 43DF990928B39FE2009A5541 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = vi.lproj/ToolbarLocation.strings; sourceTree = ""; }; + 43E0BE322C60EC4B004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/ActivityStream.strings; sourceTree = ""; }; + 43E0BE332C60EC4B004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/AddressToolbar.strings; sourceTree = ""; }; + 43E0BE342C60EC4B004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/BiometricAuthentication.strings; sourceTree = ""; }; + 43E0BE352C60EC4B004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/BottomSheet.strings; sourceTree = ""; }; + 43E0BE362C60EC4B004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/CredentialProvider.strings; sourceTree = ""; }; + 43E0BE372C60EC4B004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/EditAddress.strings; sourceTree = ""; }; + 43E0BE382C60EC4B004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; + 43E0BE392C60EC4B004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/FirefoxHomepage.strings; sourceTree = ""; }; + 43E0BE3A2C60EC4C004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/FirefoxLogins.strings; sourceTree = ""; }; + 43E0BE3B2C60EC4C004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/JumpBackIn.strings; sourceTree = ""; }; + 43E0BE3C2C60EC4C004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/KeyboardAccessory.strings; sourceTree = ""; }; + 43E0BE3D2C60EC4C004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/LoginsHelper.strings; sourceTree = ""; }; + 43E0BE3E2C60EC4C004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/Microsurvey.strings; sourceTree = ""; }; + 43E0BE3F2C60EC4C004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/Notification.strings; sourceTree = ""; }; + 43E0BE402C60EC4C004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/Shopping.strings; sourceTree = ""; }; + 43E0BE412C60EC4C004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/TabLocation.strings; sourceTree = ""; }; + 43E0BE422C60EC4C004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/Toolbar.strings; sourceTree = ""; }; + 43E0BE432C60EC4C004FED65 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/ZoomPageBar.strings; sourceTree = ""; }; 43E0E9D728B39CA3002E264A /* gl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gl; path = gl.lproj/BookmarkPanel.strings; sourceTree = ""; }; 43E129A929BA6A480084C419 /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/EngagementNotification.strings; sourceTree = ""; }; 43E129AA29BA6A480084C419 /* ia */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ia; path = ia.lproj/Onboarding.strings; sourceTree = ""; }; @@ -5257,6 +5421,7 @@ 43E18BBF2A24C09100433C0C /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/DisplayCard.strings; sourceTree = ""; }; 43E1D7B029FFD6B5008F2117 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Notification.strings; sourceTree = ""; }; 43E1D7B129FFD6B5008F2117 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/ZoomPageBar.strings; sourceTree = ""; }; + 43E1DCD82C7C9AAB008E952F /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/NativeErrorPage.strings"; sourceTree = ""; }; 43E1FE0129F69D1200F5B25F /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/Alert.strings"; sourceTree = ""; }; 43E1FE0229F69D1200F5B25F /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/BiometricAuthentication.strings"; sourceTree = ""; }; 43E1FE0329F69D1200F5B25F /* sat-Olck */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "sat-Olck"; path = "sat-Olck.lproj/Edit Card.strings"; sourceTree = ""; }; @@ -5353,6 +5518,9 @@ 43E68A5E2B5E83DE004BC945 /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/TabToolbar.strings; sourceTree = ""; }; 43E699ED2AF9014B00030DDD /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Shopping.strings; sourceTree = ""; }; 43E69EAF254D064E00B591C2 /* SimpleTab.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleTab.swift; sourceTree = ""; }; + 43E6BE132C57AF7600C2152C /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/ScanQRCode.strings; sourceTree = ""; }; + 43E6BE152C57AF7600C2152C /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/Toolbar.strings; sourceTree = ""; }; + 43E7071E2C6A26FC00A29AE4 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Toolbar.strings; sourceTree = ""; }; 43E730C22C20522100892124 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/ActivityStream.strings; sourceTree = ""; }; 43E730C32C20522100892124 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/AddressToolbar.strings; sourceTree = ""; }; 43E730C42C20522100892124 /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/EnhancedTrackingProtection.strings; sourceTree = ""; }; @@ -5435,6 +5603,7 @@ 43EBF7072A3735DC00E56F21 /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/RememberCard.strings; sourceTree = ""; }; 43EBF7082A3735DC00E56F21 /* rm */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = rm; path = rm.lproj/UpdateCard.strings; sourceTree = ""; }; 43EC35B528B39E0F00E3513B /* mr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = mr; path = mr.lproj/BookmarkPanel.strings; sourceTree = ""; }; + 43EC361F2C7C9A4B00824F5E /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = cy.lproj/NativeErrorPage.strings; sourceTree = ""; }; 43EC4E1E2BE8F46200290013 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Microsurvey.strings; sourceTree = ""; }; 43EC6B9F2B14B34C00E417F1 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/FirefoxHomepage.strings; sourceTree = ""; }; 43EC79AA2C3C00BF00519D98 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/KeyboardAccessory.strings"; sourceTree = ""; }; @@ -5443,6 +5612,7 @@ 43EC79AD2C3C00BF00519D98 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/Share.strings"; sourceTree = ""; }; 43EC79AE2C3C00BF00519D98 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/Shopping.strings"; sourceTree = ""; }; 43ECB7C52BE8F3410024824F /* hy-AM */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "hy-AM"; path = "hy-AM.lproj/Microsurvey.strings"; sourceTree = ""; }; + 43ED215A2C57AFEC0014F10F /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/Toolbar.strings; sourceTree = ""; }; 43ED29612B305FD800481A75 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/ContextualHints.strings; sourceTree = ""; }; 43ED29622B305FD800481A75 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/CredentialProvider.strings; sourceTree = ""; }; 43ED29632B305FD800481A75 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Credentials.strings; sourceTree = ""; }; @@ -5460,6 +5630,7 @@ 43EE2FFB29FFD7D700C97C08 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Notification.strings; sourceTree = ""; }; 43EE2FFC29FFD7D700C97C08 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/ZoomPageBar.strings; sourceTree = ""; }; 43EE7D6828B39E20007D5A02 /* ms */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ms; path = ms.lproj/BookmarkPanel.strings; sourceTree = ""; }; + 43EE827C2C7C9D4600CDF5B9 /* nn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nn; path = nn.lproj/NativeErrorPage.strings; sourceTree = ""; }; 43EE84312BA85BB9003334F5 /* zh-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-TW"; path = "zh-TW.lproj/BottomSheet.strings"; sourceTree = ""; }; 43EECA382C3C00560032D70E /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/EditAddress.strings"; sourceTree = ""; }; 43EECA392C3C00560032D70E /* en-GB */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-GB"; path = "en-GB.lproj/ScanQRCode.strings"; sourceTree = ""; }; @@ -5469,6 +5640,7 @@ 43EF6F99293E0CB8006A4C7D /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/TabsTray.strings; sourceTree = ""; }; 43EF88BD2A3733B300AB9AAB /* co */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = co; path = co.lproj/Footer.strings; sourceTree = ""; }; 43EF88BE2A3733B300AB9AAB /* co */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = co; path = co.lproj/RememberCard.strings; sourceTree = ""; }; + 43F013FF2C7C9ABA00F8CF52 /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/NativeErrorPage.strings"; sourceTree = ""; }; 43F034172AC1A2E800021EA0 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/TabLocation.strings; sourceTree = ""; }; 43F084E92A52E49F00C35028 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/CustomizeFirefoxHome.strings; sourceTree = ""; }; 43F084EA2A52E49F00C35028 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/SelectCreditCard.strings; sourceTree = ""; }; @@ -5613,6 +5785,7 @@ 43FAF1A82C2050C200C52557 /* en-US */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-US"; path = "en-US.lproj/QRCode.strings"; sourceTree = ""; }; 43FB409E2A0BCB650006C10B /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/FirefoxSync.strings; sourceTree = ""; }; 43FB409F2A0BCB650006C10B /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Upgrade.strings; sourceTree = ""; }; + 43FB540B2C57AFFA00562B25 /* en-CA */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "en-CA"; path = "en-CA.lproj/Toolbar.strings"; sourceTree = ""; }; 43FBF67F2ACADCF400F86E9E /* be */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = be; path = be.lproj/Share.strings; sourceTree = ""; }; 43FC1AAB2AC1A22C00989CA0 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Share.strings; sourceTree = ""; }; 43FC1AAC2AC1A22C00989CA0 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/TabLocation.strings; sourceTree = ""; }; @@ -5629,6 +5802,10 @@ 43FEB816293E0C3A00DBC665 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Alerts.strings; sourceTree = ""; }; 43FEB817293E0C3A00DBC665 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/SearchHeaderTitle.strings; sourceTree = ""; }; 43FEB818293E0C3A00DBC665 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/TabsTray.strings; sourceTree = ""; }; + 43FECBDA2C7C9AC900D644AE /* eo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eo; path = eo.lproj/EditAddress.strings; sourceTree = ""; }; + 43FECBDB2C7C9AC900D644AE /* eo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eo; path = eo.lproj/NativeErrorPage.strings; sourceTree = ""; }; + 43FECBDC2C7C9ACA00D644AE /* eo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eo; path = eo.lproj/ScanQRCode.strings; sourceTree = ""; }; + 43FECBDD2C7C9ACA00D644AE /* eo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = eo; path = eo.lproj/Toolbar.strings; sourceTree = ""; }; 43FEE9662A24C0A3001C35F7 /* or */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = or; path = "or.lproj/Default Browser.strings"; sourceTree = ""; }; 43FF69D12C2050EC00D4B9C3 /* es-AR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-AR"; path = "es-AR.lproj/ActivityStream.strings"; sourceTree = ""; }; 43FF69D22C2050EC00D4B9C3 /* es-AR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-AR"; path = "es-AR.lproj/AddressToolbar.strings"; sourceTree = ""; }; @@ -6433,6 +6610,7 @@ 8A8629E52880B69C0096DDB1 /* BookmarksPanelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksPanelTests.swift; sourceTree = ""; }; 8A86DAD7277298DE00D7BFFF /* ClosedTabsStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClosedTabsStoreTests.swift; sourceTree = ""; }; 8A87AEE02C1B4D17007428B2 /* SearchViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchViewModelTests.swift; sourceTree = ""; }; + 8A880C432C63CFE200B77F23 /* MockLoginViewModelDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockLoginViewModelDelegate.swift; sourceTree = ""; }; 8A8917682B57283B008B01EA /* HomepageHeaderCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomepageHeaderCell.swift; sourceTree = ""; }; 8A8BAE152B2119E600D774EB /* InternalURL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InternalURL.swift; sourceTree = ""; }; 8A8DDEBE276259A900E7B97A /* RatingPromptManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RatingPromptManager.swift; sourceTree = ""; }; @@ -6452,6 +6630,8 @@ 8A95FF652B1E977E00AC303D /* TelemetryContextualIdentifierTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TelemetryContextualIdentifierTests.swift; sourceTree = ""; }; 8A96C4B828F9DD8700B75884 /* ThemableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemableTests.swift; sourceTree = ""; }; 8A96C4BA28F9E7B300B75884 /* XCTestCaseRootViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCTestCaseRootViewController.swift; sourceTree = ""; }; + 8A97E6E92C58487900F94793 /* AddressLocaleFeatureValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressLocaleFeatureValidator.swift; sourceTree = ""; }; + 8A97E6ED2C584AC300F94793 /* AddressLocaleFeatureValidatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressLocaleFeatureValidatorTests.swift; sourceTree = ""; }; 8A99DB9727C6DD3E007EA6BD /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = "is.lproj/Default Browser.strings"; sourceTree = ""; }; 8A99DB9827C6DD3E007EA6BD /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/Today.strings; sourceTree = ""; }; 8A99DB9927C6DD3E007EA6BD /* is */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = is; path = is.lproj/BookmarkPanel.strings; sourceTree = ""; }; @@ -6470,6 +6650,7 @@ 8AAAB0582C1B723F008830B3 /* MockRustFirefoxSuggest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockRustFirefoxSuggest.swift; sourceTree = ""; }; 8AAAB05A2C1B7268008830B3 /* ClientTabsSearchWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientTabsSearchWrapper.swift; sourceTree = ""; }; 8AAAB05E2C1B72C9008830B3 /* SearchHighlightItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchHighlightItem.swift; sourceTree = ""; }; + 8AABB92C2C64F77000F1FE51 /* LoginProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginProvider.swift; sourceTree = ""; }; 8AABBCFB2A0010900089941E /* GleanWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GleanWrapper.swift; sourceTree = ""; }; 8AABBCFD2A0017560089941E /* MockGleanWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockGleanWrapper.swift; sourceTree = ""; }; 8AABBD002A001ADF0089941E /* ApplicationHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationHelper.swift; sourceTree = ""; }; @@ -6495,6 +6676,9 @@ 8AB8574527D97CB00075C173 /* HomepageContextMenuProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomepageContextMenuProtocol.swift; sourceTree = ""; }; 8AB8574727D97CD40075C173 /* HomePanelType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomePanelType.swift; sourceTree = ""; }; 8AB8574927D97CE90075C173 /* HomePanelDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomePanelDelegate.swift; sourceTree = ""; }; + 8AB893A22C73AF5200DAEED7 /* MockCreditCardProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockCreditCardProvider.swift; sourceTree = ""; }; + 8AB893A52C73AFBA00DAEED7 /* MockLoginProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockLoginProvider.swift; sourceTree = ""; }; + 8AB893A82C73CBBD00DAEED7 /* CreditCardProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreditCardProvider.swift; sourceTree = ""; }; 8ABA9C8A28931207002C0077 /* JumpBackInDataAdaptorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JumpBackInDataAdaptorTests.swift; sourceTree = ""; }; 8ABA9C8C28931223002C0077 /* MockDispatchQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockDispatchQueue.swift; sourceTree = ""; }; 8ABC5AED284532C900FEA552 /* PocketDiscoverCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PocketDiscoverCell.swift; sourceTree = ""; }; @@ -6993,6 +7177,7 @@ B18D4F2291BA5459431EDCA9 /* es-MX */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-MX"; path = "es-MX.lproj/Shared.strings"; sourceTree = ""; }; B19648D5B3667FE0CB25D069 /* my */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = my; path = my.lproj/Menu.strings; sourceTree = ""; }; B1C044CA95EB3F5258D2EC51 /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/InfoPlist.strings; sourceTree = ""; }; + B1CA62812C0DB43600D31625 /* MultiWindowTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiWindowTests.swift; sourceTree = ""; }; B1D14C77AD55FE126B7904D8 /* oc */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = oc; path = oc.lproj/PrivateBrowsing.strings; sourceTree = ""; }; B1D74C6B9DE786A50986BB1A /* ur */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ur; path = "ur.lproj/Default Browser.strings"; sourceTree = ""; }; B1F04E16A0BF365D67AD0C6F /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/Search.strings; sourceTree = ""; }; @@ -8042,6 +8227,7 @@ E16258EE2A83BE0800522742 /* FakespotLoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakespotLoadingView.swift; sourceTree = ""; }; E1634E1AB9F05311CD6C8C6C /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/AuthenticationManager.strings; sourceTree = ""; }; E16941B32C4E4A2E00FF5F4E /* BrowserViewControllerStateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowserViewControllerStateTests.swift; sourceTree = ""; }; + E16941B72C5119A200FF5F4E /* Autocompletable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Autocompletable.swift; sourceTree = ""; }; E169C6E72979CA0E0017B8D7 /* URLMailTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLMailTests.swift; sourceTree = ""; }; E16AD22B2A8A7AE800F0AA58 /* FakespotHighlightsCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakespotHighlightsCardView.swift; sourceTree = ""; }; E16C76802ABDC0DB00172DB5 /* FakespotHighlightsCardViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakespotHighlightsCardViewModelTests.swift; sourceTree = ""; }; @@ -9450,6 +9636,7 @@ 3BF4B8DA1D38493300493393 /* Utils */, EB7A651020699BD200B52A5F /* WebPagesForTesting.swift */, 0B9D40781E8D5AC80059E664 /* XCUITests-Bridging-Header.h */, + B1CA62812C0DB43600D31625 /* MultiWindowTests.swift */, ); path = XCUITests; sourceTree = ""; @@ -10325,6 +10512,15 @@ path = Extensions; sourceTree = ""; }; + 8A97E6EC2C58489C00F94793 /* AddressUtility */ = { + isa = PBXGroup; + children = ( + 8CEDF07F2BFE138B00D2617B /* AddressProvider.swift */, + 8A97E6E92C58487900F94793 /* AddressLocaleFeatureValidator.swift */, + ); + path = AddressUtility; + sourceTree = ""; + }; 8A9B87A92C1B30340042B894 /* Search */ = { isa = PBXGroup; children = ( @@ -10408,6 +10604,15 @@ path = LogoHeader; sourceTree = ""; }; + 8AB893A12C73AF4500DAEED7 /* Mocks */ = { + isa = PBXGroup; + children = ( + 8AB893A22C73AF5200DAEED7 /* MockCreditCardProvider.swift */, + 8AB893A52C73AFBA00DAEED7 /* MockLoginProvider.swift */, + ); + path = Mocks; + sourceTree = ""; + }; 8ABCBE622C485CAA00480A21 /* FeatureFlags */ = { isa = PBXGroup; children = ( @@ -10840,12 +11045,16 @@ AB2AC6642BD15E2C00022AAB /* TrackingProtection */ = { isa = PBXGroup; children = ( + 0AFF7F692C7C7BB800265214 /* CertificatesCell.swift */, + 0AFF7F6A2C7C7BB900265214 /* CertificatesViewController.swift */, + 0AFF7F6B2C7C7BB900265214 /* CertificatesViewModel.swift */, AB9CBBFE2C53B64B00102610 /* TrackingProtectionAction.swift */, AB9CBBFF2C53B64B00102610 /* TrackingProtectionMiddleware.swift */, AB9CBC002C53B64B00102610 /* TrackingProtectionModel.swift */, AB9CBC012C53B64B00102610 /* TrackingProtectionState.swift */, AB2AC6652BD15E6300022AAB /* CertificatesHandler.swift */, AB936A682C05F2B100600F82 /* TrackingProtectionButton.swift */, + 0A4978492C53E63200B1E82A /* TrackingProtectionViewController.swift */, ); path = TrackingProtection; sourceTree = ""; @@ -10853,7 +11062,9 @@ AB9CBC0A2C53B9A400102610 /* TrackingProtectionTests */ = { isa = PBXGroup; children = ( + 0AFF7F652C7784F000265214 /* TrackingProtectionModelTests.swift */, AB9CBC062C53B76400102610 /* TrackingProtectionStateTests.swift */, + 0A7693602C7DD19500103A6D /* CertificatesViewModelTests.swift */, ); path = TrackingProtectionTests; sourceTree = ""; @@ -10865,6 +11076,7 @@ AB42CC722A1F523F003C9594 /* CreditCardBottomSheetViewController.swift */, AB42CC732A1F5240003C9594 /* CreditCardBottomSheetHeaderView.swift */, ABEF80D42A254185003F52C4 /* CreditCardBottomSheetFooterView.swift */, + 8AB893A82C73CBBD00DAEED7 /* CreditCardProvider.swift */, ); path = CreditCardBottomSheet; sourceTree = ""; @@ -10872,9 +11084,11 @@ B23620492B7EAF2C000B1DE7 /* Autofill */ = { isa = PBXGroup; children = ( + 8AB893A12C73AF4500DAEED7 /* Mocks */, 439B78172A09721600CAAE37 /* FormAutofillHelperTests.swift */, 8CCD74722B90A945008F919B /* LoginListViewModelTests.swift */, 8CEDF07D2BFE04B100D2617B /* AddressListViewModelTests.swift */, + 8A97E6ED2C584AC300F94793 /* AddressLocaleFeatureValidatorTests.swift */, ); path = Autofill; sourceTree = ""; @@ -10896,11 +11110,11 @@ B2FEA6892B460CEC0058E616 /* Address */ = { isa = PBXGroup; children = ( + 8A97E6EC2C58489C00F94793 /* AddressUtility */, 8C2937732BF79F0C00146613 /* Edit */, B2FEA68C2B460D390058E616 /* AddressAutofillSettingsViewController.swift */, B2FEA68E2B460D9E0058E616 /* AddressAutofillSettingsViewModel.swift */, B2DFB7DE2B619DB80004CEA5 /* AddressListViewModel.swift */, - 8CEDF07F2BFE138B00D2617B /* AddressProvider.swift */, B2FEA68A2B460D1D0058E616 /* AddressAutofillSettingsView.swift */, B2FEA6902B4661BE0058E616 /* AddressAutofillToggle.swift */, B2DFB7E02B619DF60004CEA5 /* AddressListView.swift */, @@ -11244,6 +11458,7 @@ 434CD57729F6FC4500A0D04B /* MockAppAuthenticator.swift */, 8AABBD022A001CBC0089941E /* MockApplicationHelper.swift */, 965C3C9729343445006499ED /* MockAppSessionManager.swift */, + 0AFF7F632C7784D600265214 /* MockDataCleaner.swift */, 8A5D1C9F2A30C9D7005AD35C /* MockAppSettingsDelegate.swift */, 8A5038132A5DFCE000A1B02A /* MockBrowserProfile.swift */, C8C3FEA029F973C40038E3BA /* MockBrowserViewController.swift */, @@ -11258,6 +11473,7 @@ 8AF10D9029D776190086351D /* MockLaunchScreenManager.swift */, 965C3C95293431FC006499ED /* MockLaunchSessionProvider.swift */, 8AF99B5329EF2AF100108DEC /* MockLogger.swift */, + 8A880C432C63CFE200B77F23 /* MockLoginViewModelDelegate.swift */, 8A93F86629D373AC004159D9 /* MockNavigationController.swift */, 5AB4237B28A1947A003BC40C /* MockNotificationCenter.swift */, E18259E229B2A51B00E6BE76 /* MockNotificationManager.swift */, @@ -11950,6 +12166,7 @@ E17798972BD6B44B00F6F0EB /* AddressToolbarContainer.swift */, E18CE8D92BDA3F6B00EE2BCD /* NavigationToolbarContainer.swift */, E14792A22C2C5C660058211C /* ToolbarHelper.swift */, + E16941B72C5119A200FF5F4E /* Autocompletable.swift */, ); path = Toolbars; sourceTree = ""; @@ -12187,6 +12404,8 @@ 43C50B4E2A0BCAA800C6A134 /* FirefoxSync.strings */, 43A7153B2A2DF94F00DD5747 /* Footer.strings */, E4E0BB171AFBC9E4008D6260 /* Info.plist */, + 437AE30E2C7C9A0C0053F7F3 /* NativeErrorPage.strings */, + 43E6BE142C57AF7600C2152C /* Toolbar.strings */, 43B627742C3C001E00A188E8 /* ScanQRCode.strings */, 43D3ED962C3BFFC900E33D46 /* EditAddress.strings */, 43903A902C20502A00ACF76E /* QRCode.strings */, @@ -12244,6 +12463,7 @@ E1E5BE242A28F7BE00248F77 /* PasswordDetailViewControllerModel.swift */, CAA3B7E52497DCB60094E3C1 /* LoginDataSource.swift */, CA520E7924913C1B00CCAB48 /* PasswordManagerViewModel.swift */, + 8AABB92C2C64F77000F1FE51 /* LoginProvider.swift */, CA7FC7D224A6A9B70012F347 /* PasswordManagerDataSourceHelper.swift */, CA90753724929B22005B794D /* NoLoginsView.swift */, CAC458F0249429C20042561A /* PasswordManagerSelectionHelper.swift */, @@ -13643,6 +13863,7 @@ 431F0C4A2AC1A112006D7D49 /* TabLocation.strings in Resources */, 43903A8F2C20502A00ACF76E /* EnhancedTrackingProtection.strings in Resources */, 43A878172B838FF90039D6B7 /* PasswordAutofill.strings in Resources */, + 43E6BE162C57AF7600C2152C /* Toolbar.strings in Resources */, D59643E725C9B8E000EAB8B9 /* Intro.strings in Resources */, D59643F325C9B8E000EAB8B9 /* Storage.strings in Resources */, D59643E925C9B8E000EAB8B9 /* Menu.strings in Resources */, @@ -13686,6 +13907,7 @@ 4308A4E62A52E38C001D652E /* SelectCreditCard.strings in Resources */, 43FA499D29C875C0005062DB /* EditCard.strings in Resources */, 43B627762C3C001E00A188E8 /* ScanQRCode.strings in Resources */, + 437AE3102C7C9A0C0053F7F3 /* NativeErrorPage.strings in Resources */, 433BADA029C8769800E34991 /* BiometricAuthentication.strings in Resources */, D58A202F25C9D96400105D25 /* BookmarkPanelDeleteConfirm.strings in Resources */, D59643EA25C9B8E000EAB8B9 /* HistoryPanel.strings in Resources */, @@ -14007,7 +14229,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if [ $ACTION != \"indexbuild\" ]; then\n bash $SOURCE_ROOT/bin/nimbus-fml.sh --verbose\nfi\n"; + shellScript = "if [ $ACTION != \"indexbuild\" ]; then\n /usr/bin/env -i HOME=$HOME PROJECT=$PROJECT CONFIGURATION=$CONFIGURATION SOURCE_ROOT=$SOURCE_ROOT bash $SOURCE_ROOT/bin/nimbus-fml.sh --verbose\nfi\n"; }; C874A4E327F62C5B006F54E5 /* Swiftlint */ = { isa = PBXShellScriptBuildPhase; @@ -14311,6 +14533,7 @@ 0B3D670E1E09B90B00C1EFC7 /* AuthenticationTest.swift in Sources */, D4C4BDCE2253725E00986F04 /* LibraryTests.swift in Sources */, 2CCF17532105E4FD00705AE5 /* DisplaySettingsTests.swift in Sources */, + B1CA62822C0DB43600D31625 /* MultiWindowTests.swift in Sources */, 2C97EC711E72C80E0092EC18 /* TopTabsTest.swift in Sources */, 39EB469A1E26DDB4006346E8 /* FxScreenGraph.swift in Sources */, 2CC1B3F01E9B861400814EEC /* DomainAutocompleteTests.swift in Sources */, @@ -14515,6 +14738,7 @@ E1877A832875DEDE00F5BDF2 /* SyncedTabCell.swift in Sources */, 21E77E522AA8BE5C00FABA10 /* TabTrayFlagManager.swift in Sources */, CA8226F324C11DB7008A6F38 /* PasswordManagerTableViewCell.swift in Sources */, + 0AFF7F6E2C7C7BBA00265214 /* CertificatesViewModel.swift in Sources */, 8A0A1BA02B2200FD00E8706F /* PrivateHomepageViewController.swift in Sources */, E4A960061ABB9C450069AD6F /* ReaderModeUtils.swift in Sources */, 8AD40FCB27BADC4B00672675 /* StatefulButton.swift in Sources */, @@ -14647,6 +14871,7 @@ 967A028E28FA026F003C35E3 /* SceneDelegate.swift in Sources */, C8B07A4128199500000AFCE7 /* NimbusFlaggableFeature.swift in Sources */, 0B62EFD21AD63CD100ACB9CD /* Clearables.swift in Sources */, + 0AFF7F6C2C7C7BBA00265214 /* CertificatesCell.swift in Sources */, 8AAEBA062BF51141000C02B5 /* MicrosurveyMiddleware.swift in Sources */, 431C0CA925C890E500395CE4 /* DefaultBrowserOnboardingViewModel.swift in Sources */, DFACBF81277B916B003D5F41 /* ConfigurableGradientView.swift in Sources */, @@ -14949,6 +15174,7 @@ 3BB50E201D627539004B33DF /* HomepageViewController.swift in Sources */, C82A94F3269F68F300624AA7 /* CoreFlaggableFeature.swift in Sources */, F84B22241A09122500AAB793 /* LibraryViewController.swift in Sources */, + E16941B82C5119A200FF5F4E /* Autocompletable.swift in Sources */, 39455F771FC83F430088A22C /* TabEventHandler.swift in Sources */, 8A36BE2929EDBC6900AC1C5C /* ContentContainer.swift in Sources */, 215B458227DA420400E5E800 /* LegacyTabMetadataManager.swift in Sources */, @@ -14957,6 +15183,7 @@ EBB89507219398E500EB91A0 /* ContentBlocker.swift in Sources */, 216A0D792A40E85A008077BA /* ThemeSettingsState.swift in Sources */, 5A3A2A0D287F742C00B79EAC /* BackgroundSyncUtility.swift in Sources */, + 8AABB92D2C64F77000F1FE51 /* LoginProvider.swift in Sources */, 21AFCFEE2AE80B700027E9CE /* TabsCoordinator.swift in Sources */, 8A1CBB952BE017D3008BE4D4 /* MicrosurveyPromptAction.swift in Sources */, 23ED80FF25C89C9800D0E9D5 /* DefaultBrowserOnboardingViewController.swift in Sources */, @@ -14978,6 +15205,7 @@ E13C072B2C2184C80087E404 /* AddressBarState.swift in Sources */, 4347B39A298DA5BB0045F677 /* CreditCardInputViewModel.swift in Sources */, 6025B10D267B6C5400F59F6B /* LoginRecordExtension.swift in Sources */, + 8AB893A62C73AFBA00DAEED7 /* MockLoginProvider.swift in Sources */, 2F44FCC51A9E85E900FD20CC /* SettingsTableViewController.swift in Sources */, 211046C92A7ADE9000A7309F /* BlockPopupSetting.swift in Sources */, 8A3233FC286270CF003E1C33 /* FxBookmarkNode.swift in Sources */, @@ -14986,6 +15214,7 @@ 8A83B7462A264FA0002FF9AC /* SettingsCoordinator.swift in Sources */, A9072B801D07B34100459960 /* NoImageModeHelper.swift in Sources */, 433396C827ACE92500491049 /* LegacyInactiveTabButton.swift in Sources */, + 8A97E6EA2C58487900F94793 /* AddressLocaleFeatureValidator.swift in Sources */, AB3DB0C92B596739001D32CB /* AppStartupTelemetry.swift in Sources */, D8AA923421A602DC002605C0 /* HomePageSettingViewController.swift in Sources */, 8A161411282C035D00DDBB02 /* CustomizeHomepageSectionViewModel.swift in Sources */, @@ -15056,6 +15285,7 @@ AB03032C2AB47AF300DCD8EF /* FakespotOptInCardViewModel.swift in Sources */, 1D2F68AB2ACB262900524B92 /* RemoteTabsPanelAction.swift in Sources */, 8A5D1CB92A30DBDB005AD35C /* ChinaSyncServiceSetting.swift in Sources */, + 8AB893A92C73CBBD00DAEED7 /* CreditCardProvider.swift in Sources */, 1D8487B42AD0C6C100F7527C /* RemoteTabsPanelMiddleware.swift in Sources */, 5AA1D8272BC09ECB00957516 /* TabTrayAnimationQueue.swift in Sources */, C849E46526B9C3DD00260F0B /* SlideoverPresentationController.swift in Sources */, @@ -15138,6 +15368,7 @@ 3B39EDCB1E16E1AA00EF029F /* CustomSearchViewController.swift in Sources */, B2DFB7E12B619DF60004CEA5 /* AddressListView.swift in Sources */, C8B0F5F7283B7CCE007AE65D /* PocketFeedStory.swift in Sources */, + 0AFF7F6D2C7C7BBA00265214 /* CertificatesViewController.swift in Sources */, 96A562A027D6D0E80045144A /* ContileProvider.swift in Sources */, 8A5D1CAC2A30D70B005AD35C /* OpenWithSetting.swift in Sources */, 8AFA263227B6E9AB00D0C33B /* ToolbarBadge.swift in Sources */, @@ -15183,6 +15414,7 @@ D3C744CD1A687D6C004CE85D /* URIFixup.swift in Sources */, 43D16B8029831DC5009F8279 /* CreditCardInputView.swift in Sources */, 21112968289480630082C08B /* HomepageMessageCardViewModel.swift in Sources */, + 8AB893A32C73AF5200DAEED7 /* MockCreditCardProvider.swift in Sources */, E1CD81BE290C5C7500124B27 /* DevicePickerTableViewHeaderCell.swift in Sources */, B2FEA68B2B460D1D0058E616 /* AddressAutofillSettingsView.swift in Sources */, E12BD0AC28AC37F00029AAF0 /* UIColor+Extension.swift in Sources */, @@ -15291,6 +15523,7 @@ C849E46126B9C39B00260F0B /* EnhancedTrackingProtectionVC.swift in Sources */, E40FAB0C1A7ABB77009CB80D /* WebServer.swift in Sources */, 59A68D66379CFA85C4EAF00B /* TwoLineImageOverlayCell.swift in Sources */, + 0A49784A2C53E63200B1E82A /* TrackingProtectionViewController.swift in Sources */, 8A04136928258DF600D20B10 /* SponsoredTileTelemetry.swift in Sources */, D04CD718215EBD85004FF5B0 /* SettingsLoadingView.swift in Sources */, 21420EF72ABA338D00B28550 /* TabTrayCoordinator.swift in Sources */, @@ -15380,6 +15613,7 @@ 8A8482F02BE1602500F9007B /* MicrosurveyPromptStateTests.swift in Sources */, 8ADED7EC27691351009C19E6 /* CalendarExtensionsTests.swift in Sources */, 3B39EDBA1E16E18900EF029F /* CustomSearchEnginesTest.swift in Sources */, + 0AFF7F662C7784F100265214 /* TrackingProtectionModelTests.swift in Sources */, C80C11EE28B3C8B80062922A /* WallpaperMetadataTrackerTests.swift in Sources */, 8C8D8C7A2AA067AD00490D32 /* FakespotCoordinatorTests.swift in Sources */, C8E531CA29E5F7D300E03FEF /* URLScannerTests.swift in Sources */, @@ -15442,6 +15676,7 @@ C869915728917809007ACC5C /* NetworkingMock.swift in Sources */, 8A4190D22A6B0848001E8401 /* StatusBarOverlayTests.swift in Sources */, C8EDDBF029DD83FC003A4C07 /* RouteTests.swift in Sources */, + 0A7693612C7DD19600103A6D /* CertificatesViewModelTests.swift in Sources */, 8AED868328CA3B3400351A50 /* BookmarkPanelViewModelTests.swift in Sources */, 434CD57829F6FC4500A0D04B /* MockAppAuthenticator.swift in Sources */, DACDE996225E537900C8F37F /* VersionSettingTests.swift in Sources */, @@ -15561,6 +15796,7 @@ D525DFB325FBE5E000B18763 /* TabTests.swift in Sources */, C8E78BDD27F4A1E700C48BAA /* HistoryDeletionUtilityTests.swift in Sources */, 0AC659272BF35854005C614A /* FxAWebViewModelTests.swift in Sources */, + 8AB893A72C73AFCC00DAEED7 /* MockLoginViewModelDelegate.swift in Sources */, 8A11C8132731E54800AC7318 /* DictionaryExtensionsTests.swift in Sources */, 5A475E8F29DB89CE009C13FD /* MockTabDataStore.swift in Sources */, 8A7A93EE2810ADF2005E7E1B /* ContileProviderTests.swift in Sources */, @@ -15586,6 +15822,7 @@ 4A59B58AD11B5EE1F80BBDEB /* TestHistory.swift in Sources */, A83E5B1D1C1DA8D80026D912 /* UIPasteboardExtensionsTests.swift in Sources */, 8A635ECD289437A8006378BA /* SyncedTabCellTests.swift in Sources */, + 8A97E6EF2C584C4900F94793 /* AddressLocaleFeatureValidatorTests.swift in Sources */, E1E425322B5A2E9700899550 /* DownloadTests.swift in Sources */, 5AB4237C28A1947A003BC40C /* MockNotificationCenter.swift in Sources */, 8A37C79F28DA4BA600B1FAD4 /* ContextualHintViewProviderTests.swift in Sources */, @@ -15605,6 +15842,7 @@ 21FA8FAE2AE856460013B815 /* TabsCoordinatorTests.swift in Sources */, E1D8BC7A21FF7A0000B100BD /* TPStatsBlocklistsTests.swift in Sources */, 5AF6254328A57A4600A90253 /* HistoryHighlightsDataAdaptorTests.swift in Sources */, + 0AFF7F642C7784D600265214 /* MockDataCleaner.swift in Sources */, 8AE80BAD2891957C00BC12EA /* TopSitesDimensionTests.swift in Sources */, D82ED2641FEB3C420059570B /* DefaultSearchPrefsTests.swift in Sources */, 1D74FF502B2797EA00FF01D0 /* WindowManagerTests.swift in Sources */, @@ -16317,6 +16555,7 @@ 439CB65D2AE68F1F00A19D54 /* sq */, 4364EA952B1DE9FB003A1240 /* bs */, 43EB5FA72B2728080063D23E /* ug */, + 4300B0DE2C57B06E00F609C1 /* es-MX */, ); name = SelectCreditCard.strings; sourceTree = ""; @@ -16382,6 +16621,8 @@ 43CCB1A22B27249B00D6B1C0 /* br */, 43EB5FAC2B2728080063D23E /* ug */, 439E22242C04A5620061A923 /* su */, + 4300B0DF2C57B06E00F609C1 /* es-MX */, + 43E0BE412C60EC4C004FED65 /* hr */, ); name = TabLocation.strings; sourceTree = ""; @@ -16444,6 +16685,7 @@ 439E22202C04A5610061A923 /* su */, 43CCB4D42C32C527001F2EBB /* ca */, 43EC79AA2C3C00BF00519D98 /* es-MX */, + 43E0BE3C2C60EC4C004FED65 /* hr */, ); name = KeyboardAccessory.strings; sourceTree = ""; @@ -16512,6 +16754,7 @@ 43D2F8B72A8A45570095D4EB /* su */, 4364EA842B1DE9FA003A1240 /* bs */, 43EB5F982B2728070063D23E /* ug */, + 43E0BE342C60EC4B004FED65 /* hr */, ); name = BiometricAuthentication.strings; sourceTree = ""; @@ -16646,6 +16889,7 @@ 43EB5FAA2B2728080063D23E /* ug */, 4357B4942C168FF1003518CF /* su */, 43EC79AE2C3C00BF00519D98 /* es-MX */, + 43E0BE402C60EC4C004FED65 /* hr */, ); name = Shopping.strings; sourceTree = ""; @@ -16715,6 +16959,7 @@ 4397616F2A0BCB850062C60C /* gl */, 4364EA8F2B1DE9FB003A1240 /* bs */, 43EB5FA12B2728080063D23E /* ug */, + 43E0BE3B2C60EC4C004FED65 /* hr */, ); name = JumpBackIn.strings; sourceTree = ""; @@ -16784,6 +17029,7 @@ 439761772A0BCB860062C60C /* gl */, 4364EA9C2B1DE9FB003A1240 /* bs */, 43EB5FAE2B2728080063D23E /* ug */, + 43C9CD4A2C6A26D9002F86B4 /* hr */, ); name = ToolbarLocation.strings; sourceTree = ""; @@ -16849,6 +17095,7 @@ 432792682BB192FD001707B5 /* en-CA */, 439E221E2C04A5610061A923 /* su */, 43B890FF2C32C60B0049F033 /* es-MX */, + 43E0BE392C60EC4B004FED65 /* hr */, ); name = FirefoxHomepage.strings; sourceTree = ""; @@ -16913,6 +17160,7 @@ 43CCB4CE2C32C526001F2EBB /* ca */, 43B890F92C32C60A0049F033 /* es-MX */, 4361DF292C453DB200CE1295 /* sat-Olck */, + 43E0BE352C60EC4B004FED65 /* hr */, ); name = BottomSheet.strings; sourceTree = ""; @@ -17128,6 +17376,49 @@ name = TabsTray.strings; sourceTree = ""; }; + 437AE30E2C7C9A0C0053F7F3 /* NativeErrorPage.strings */ = { + isa = PBXVariantGroup; + children = ( + 437AE30F2C7C9A0C0053F7F3 /* bs */, + 43CFB14C2C7C9A2B003B700F /* co */, + 43DE88092C7C9A3B00A7D4E0 /* cs */, + 43EC361F2C7C9A4B00824F5E /* cy */, + 4345E88F2C7C9A5B00FC0D7D /* da */, + 43548AB92C7C9A6A0004C846 /* de */, + 437CD9FF2C7C9A8B00A34E3E /* el */, + 43E1DCD82C7C9AAB008E952F /* en-GB */, + 43F013FF2C7C9ABA00F8CF52 /* en-US */, + 43FECBDB2C7C9AC900D644AE /* eo */, + 4364018E2C7C9AE700FF64C6 /* es-AR */, + 4373440E2C7C9AF500457D4D /* es-CL */, + 430EC8982C7C9B5100759193 /* fi */, + 431D8FEE2C7C9B61001E041F /* fr */, + 43ADBF222C7C9BAF006B6E34 /* he */, + 4321F0522C7C9BE00001D0DD /* hsb */, + 43940EE22C7C9C0D000AA60B /* ia */, + 43B0B5692C7C9C2A00B2A5AE /* is */, + 43BF3C5F2C7C9C380017A926 /* it */, + 43C4422D2C7C9C4700ED0870 /* ja */, + 43400BC22C7C9C7100D8ECE9 /* kab */, + 434FE6142C7C9C7F00C44560 /* kk */, + 43EE827C2C7C9D4600CDF5B9 /* nn */, + 4361916A2C7C9D72005B31E3 /* pa-IN */, + 436FC4912C7C9D810083D324 /* pl */, + 437E27022C7C9D8F00CE7490 /* pt-BR */, + 438C433E2C7C9D9F005AB958 /* pt-PT */, + 4304BD432C7C9DCB0063AF05 /* ru */, + 438613FD2C7C9E05005E3A65 /* sk */, + 439410F22C7C9E1400328C59 /* sl */, + 430832542C7C9E4100F58087 /* sv */, + 43964FF82C7C9E89001C730A /* tr */, + 43B210482C7C9EA500B136AD /* ug */, + 4334C4F02C7C9EDE00FBEFCF /* vi */, + 43500A6B2C7C9EFB008E385F /* zh-CN */, + 43CF316F2C7C9F0900D898EE /* zh-TW */, + ); + name = NativeErrorPage.strings; + sourceTree = ""; + }; 43903A872C20502A00ACF76E /* ActivityStream.strings */ = { isa = PBXVariantGroup; children = ( @@ -17184,6 +17475,10 @@ 430901D52C3C00DB00D5F3AC /* eu */, 431B95F62C453A89003B17BF /* en-CA */, 4375F0E12C453BC4001F3A55 /* hu */, + 4300B0D92C57B06E00F609C1 /* es-MX */, + 437E67412C57B4AB00A8791A /* th */, + 43E0BE322C60EC4B004FED65 /* hr */, + 43C09A7A2C7C9EB400BCD71D /* uk */, ); name = ActivityStream.strings; sourceTree = ""; @@ -17243,6 +17538,9 @@ 430901D62C3C00DB00D5F3AC /* eu */, 431B95F72C453A89003B17BF /* en-CA */, 4375F0E22C453BC4001F3A55 /* hu */, + 437E67422C57B4AB00A8791A /* th */, + 43E0BE332C60EC4B004FED65 /* hr */, + 43C09A7B2C7C9EB400BCD71D /* uk */, ); name = AddressToolbar.strings; sourceTree = ""; @@ -17302,6 +17600,10 @@ 430901D82C3C00DB00D5F3AC /* eu */, 431B95F92C453A89003B17BF /* en-CA */, 4375F0E42C453BC4001F3A55 /* hu */, + 4300B0DB2C57B06E00F609C1 /* es-MX */, + 437E67442C57B4AB00A8791A /* th */, + 43E0BE382C60EC4B004FED65 /* hr */, + 43C09A7D2C7C9EB400BCD71D /* uk */, ); name = EnhancedTrackingProtection.strings; sourceTree = ""; @@ -17359,6 +17661,8 @@ 43B5176E2C3C0217004F9302 /* kab */, 431B95FA2C453A89003B17BF /* en-CA */, 4375F0E52C453BC4001F3A55 /* hu */, + 437E67452C57B4AB00A8791A /* th */, + 43C9CD452C6A26D9002F86B4 /* hr */, ); name = QRCode.strings; sourceTree = ""; @@ -17569,6 +17873,7 @@ 4364EA932B1DE9FB003A1240 /* bs */, 43EB5FA52B2728080063D23E /* ug */, 437E34352C3C03870025BFA9 /* ro */, + 43C9CD462C6A26D9002F86B4 /* hr */, ); name = ResearchSurface.strings; sourceTree = ""; @@ -17637,6 +17942,7 @@ 4364EA902B1DE9FB003A1240 /* bs */, 43EB5FA22B2728080063D23E /* ug */, 439E22232C04A5620061A923 /* su */, + 43E0BE3F2C60EC4C004FED65 /* hr */, ); name = Notification.strings; sourceTree = ""; @@ -17705,6 +18011,7 @@ 439CB65F2AE68F1F00A19D54 /* sq */, 4364EA9F2B1DE9FB003A1240 /* bs */, 43EB5FB12B2728090063D23E /* ug */, + 43E0BE432C60EC4C004FED65 /* hr */, ); name = ZoomPageBar.strings; sourceTree = ""; @@ -17912,6 +18219,7 @@ 4301323E2ADD559F00B1FAD7 /* sq */, 4364EA9D2B1DE9FB003A1240 /* bs */, 43EB5FAF2B2728080063D23E /* ug */, + 4300B0E12C57B06E00F609C1 /* es-MX */, ); name = UpdateCard.strings; sourceTree = ""; @@ -17974,6 +18282,7 @@ 4357B4922C168FF1003518CF /* su */, 43CCB4D72C32C527001F2EBB /* ca */, 43EC79AB2C3C00BF00519D98 /* es-MX */, + 43C9CD442C6A26D9002F86B4 /* hr */, ); name = PasswordAutofill.strings; sourceTree = ""; @@ -18039,6 +18348,7 @@ 43B890FA2C32C60A0049F033 /* es-MX */, 43D79A832C4539FD0057C720 /* br */, 4361DF2A2C453DB200CE1295 /* sat-Olck */, + 43C9CD422C6A26D8002F86B4 /* hr */, ); name = ContextualHints.strings; sourceTree = ""; @@ -18105,6 +18415,7 @@ 43CCB4D02C32C526001F2EBB /* ca */, 43B890FB2C32C60A0049F033 /* es-MX */, 4361DF2B2C453DB200CE1295 /* sat-Olck */, + 43E0BE362C60EC4B004FED65 /* hr */, ); name = CredentialProvider.strings; sourceTree = ""; @@ -18167,6 +18478,8 @@ 439E221D2C04A5610061A923 /* su */, 43CCB4D12C32C526001F2EBB /* ca */, 43B890FC2C32C60B0049F033 /* es-MX */, + 433020F72C57B40600AF5EE2 /* sat-Olck */, + 43C9CD432C6A26D9002F86B4 /* hr */, ); name = Credentials.strings; sourceTree = ""; @@ -18232,6 +18545,7 @@ 439E221F2C04A5610061A923 /* su */, 43CCB4D32C32C527001F2EBB /* ca */, 43B891002C32C60B0049F033 /* es-MX */, + 43E0BE3A2C60EC4C004FED65 /* hr */, ); name = FirefoxLogins.strings; sourceTree = ""; @@ -18297,6 +18611,7 @@ 4300B70C2C298D9C0099DEEC /* lo */, 43CCB4D52C32C527001F2EBB /* ca */, 43B891022C32C60B0049F033 /* es-MX */, + 43E0BE3D2C60EC4C004FED65 /* hr */, ); name = LoginsHelper.strings; sourceTree = ""; @@ -18358,6 +18673,7 @@ 43F851162C168FBB00FA74A7 /* si */, 4357B4952C168FF1003518CF /* su */, 43CCB4D92C32C527001F2EBB /* ca */, + 43C9CD492C6A26D9002F86B4 /* hr */, ); name = TabToolbar.strings; sourceTree = ""; @@ -18404,6 +18720,20 @@ 4338F50D2C453D8800851075 /* rm */, 43F6E7632C453DF70018EEA4 /* sq */, 4383E28B2C453EAA00B775A3 /* vi */, + 43E6BE132C57AF7600C2152C /* bs */, + 43383DEA2C57AF94001BCD0E /* co */, + 435B35652C57AFA200F1C0BE /* cs */, + 436957A82C57AFB100B079FE /* cy */, + 4378EFA82C57AFBF0031897A /* da */, + 4300B0DD2C57B06E00F609C1 /* es-MX */, + 4390F7E22C57B1E700F52D3C /* ja */, + 438B16432C57B39C007BD1A3 /* pl */, + 43B3869C2C57B446007728B6 /* sl */, + 437E67462C57B4AB00A8791A /* th */, + 433D64C62C60EE1100CEC389 /* nb */, + 43C9CD472C6A26D9002F86B4 /* hr */, + 43FECBDC2C7C9ACA00D644AE /* eo */, + 43C09A7E2C7C9EB400BCD71D /* uk */, ); name = ScanQRCode.strings; sourceTree = ""; @@ -18465,6 +18795,8 @@ 438ABE532C0DDB6C00528A07 /* br */, 43B68F092C1690270012DAE2 /* th */, 43CCB4D62C32C527001F2EBB /* ca */, + 4300B0DC2C57B06E00F609C1 /* es-MX */, + 43E0BE3E2C60EC4C004FED65 /* hr */, ); name = Microsurvey.strings; sourceTree = ""; @@ -18656,6 +18988,18 @@ 4375F0E32C453BC4001F3A55 /* hu */, 4338F50C2C453D8800851075 /* rm */, 43F6E7622C453DF70018EEA4 /* sq */, + 43383DE92C57AF94001BCD0E /* co */, + 4378EFA72C57AFBF0031897A /* da */, + 4300B0DA2C57B06E00F609C1 /* es-MX */, + 4390F7E12C57B1E700F52D3C /* ja */, + 438E812A2C57B30F004E1492 /* nb */, + 438B16422C57B39C007BD1A3 /* pl */, + 433020F82C57B40700AF5EE2 /* sat-Olck */, + 4396C29A2C57B42700837488 /* si */, + 437E67432C57B4AB00A8791A /* th */, + 43E0BE372C60EC4B004FED65 /* hr */, + 43FECBDA2C7C9AC900D644AE /* eo */, + 43C09A7C2C7C9EB400BCD71D /* uk */, ); name = EditAddress.strings; sourceTree = ""; @@ -18721,6 +19065,7 @@ 43D74AE12BCD4286000D0251 /* en-CA */, 4357B4932C168FF1003518CF /* su */, 43EC79AD2C3C00BF00519D98 /* es-MX */, + 43C9CD482C6A26D9002F86B4 /* hr */, ); name = Share.strings; sourceTree = ""; @@ -18865,6 +19210,65 @@ name = SearchHeaderTitle.strings; sourceTree = ""; }; + 43E6BE142C57AF7600C2152C /* Toolbar.strings */ = { + isa = PBXVariantGroup; + children = ( + 43E6BE152C57AF7600C2152C /* bs */, + 43383DEB2C57AF94001BCD0E /* co */, + 435B35662C57AFA200F1C0BE /* cs */, + 4378EFA92C57AFBF0031897A /* da */, + 4386E32C2C57AFCF00C97DCD /* de */, + 43DF2A222C57AFDD0083E728 /* dsb */, + 43ED215A2C57AFEC0014F10F /* el */, + 43FB540B2C57AFFA00562B25 /* en-CA */, + 430ADCC62C57B00800B0A55B /* en-GB */, + 431813AE2C57B017007C3268 /* en-US */, + 438C39022C57B042005CD2CC /* es-AR */, + 439A03F62C57B051003675A3 /* es-CL */, + 4300B0E02C57B06E00F609C1 /* es-MX */, + 433954F62C57B0A900EC1EAE /* fi */, + 4391326B2C57B0BB00805AE1 /* fr */, + 43211E6B2C57B11F003F9FAF /* he */, + 43961E7E2C57B15B00762948 /* hsb */, + 43B388562C57B1830004C424 /* hy-AM */, + 430B81E02C57B198008C994B /* ia */, + 4329BDC32C57B1BF007F18C7 /* is */, + 434ED7EC2C57B1D4000C86E4 /* it */, + 4390F7E32C57B1E800F52D3C /* ja */, + 437A0B5A2C57B22500FCF751 /* kab */, + 4315028A2C57B23B005BB18E /* kk */, + 4389B08A2C57B27500C52A68 /* ko */, + 43AB384A2C57B33900F19287 /* nl */, + 430439F42C57B34D005518C3 /* nn */, + 43328A002C57B38800444F7E /* pa-IN */, + 438B16442C57B39C007BD1A3 /* pl */, + 439A9F662C57B3AF00448564 /* pt-BR */, + 43A9086F2C57B3C300698450 /* pt-PT */, + 436705DA2C57B3D7002C7FE6 /* rm */, + 431D82792C57B3F600062D71 /* ru */, + 43A4B26E2C57B43700EF1B91 /* sk */, + 43B3869D2C57B446007728B6 /* sl */, + 43C2B2312C57B4560010E539 /* sq */, + 432985492C57B47B00FE7CA2 /* sv */, + 437E67472C57B4AB00A8791A /* th */, + 43B956C12C57B4C800F0E77B /* tr */, + 43D6F80C2C57B4E7004BFBB8 /* ug */, + 43571C312C57B52200A2DD89 /* vi */, + 43BFE5BA2C57B54100CFB4C5 /* zh-CN */, + 43CE9F3D2C57B5500069B3AF /* zh-TW */, + 437511122C60EA6E001E649A /* br */, + 4391631F2C60EA8C00263662 /* ca */, + 4304EDF32C60EABA00F3C9DE /* cy */, + 43E0BE422C60EC4C004FED65 /* hr */, + 433D64C72C60EE1100CEC389 /* nb */, + 43E7071E2C6A26FC00A29AE4 /* hu */, + 43A04D712C73605C0052F8EB /* eu */, + 43FECBDD2C7C9ACA00D644AE /* eo */, + 43C09A7F2C7C9EB400BCD71D /* uk */, + ); + name = Toolbar.strings; + sourceTree = ""; + }; 43F118FE2A52E42400C44C6C /* CustomizeFirefoxHome.strings */ = { isa = PBXVariantGroup; children = ( @@ -22883,7 +23287,7 @@ repositoryURL = "https://github.com/mozilla/rust-components-swift.git"; requirement = { kind = exactVersion; - version = 130.0.20240724050232; + version = 131.0.20240821050323; }; }; 435C85EE2788F4D00072B526 /* XCRemoteSwiftPackageReference "glean-swift" */ = { @@ -22891,7 +23295,7 @@ repositoryURL = "https://github.com/mozilla/glean-swift"; requirement = { kind = exactVersion; - version = 60.3.0; + version = 60.5.0; }; }; 4368F83B279669690013419B /* XCRemoteSwiftPackageReference "SnapKit" */ = { @@ -22923,7 +23327,7 @@ repositoryURL = "https://github.com/getsentry/sentry-cocoa.git"; requirement = { kind = exactVersion; - version = 8.21.0; + version = 8.34.0; }; }; 8AB30EC62B6C038600BD9A9B /* XCRemoteSwiftPackageReference "lottie-ios" */ = { diff --git a/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index cb48e650dce5..832902767ed7 100644 --- a/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/firefox-ios/Client.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -41,8 +41,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/mozilla/glean-swift", "state" : { - "revision" : "38c00984dcbc7aff85352b917a40f95395044eab", - "version" : "60.3.0" + "revision" : "2185e2eea2d5ce272ff5c0e851eed42d52104ce4", + "version" : "60.5.0" } }, { @@ -95,8 +95,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/mozilla/rust-components-swift.git", "state" : { - "revision" : "21102e5081ce1b7446b110f474d2d2a7a2029527", - "version" : "130.0.20240724050232" + "revision" : "368989055f07f4e2c135ebed7f4d649b22c1dae2", + "version" : "131.0.20240821050323" } }, { @@ -104,8 +104,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/getsentry/sentry-cocoa.git", "state" : { - "revision" : "38f4f70d07117b9f958a76b1bff278c2f29ffe0e", - "version" : "8.21.0" + "revision" : "d2ced2d961b34573ebd2ea0567a2f1408e90f0ae", + "version" : "8.34.0" } }, { @@ -122,8 +122,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-asn1.git", "state" : { - "revision" : "c7e239b5c1492ffc3ebd7fbcc7a92548ce4e78f0", - "version" : "1.1.0" + "revision" : "df5d2fcd22e3f480e3ef85bf23e277a4a0ef524d", + "version" : "1.2.0" } }, { @@ -140,8 +140,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-crypto.git", "state" : { - "revision" : "f0525da24dc3c6cbb2b6b338b65042bc91cbc4bb", - "version" : "3.3.0" + "revision" : "a53a7e8f858902659d4784322bede34f4e49097e", + "version" : "3.6.1" } }, { diff --git a/firefox-ios/Client/AdjustHelper.swift b/firefox-ios/Client/AdjustHelper.swift index 438e0eb67ec5..3498cc18dd60 100644 --- a/firefox-ios/Client/AdjustHelper.swift +++ b/firefox-ios/Client/AdjustHelper.swift @@ -1,124 +1,126 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/ - -import Common -import Foundation -import Adjust -import Shared - -final class AdjustHelper: NSObject, FeatureFlaggable { - private static let adjustAppTokenKey = "AdjustAppToken" - private let profile: Profile - private let telemetryHelper: AdjustTelemetryProtocol - - init(profile: Profile, - telemetryHelper: AdjustTelemetryProtocol = AdjustTelemetryHelper()) { - self.profile = profile - self.telemetryHelper = telemetryHelper - let sendUsageData = profile.prefs.boolForKey(AppConstants.prefSendUsageData) ?? true - - // This is required for adjust to work properly with ASA and we avoid directly disabling - // third-party sharing as there is a specific method provided to us by adjust for that. - // Note: These settings are persisted on the adjust backend as well - if sendUsageData { - if let adjustThirdPartySharing = ADJThirdPartySharing(isEnabledNumberBool: true) { - Adjust.trackThirdPartySharing(adjustThirdPartySharing) - } - } else { - Adjust.disableThirdPartySharing() - } - } - - func setupAdjust() { - guard let config = getConfig() else { return } - - // Always initialize Adjust if we have a config - otherwise we cannot enable/disable it later. Their SDK must be - // initialized through appDidFinishLaunching otherwise it will be in a bad state. - Adjust.appDidLaunch(config) - - AdjustHelper.setEnabled(shouldEnable) - } - - /// Used to enable or disable Adjust SDK and it's features. - /// If user has disabled Send Anonymous Usage Data then we ask Adjust to erase the user's data as well. - static func setEnabled(_ enabled: Bool) { - Adjust.setEnabled(enabled) - - if !enabled { - Adjust.disableThirdPartySharing() - Adjust.gdprForgetMe() - } - } - +// swiftlint:disable comment_spacing file_header +//// This Source Code Form is subject to the terms of the Mozilla Public +//// License, v. 2.0. If a copy of the MPL was not distributed with this +//// file, You can obtain one at http://mozilla.org/MPL/2.0/ +// +//import Common +//import Foundation +//import Adjust +//import Shared +// +//final class AdjustHelper: NSObject, FeatureFlaggable { +// private static let adjustAppTokenKey = "AdjustAppToken" +// private let profile: Profile +// private let telemetryHelper: AdjustTelemetryProtocol +// +// init(profile: Profile, +// telemetryHelper: AdjustTelemetryProtocol = AdjustTelemetryHelper()) { +// self.profile = profile +// self.telemetryHelper = telemetryHelper +// let sendUsageData = profile.prefs.boolForKey(AppConstants.prefSendUsageData) ?? true +// +// // This is required for adjust to work properly with ASA and we avoid directly disabling +// // third-party sharing as there is a specific method provided to us by adjust for that. +// // Note: These settings are persisted on the adjust backend as well +// if sendUsageData { +// if let adjustThirdPartySharing = ADJThirdPartySharing(isEnabledNumberBool: true) { +// Adjust.trackThirdPartySharing(adjustThirdPartySharing) +// } +// } else { +// Adjust.disableThirdPartySharing() +// } +// } +// +// func setupAdjust() { +// guard let config = getConfig() else { return } +// +// // Always initialize Adjust if we have a config - otherwise we cannot enable/disable it later. Their SDK must be +// // initialized through appDidFinishLaunching otherwise it will be in a bad state. +// Adjust.appDidLaunch(config) +// +// AdjustHelper.setEnabled(shouldEnable) +// } +// +// /// Used to enable or disable Adjust SDK and it's features. +// /// If user has disabled Send Anonymous Usage Data then we ask Adjust to erase the user's data as well. +// static func setEnabled(_ enabled: Bool) { +// Adjust.setEnabled(enabled) +// +// if !enabled { +// Adjust.disableThirdPartySharing() +// Adjust.gdprForgetMe() +// } +// } +// // MARK: - Private - - private func getConfig() -> ADJConfig? { - let bundle = AppInfo.applicationBundle - guard let appToken = bundle.object(forInfoDictionaryKey: AdjustHelper.adjustAppTokenKey) as? String, - !appToken.isEmpty else { - return nil - } - - let isProd = featureFlags.isCoreFeatureEnabled(.adjustEnvironmentProd) - let environment = isProd ? ADJEnvironmentProduction : ADJEnvironmentSandbox - let config = ADJConfig(appToken: appToken, environment: environment) - config?.logLevel = isProd ? ADJLogLevelSuppress : ADJLogLevelDebug - - // Record attribution changes - // https://help.adjust.com/en/article/ios-sdk-adjconfig-class#set-up-delegate - config?.delegate = (self as AdjustHelper) - - return config - } - - /// Return true if Adjust should be enabled. If the user has disabled the Send Anonymous Usage Data - /// then we only do one ping to get the attribution and turn it off (i.e. we only enable it if we - /// have not seen the attribution data yet). - private var shouldEnable: Bool { - return shouldTrackRetention || !hasAttribution - } - - /// Return true if retention (session) tracking should be enabled. - /// This follows the Send Anonymous Usage Data setting. - private var shouldTrackRetention: Bool { - return profile.prefs.boolForKey(AppConstants.prefSendUsageData) ?? true - } - +// +// private func getConfig() -> ADJConfig? { +// let bundle = AppInfo.applicationBundle +// guard let appToken = bundle.object(forInfoDictionaryKey: AdjustHelper.adjustAppTokenKey) as? String, +// !appToken.isEmpty else { +// return nil +// } +// +// let isProd = featureFlags.isCoreFeatureEnabled(.adjustEnvironmentProd) +// let environment = isProd ? ADJEnvironmentProduction : ADJEnvironmentSandbox +// let config = ADJConfig(appToken: appToken, environment: environment) +// config?.logLevel = isProd ? ADJLogLevelSuppress : ADJLogLevelDebug +// +// // Record attribution changes +// // https://help.adjust.com/en/article/ios-sdk-adjconfig-class#set-up-delegate +// config?.delegate = (self as AdjustHelper) +// +// return config +// } +// +// /// Return true if Adjust should be enabled. If the user has disabled the Send Anonymous Usage Data +// /// then we only do one ping to get the attribution and turn it off (i.e. we only enable it if we +// /// have not seen the attribution data yet). +// private var shouldEnable: Bool { +// return shouldTrackRetention || !hasAttribution +// } +// +// /// Return true if retention (session) tracking should be enabled. +// /// This follows the Send Anonymous Usage Data setting. +// private var shouldTrackRetention: Bool { +// return profile.prefs.boolForKey(AppConstants.prefSendUsageData) ?? true +// } +// // MARK: - UserDefaults - - private enum UserDefaultsKey: String { - case hasAttribution = "com.moz.adjust.hasAttribution.key" - } - - private var hasAttribution: Bool { - get { UserDefaults.standard.object(forKey: UserDefaultsKey.hasAttribution.rawValue) as? Bool ?? false } - set { UserDefaults.standard.set(newValue, forKey: UserDefaultsKey.hasAttribution.rawValue) } - } -} - +// +// private enum UserDefaultsKey: String { +// case hasAttribution = "com.moz.adjust.hasAttribution.key" +// } +// +// private var hasAttribution: Bool { +// get { UserDefaults.standard.object(forKey: UserDefaultsKey.hasAttribution.rawValue) as? Bool ?? false } +// set { UserDefaults.standard.set(newValue, forKey: UserDefaultsKey.hasAttribution.rawValue) } +// } +//} +// // MARK: - AdjustDelegate -extension AdjustHelper: AdjustDelegate { - /// This is called when Adjust has figured out the attribution. It will call us with a summary - /// of all the things it knows. Like the campaign ID. We simply save a boolean that attribution - /// has changed so we know the single attribution ping to Adjust was done. - /// - /// We also disable Adjust based on the Send Anonymous Usage Data setting. - func adjustAttributionChanged(_ attribution: ADJAttribution?) { - hasAttribution = true - if !shouldEnable { - AdjustHelper.setEnabled(false) - } - - telemetryHelper.setAttributionData(attribution) - } - - func adjustDeeplinkResponse(_ deeplink: URL?) -> Bool { - guard let url = deeplink else { return true } - - // Send telemetry if url is not nil - let attribution = Adjust.attribution() - telemetryHelper.sendDeeplinkTelemetry(url: url, attribution: attribution) - return true - } -} +//extension AdjustHelper: AdjustDelegate { +// /// This is called when Adjust has figured out the attribution. It will call us with a summary +// /// of all the things it knows. Like the campaign ID. We simply save a boolean that attribution +// /// has changed so we know the single attribution ping to Adjust was done. +// /// +// /// We also disable Adjust based on the Send Anonymous Usage Data setting. +// func adjustAttributionChanged(_ attribution: ADJAttribution?) { +// hasAttribution = true +// if !shouldEnable { +// AdjustHelper.setEnabled(false) +// } +// +// telemetryHelper.setAttributionData(attribution) +// } +// +// func adjustDeeplinkResponse(_ deeplink: URL?) -> Bool { +// guard let url = deeplink else { return true } +// +// // Send telemetry if url is not nil +// let attribution = Adjust.attribution() +// telemetryHelper.sendDeeplinkTelemetry(url: url, attribution: attribution) +// return true +// } +//} +// swiftlint:enable comment_spacing file_header diff --git a/firefox-ios/Client/AdjustTelemetryHelper.swift b/firefox-ios/Client/AdjustTelemetryHelper.swift index 857b9e19f367..0db7a0578f83 100644 --- a/firefox-ios/Client/AdjustTelemetryHelper.swift +++ b/firefox-ios/Client/AdjustTelemetryHelper.swift @@ -1,58 +1,60 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/ - -import Foundation -import Adjust - -protocol AdjustTelemetryData { - var campaign: String? { get set } - var adgroup: String? { get set } - var creative: String? { get set } - var network: String? { get set } -} - -extension ADJAttribution: AdjustTelemetryData { -} - -protocol AdjustTelemetryProtocol { - func setAttributionData(_ attribution: AdjustTelemetryData?) - func sendDeeplinkTelemetry(url: URL, attribution: AdjustTelemetryData?) -} - -class AdjustTelemetryHelper: AdjustTelemetryProtocol { - var gleanWrapper: GleanWrapper - var telemetry: AdjustWrapper - - init(gleanWrapper: GleanWrapper = DefaultGleanWrapper(), - telemetry: AdjustWrapper = DefaultAdjustWrapper()) { - self.gleanWrapper = gleanWrapper - self.telemetry = telemetry - } - - func sendDeeplinkTelemetry(url: URL, attribution: AdjustTelemetryData?) { - telemetry.recordDeeplink(url: url) - - setAttributionData(attribution) - } - - func setAttributionData(_ attribution: AdjustTelemetryData?) { - if let campaign = attribution?.campaign { - telemetry.record(campaign: campaign) - } - - if let adgroup = attribution?.adgroup { - telemetry.record(adgroup: adgroup) - } - - if let creative = attribution?.creative { - telemetry.record(creative: creative) - } - - if let network = attribution?.network { - telemetry.record(network: network) - } - - gleanWrapper.submitPing() - } -} +// swiftlint:disable comment_spacing file_header +//// This Source Code Form is subject to the terms of the Mozilla Public +//// License, v. 2.0. If a copy of the MPL was not distributed with this +//// file, You can obtain one at http://mozilla.org/MPL/2.0/ +// +//import Foundation +//import Adjust +// +//protocol AdjustTelemetryData { +// var campaign: String? { get set } +// var adgroup: String? { get set } +// var creative: String? { get set } +// var network: String? { get set } +//} +// +//extension ADJAttribution: AdjustTelemetryData { +//} +// +//protocol AdjustTelemetryProtocol { +// func setAttributionData(_ attribution: AdjustTelemetryData?) +// func sendDeeplinkTelemetry(url: URL, attribution: AdjustTelemetryData?) +//} +// +//class AdjustTelemetryHelper: AdjustTelemetryProtocol { +// var gleanWrapper: GleanWrapper +// var telemetry: AdjustWrapper +// +// init(gleanWrapper: GleanWrapper = DefaultGleanWrapper(), +// telemetry: AdjustWrapper = DefaultAdjustWrapper()) { +// self.gleanWrapper = gleanWrapper +// self.telemetry = telemetry +// } +// +// func sendDeeplinkTelemetry(url: URL, attribution: AdjustTelemetryData?) { +// telemetry.recordDeeplink(url: url) +// +// setAttributionData(attribution) +// } +// +// func setAttributionData(_ attribution: AdjustTelemetryData?) { +// if let campaign = attribution?.campaign { +// telemetry.record(campaign: campaign) +// } +// +// if let adgroup = attribution?.adgroup { +// telemetry.record(adgroup: adgroup) +// } +// +// if let creative = attribution?.creative { +// telemetry.record(creative: creative) +// } +// +// if let network = attribution?.network { +// telemetry.record(network: network) +// } +// +// gleanWrapper.submitPing() +// } +//} +// swiftlint:enable comment_spacing file_header diff --git a/firefox-ios/Client/Application/AccessibilityIdentifiers.swift b/firefox-ios/Client/Application/AccessibilityIdentifiers.swift index 9d2e20cd81e9..0ee388e13aad 100644 --- a/firefox-ios/Client/Application/AccessibilityIdentifiers.swift +++ b/firefox-ios/Client/Application/AccessibilityIdentifiers.swift @@ -510,6 +510,7 @@ public struct AccessibilityIdentifiers { static let fxaSignInButton = "EmailSignIn.button" static let qrButton = "QRCodeSignIn.button" static let qrScanFailedAlertOkButton = "qrCodeAlert.okButton" + static let signInButton = "Sign in" } struct Search { diff --git a/firefox-ios/Client/Application/AppDelegate+SyncSentTabs.swift b/firefox-ios/Client/Application/AppDelegate+SyncSentTabs.swift index a0956a4e3698..ad555e43fecf 100644 --- a/firefox-ios/Client/Application/AppDelegate+SyncSentTabs.swift +++ b/firefox-ios/Client/Application/AppDelegate+SyncSentTabs.swift @@ -10,16 +10,17 @@ import Account import Common extension UIApplication { - var sendTabDelegate: SendTabDelegate { - return AppSendTabDelegate(app: self) + var fxaCommandsDelegate: FxACommandsDelegate { + return AppFxACommandsDelegate(app: self) } } -/// Sent tabs can be displayed not only by receiving push notifications, but by sync. +/// Close and Sent tabs can be displayed not only by receiving push notifications, +/// but by sync. /// Sync will get the list of sent tabs, and try to display any in that list. -/// Thus, push notifications are not needed to receive sent tabs, they can be handled -/// when the app performs a sync. -class AppSendTabDelegate: SendTabDelegate { +/// Thus, push notifications are not needed to receive sent or closed tabs; +/// they can be handled when the app performs a sync. +class AppFxACommandsDelegate: FxACommandsDelegate { private let app: ApplicationStateProvider private let logger: Logger private var applicationHelper: ApplicationHelper @@ -46,4 +47,10 @@ class AppSendTabDelegate: SendTabDelegate { } } } + + func closeTabs(for urls: [URL]) { + Task { + await self.applicationHelper.closeTabs(urls) + } + } } diff --git a/firefox-ios/Client/Application/AppDelegate.swift b/firefox-ios/Client/Application/AppDelegate.swift index c75da4485cb6..f0645d45d105 100644 --- a/firefox-ios/Client/Application/AppDelegate.swift +++ b/firefox-ios/Client/Application/AppDelegate.swift @@ -25,7 +25,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { lazy var profile: Profile = BrowserProfile( localName: "profile", - sendTabDelegate: UIApplication.shared.sendTabDelegate, + fxaCommandsDelegate: UIApplication.shared.fxaCommandsDelegate, creditCardAutofillEnabled: creditCardAutofillStatus ) diff --git a/firefox-ios/Client/Application/AppLaunchUtil.swift b/firefox-ios/Client/Application/AppLaunchUtil.swift index 47b2667e2426..fefd5d5df2a4 100644 --- a/firefox-ios/Client/Application/AppLaunchUtil.swift +++ b/firefox-ios/Client/Application/AppLaunchUtil.swift @@ -11,7 +11,7 @@ import Glean class AppLaunchUtil { private var logger: Logger - private var adjustHelper: AdjustHelper +// private var adjustHelper: AdjustHelper private var profile: Profile private let introScreenManager: IntroScreenManager @@ -19,7 +19,7 @@ class AppLaunchUtil { profile: Profile) { self.logger = logger self.profile = profile - self.adjustHelper = AdjustHelper(profile: profile) +// self.adjustHelper = AdjustHelper(profile: profile) self.introScreenManager = IntroScreenManager(prefs: profile.prefs) } @@ -78,7 +78,13 @@ class AppLaunchUtil { } } - RustFirefoxAccounts.startup(prefs: profile.prefs) { _ in + // RustFirefoxAccounts doesn't have access to the feature flags + // So we check the nimbus flag here before sending it to the startup + var fxaFeatures: RustFxAFeatures = [] + if LegacyFeatureFlagsManager.shared.isFeatureEnabled(.closeRemoteTabs, checking: .buildAndUser) { + fxaFeatures.insert(.closeRemoteTabs) + } + RustFirefoxAccounts.startup(prefs: profile.prefs, features: fxaFeatures) { _ in self.logger.log("RustFirefoxAccounts started", level: .info, category: .sync) AppEventQueue.signal(event: .accountManagerInitialized) } @@ -122,7 +128,7 @@ class AppLaunchUtil { } updateSessionCount() - adjustHelper.setupAdjust() +// adjustHelper.setupAdjust() AppEventQueue.signal(event: .postLaunchDependenciesComplete) } diff --git a/firefox-ios/Client/Application/UITestAppDelegate.swift b/firefox-ios/Client/Application/UITestAppDelegate.swift index 7f347a7daed4..7f9d71402c57 100644 --- a/firefox-ios/Client/Application/UITestAppDelegate.swift +++ b/firefox-ios/Client/Application/UITestAppDelegate.swift @@ -34,12 +34,7 @@ class UITestAppDelegate: AppDelegate, FeatureFlaggable { launchArguments.forEach { arg in if arg.starts(with: LaunchArguments.ServerPort) { - let portString = arg.replacingOccurrences(of: LaunchArguments.ServerPort, with: "") - if let port = Int(portString) { - AppInfo.webserverPort = port - } else { - fatalError("Failed to set web server port override.") - } + configureWebserverPort(arg) } if arg.starts(with: LaunchArguments.LoadDatabasePrefix) { @@ -134,13 +129,13 @@ class UITestAppDelegate: AppDelegate, FeatureFlaggable { // Use a clean profile for each test session. profile = BrowserProfile( localName: "testProfile", - sendTabDelegate: application.sendTabDelegate, + fxaCommandsDelegate: application.fxaCommandsDelegate, clear: true ) } else { profile = BrowserProfile( localName: "testProfile", - sendTabDelegate: application.sendTabDelegate + fxaCommandsDelegate: application.fxaCommandsDelegate ) } @@ -200,6 +195,15 @@ class UITestAppDelegate: AppDelegate, FeatureFlaggable { return profile } + private func configureWebserverPort(_ arg: String) { + let portString = arg.replacingOccurrences(of: LaunchArguments.ServerPort, with: "") + if let port = Int(portString) { + AppInfo.webserverPort = port + } else { + fatalError("Failed to set web server port override.") + } + } + override func application( _ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil diff --git a/firefox-ios/Client/Application/WindowManager.swift b/firefox-ios/Client/Application/WindowManager.swift index 4825cf4b8c2b..3ed5d022b560 100644 --- a/firefox-ios/Client/Application/WindowManager.swift +++ b/firefox-ios/Client/Application/WindowManager.swift @@ -24,10 +24,6 @@ enum MultiWindowAction { /// General window management class that provides some basic coordination and /// state management for multiple windows shared across a single running app. protocol WindowManager { - /// The UUID of the active window (there is always at least 1, except in - /// the earliest stages of app startup lifecycle) - var activeWindow: WindowUUID { get set } - /// A collection of all open windows and their related metadata. var windows: [WindowUUID: AppWindowInfo] { get } @@ -98,14 +94,9 @@ final class WindowManagerImplementation: WindowManager, WindowTabsSyncCoordinato // process of initial configuration. private var reservedUUIDs: [WindowUUID] = [] - var activeWindow: WindowUUID { - get { return uuidForActiveWindow() } - set { _activeWindowUUID = newValue } - } private let logger: Logger private let tabDataStore: TabDataStore private let defaults: UserDefaultsInterface - private var _activeWindowUUID: WindowUUID? private let tabSyncCoordinator = WindowTabsSyncCoordinator() private let widgetSimpleTabsCoordinator = WindowSimpleTabsCoordinator() @@ -145,7 +136,7 @@ final class WindowManagerImplementation: WindowManager, WindowTabsSyncCoordinato guard let tabManager = window(for: windowUUID)?.tabManager else { assertionFailure("Tab Manager unavailable for requested UUID: \(windowUUID). This is a client error.") logger.log("No tab manager for window UUID.", level: .fatal, category: .window) - return window(for: activeWindow)?.tabManager ?? windows.first!.value.tabManager! + return windows.first!.value.tabManager! } return tabManager @@ -345,38 +336,6 @@ final class WindowManagerImplementation: WindowManager, WindowTabsSyncCoordinato private func updateWindow(_ info: AppWindowInfo?, for uuid: WindowUUID) { windows[uuid] = info - didUpdateWindow(uuid) - } - - /// Called internally when a window is updated (or removed). - /// - Parameter uuid: the UUID of the window that changed. - private func didUpdateWindow(_ uuid: WindowUUID) { - // Convenience. If the client has successfully configured - // a window and it is the only window in the app, we can - // be sure we automatically set it as the active window. - if windows.count == 1 { - activeWindow = windows.keys.first! - } - } - - private func uuidForActiveWindow() -> WindowUUID { - guard !windows.isEmpty else { - // No app windows. Unsupported state; can't recover gracefully. - fatalError() - } - - guard windows.count > 1 else { - // For apps with only 1 window we can always safely return it as the active window. - return windows.keys.first! - } - - guard let uuid = _activeWindowUUID else { - let message = "App has multiple windows but no active window UUID. This is a client error." - logger.log(message, level: .fatal, category: .window) - assertionFailure(message) - return windows.keys.first! - } - return uuid } private func window(for windowUUID: WindowUUID, createIfNeeded: Bool = false) -> AppWindowInfo? { diff --git a/firefox-ios/Client/ApplicationHelper.swift b/firefox-ios/Client/ApplicationHelper.swift index 55925612388f..1079f5b3bce5 100644 --- a/firefox-ios/Client/ApplicationHelper.swift +++ b/firefox-ios/Client/ApplicationHelper.swift @@ -9,6 +9,7 @@ protocol ApplicationHelper { func openSettings() func open(_ url: URL) func open(_ url: URL, inWindow: WindowUUID) + func closeTabs(_ urls: [URL]) async } /// UIApplication.shared wrapper @@ -46,4 +47,17 @@ struct DefaultApplicationHelper: ApplicationHelper { open(url) } } + + /// Closes all tabs that match the url passed in + /// This is most likely from other clients connected to the same + /// account requesting to close the tab on this device + /// + /// - Parameters: + /// - urls: an array of URLs requested to be closed + func closeTabs(_ urls: [URL]) async { + let windowManager = AppContainer.shared.resolve() as WindowManager + for tabManager in windowManager.allWindowTabManagers() { + await tabManager.removeTabs(by: urls) + } + } } diff --git a/firefox-ios/Client/Assets/CC_Script/AutofillTelemetry.sys.mjs b/firefox-ios/Client/Assets/CC_Script/AutofillTelemetry.sys.mjs index a637c3766886..f76afd96581d 100644 --- a/firefox-ios/Client/Assets/CC_Script/AutofillTelemetry.sys.mjs +++ b/firefox-ios/Client/Assets/CC_Script/AutofillTelemetry.sys.mjs @@ -122,7 +122,7 @@ class AutofillTelemetryBase { const extra = this.#initFormEventExtra("unavailable"); for (const fieldDetail of fieldDetails) { - let { filledState, value } = data[fieldDetail.elementId]; + let { filledState, value } = data.get(fieldDetail.elementId); switch (filledState) { case FIELD_STATES.AUTO_FILLED: filledState = "filled"; @@ -152,7 +152,7 @@ class AutofillTelemetryBase { const extra = this.#initFormEventExtra("unavailable"); for (const fieldDetail of fieldDetails) { - let { filledState, value } = data[fieldDetail.elementId]; + let { filledState, value } = data.get(fieldDetail.elementId); switch (filledState) { case FIELD_STATES.AUTO_FILLED: filledState = "autofilled"; diff --git a/firefox-ios/Client/Assets/CC_Script/FormAutofillHandler.sys.mjs b/firefox-ios/Client/Assets/CC_Script/FormAutofillHandler.sys.mjs index f8d7d6309beb..3c0fdb68a14e 100644 --- a/firefox-ios/Client/Assets/CC_Script/FormAutofillHandler.sys.mjs +++ b/firefox-ios/Client/Assets/CC_Script/FormAutofillHandler.sys.mjs @@ -227,7 +227,7 @@ export class FormAutofillHandler { * @param {object} profile * A profile to be previewed with */ - async previewFields(elementIds, profile) { + previewFields(elementIds, profile) { this.getAdaptedProfiles([profile]); for (const fieldDetail of this.fieldDetails) { @@ -945,15 +945,15 @@ export class FormAutofillHandler { * value: The value of the element */ collectFormFilledData() { - const filledData = {}; + const filledData = new Map(); for (const fieldDetail of this.fieldDetails) { const element = fieldDetail.element; - filledData[fieldDetail.elementId] = { + filledData.set(fieldDetail.elementId, { filledState: element.autofillState, filledValue: this.computeFillingValue(fieldDetail), value: element.value, - }; + }); } return filledData; } diff --git a/firefox-ios/Client/Assets/CC_Script/FormAutofillSection.sys.mjs b/firefox-ios/Client/Assets/CC_Script/FormAutofillSection.sys.mjs index f1626ecec1e6..61bb1a52195f 100644 --- a/firefox-ios/Client/Assets/CC_Script/FormAutofillSection.sys.mjs +++ b/firefox-ios/Client/Assets/CC_Script/FormAutofillSection.sys.mjs @@ -294,7 +294,7 @@ export class FormAutofillSection { }; for (const detail of this.fieldDetails) { - const { filledValue } = formFilledData[detail.elementId]; + const { filledValue } = formFilledData.get(detail.elementId); if ( !filledValue || diff --git a/firefox-ios/Client/Assets/CC_Script/FormStateManager.sys.mjs b/firefox-ios/Client/Assets/CC_Script/FormStateManager.sys.mjs index 1e4662b2defe..6b5595b16024 100644 --- a/firefox-ios/Client/Assets/CC_Script/FormStateManager.sys.mjs +++ b/firefox-ios/Client/Assets/CC_Script/FormStateManager.sys.mjs @@ -26,7 +26,6 @@ export class FormStateManager { * @returns {Array | null} * Return target form's handler from content cache * (or return null if the information is not found in the cache). - * */ getFormHandler(element) { if (!element) { diff --git a/firefox-ios/Client/Assets/CC_Script/HeuristicsRegExp.sys.mjs b/firefox-ios/Client/Assets/CC_Script/HeuristicsRegExp.sys.mjs index c4141628f8fe..1b7ff0d0b61f 100644 --- a/firefox-ios/Client/Assets/CC_Script/HeuristicsRegExp.sys.mjs +++ b/firefox-ios/Client/Assets/CC_Script/HeuristicsRegExp.sys.mjs @@ -555,7 +555,7 @@ export const HeuristicsRegExp = { // Note: `cc-name` expression has been moved up, above `name`, in // order to handle specialization through ordering. "cc-number": - "(add)?(?:card|cc|acct).?(?:number|#|no|num|field)" + + "(add)?(?:card|cc|acct).?(?:number|#|no|num|field(?!s)|pan)" + // In order to support webkit we convert all negative lookbehinds to a capture group // (? (?notword)|word // TODO: Bug 1829583 diff --git a/firefox-ios/Client/Assets/Images.xcassets/externalLinkSmall.imageset/Contents.json b/firefox-ios/Client/Assets/Images.xcassets/externalLinkSmall.imageset/Contents.json new file mode 100644 index 000000000000..bd08dc6caa87 --- /dev/null +++ b/firefox-ios/Client/Assets/Images.xcassets/externalLinkSmall.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "externalLinkSmall.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/firefox-ios/Client/Assets/Images.xcassets/externalLinkSmall.imageset/externalLinkSmall.pdf b/firefox-ios/Client/Assets/Images.xcassets/externalLinkSmall.imageset/externalLinkSmall.pdf new file mode 100644 index 000000000000..371e4c8a0886 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/externalLinkSmall.imageset/externalLinkSmall.pdf differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/shareAppleLarge.imageset/shareAppleLarge.pdf b/firefox-ios/Client/Assets/Images.xcassets/shareAppleLarge.imageset/shareAppleLarge.pdf deleted file mode 100644 index 7bf0bf5aa5a2..000000000000 Binary files a/firefox-ios/Client/Assets/Images.xcassets/shareAppleLarge.imageset/shareAppleLarge.pdf and /dev/null differ diff --git a/firefox-ios/Client/Assets/Images.xcassets/shareAppleLarge.imageset/Contents.json b/firefox-ios/Client/Assets/Images.xcassets/shareLarge.imageset/Contents.json similarity index 82% rename from firefox-ios/Client/Assets/Images.xcassets/shareAppleLarge.imageset/Contents.json rename to firefox-ios/Client/Assets/Images.xcassets/shareLarge.imageset/Contents.json index 24830b2b641d..0382672dd09e 100644 --- a/firefox-ios/Client/Assets/Images.xcassets/shareAppleLarge.imageset/Contents.json +++ b/firefox-ios/Client/Assets/Images.xcassets/shareLarge.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "shareAppleLarge.pdf", + "filename" : "shareLarge.pdf", "idiom" : "universal" } ], diff --git a/firefox-ios/Client/Assets/Images.xcassets/shareLarge.imageset/shareLarge.pdf b/firefox-ios/Client/Assets/Images.xcassets/shareLarge.imageset/shareLarge.pdf new file mode 100644 index 000000000000..139611678046 Binary files /dev/null and b/firefox-ios/Client/Assets/Images.xcassets/shareLarge.imageset/shareLarge.pdf differ diff --git a/firefox-ios/Client/Coordinators/AddressAutofillCoordinator.swift b/firefox-ios/Client/Coordinators/AddressAutofillCoordinator.swift index e8b071b2f7ab..ed9a9308fa67 100644 --- a/firefox-ios/Client/Coordinators/AddressAutofillCoordinator.swift +++ b/firefox-ios/Client/Coordinators/AddressAutofillCoordinator.swift @@ -11,10 +11,12 @@ import WebKit class AddressAutofillCoordinator: BaseCoordinator { // MARK: - Properties + typealias BottomSheetCardParentCoordinator = BrowserNavigationHandler & ParentCoordinatorDelegate private let profile: Profile private let themeManager: ThemeManager private let tabManager: TabManager - private weak var parentCoordinator: ParentCoordinatorDelegate? + private weak var parentCoordinator: BottomSheetCardParentCoordinator? + private var windowUUID: WindowUUID { return tabManager.windowUUID } // MARK: - Initializers @@ -28,7 +30,7 @@ class AddressAutofillCoordinator: BaseCoordinator { init( profile: Profile, router: Router, - parentCoordinator: ParentCoordinatorDelegate?, + parentCoordinator: BottomSheetCardParentCoordinator?, themeManager: ThemeManager = AppContainer.shared.resolve(), tabManager: TabManager ) { @@ -46,8 +48,8 @@ class AddressAutofillCoordinator: BaseCoordinator { func showAddressAutofill(frame: WKFrameInfo?) { let bottomSheetViewModel = BottomSheetViewModel( closeButtonA11yLabel: .CloseButtonTitle, - closeButtonA11yIdentifier: - AccessibilityIdentifiers.Autofill.addressCloseButton) + closeButtonA11yIdentifier: AccessibilityIdentifiers.Autofill.addressCloseButton + ) let viewModel = AddressListViewModel( windowUUID: tabManager.windowUUID, @@ -75,6 +77,11 @@ class AddressAutofillCoordinator: BaseCoordinator { self.parentCoordinator?.didFinish(from: self) } } + viewModel.manageAddressesInfoAction = { [weak self] in + guard let self else { return } + parentCoordinator?.show(settings: .addresses, onDismiss: {}) + parentCoordinator?.didFinish(from: self) + } let bottomSheetView = AddressAutoFillBottomSheetView(windowUUID: tabManager.windowUUID, addressListViewModel: viewModel) let hostingController = SelfSizingHostingController(rootView: bottomSheetView) @@ -85,8 +92,10 @@ class AddressAutofillCoordinator: BaseCoordinator { object: .addressAutofillPromptDismissed ) } - let bottomSheetVC = BottomSheetViewController(viewModel: bottomSheetViewModel, - childViewController: hostingController) + let bottomSheetVC = BottomSheetViewController( + viewModel: bottomSheetViewModel, + childViewController: hostingController, + windowUUID: tabManager.windowUUID) TelemetryWrapper.recordEvent( category: .action, method: .tap, diff --git a/firefox-ios/Client/Coordinators/CredentialAutofillCoordinator.swift b/firefox-ios/Client/Coordinators/CredentialAutofillCoordinator.swift index 7fc48c194562..51ebbeb57a21 100644 --- a/firefox-ios/Client/Coordinators/CredentialAutofillCoordinator.swift +++ b/firefox-ios/Client/Coordinators/CredentialAutofillCoordinator.swift @@ -48,7 +48,7 @@ class CredentialAutofillCoordinator: BaseCoordinator { viewType state: CreditCardBottomSheetState, frame: WKFrameInfo?, alertContainer: UIView) { - let creditCardControllerViewModel = CreditCardBottomSheetViewModel(profile: profile, + let creditCardControllerViewModel = CreditCardBottomSheetViewModel(creditCardProvider: profile.autofill, creditCard: creditCard, decryptedCreditCard: decryptedCard, state: state) @@ -107,12 +107,14 @@ class CredentialAutofillCoordinator: BaseCoordinator { var bottomSheetViewModel = BottomSheetViewModel( closeButtonA11yLabel: .CloseButtonTitle, - closeButtonA11yIdentifier: AccessibilityIdentifiers.Autofill.creditCardCloseButton) + closeButtonA11yIdentifier: AccessibilityIdentifiers.Autofill.creditCardCloseButton + ) bottomSheetViewModel.shouldDismissForTapOutside = false let bottomSheetVC = BottomSheetViewController( viewModel: bottomSheetViewModel, - childViewController: viewController + childViewController: viewController, + windowUUID: windowUUID ) router.present(bottomSheetVC) if state == .save { @@ -182,12 +184,14 @@ class CredentialAutofillCoordinator: BaseCoordinator { var bottomSheetViewModel = BottomSheetViewModel( closeButtonA11yLabel: .CloseButtonTitle, - closeButtonA11yIdentifier: AccessibilityIdentifiers.Autofill.loginCloseButton) + closeButtonA11yIdentifier: AccessibilityIdentifiers.Autofill.loginCloseButton + ) bottomSheetViewModel.shouldDismissForTapOutside = false let bottomSheetVC = BottomSheetViewController( viewModel: bottomSheetViewModel, - childViewController: viewController + childViewController: viewController, + windowUUID: windowUUID ) router.present(bottomSheetVC) TelemetryWrapper.recordEvent( diff --git a/firefox-ios/Client/Coordinators/EnhancedTrackingProtectionCoordinator.swift b/firefox-ios/Client/Coordinators/EnhancedTrackingProtectionCoordinator.swift index 4316d5c47bf3..c79f32ddcb45 100644 --- a/firefox-ios/Client/Coordinators/EnhancedTrackingProtectionCoordinator.swift +++ b/firefox-ios/Client/Coordinators/EnhancedTrackingProtectionCoordinator.swift @@ -13,11 +13,17 @@ protocol EnhancedTrackingProtectionCoordinatorDelegate: AnyObject { } class EnhancedTrackingProtectionCoordinator: BaseCoordinator, - EnhancedTrackingProtectionMenuDelegate { + TrackingProtectionMenuDelegate, + EnhancedTrackingProtectionMenuDelegate, + FeatureFlaggable { private let profile: Profile private let tabManager: TabManager - private let enhancedTrackingProtectionMenuVC: EnhancedTrackingProtectionMenuVC + private var legacyEnhancedTrackingProtectionMenuVC: EnhancedTrackingProtectionMenuVC? + private var enhancedTrackingProtectionMenuVC: TrackingProtectionViewController? weak var parentCoordinator: EnhancedTrackingProtectionCoordinatorDelegate? + private var trackingProtectionRefactorStatus: Bool { + featureFlags.isFeatureEnabled(.trackingProtectionRefactor, checking: .buildOnly) + } init(router: Router, profile: Profile = AppContainer.shared.resolve(), @@ -27,33 +33,72 @@ class EnhancedTrackingProtectionCoordinator: BaseCoordinator, let url = tab?.url ?? URL(fileURLWithPath: "") let displayTitle = tab?.displayTitle ?? "" let contentBlockerStatus = tab?.contentBlocker?.status ?? .blocking + let contentBlockerStats = tab?.contentBlocker?.stats let connectionSecure = tab?.webView?.hasOnlySecureContent ?? true - let etpViewModel = EnhancedTrackingProtectionMenuVM( - url: url, - displayTitle: displayTitle, - connectionSecure: connectionSecure, - globalETPIsEnabled: FirefoxTabContentBlocker.isTrackingProtectionEnabled(prefs: profile.prefs), - contentBlockerStatus: contentBlockerStatus) - - self.enhancedTrackingProtectionMenuVC = EnhancedTrackingProtectionMenuVC(viewModel: etpViewModel, - windowUUID: tabManager.windowUUID) self.profile = profile self.tabManager = tabManager super.init(router: router) - enhancedTrackingProtectionMenuVC.enhancedTrackingProtectionMenuDelegate = self + if self.trackingProtectionRefactorStatus { + let etpViewModel = TrackingProtectionModel( + url: url, + displayTitle: displayTitle, + connectionSecure: connectionSecure, + globalETPIsEnabled: FirefoxTabContentBlocker.isTrackingProtectionEnabled(prefs: profile.prefs), + contentBlockerStatus: contentBlockerStatus, + contentBlockerStats: contentBlockerStats, + selectedTab: tabManager.selectedTab + ) + + enhancedTrackingProtectionMenuVC = TrackingProtectionViewController(viewModel: etpViewModel, + windowUUID: tabManager.windowUUID) + enhancedTrackingProtectionMenuVC?.enhancedTrackingProtectionMenuDelegate = self + } else { + let oldEtpViewModel = EnhancedTrackingProtectionMenuVM( + url: url, + displayTitle: displayTitle, + connectionSecure: connectionSecure, + globalETPIsEnabled: FirefoxTabContentBlocker.isTrackingProtectionEnabled(prefs: profile.prefs), + contentBlockerStatus: contentBlockerStatus + ) + + legacyEnhancedTrackingProtectionMenuVC = EnhancedTrackingProtectionMenuVC(viewModel: oldEtpViewModel, + windowUUID: tabManager.windowUUID) + legacyEnhancedTrackingProtectionMenuVC?.enhancedTrackingProtectionMenuDelegate = self + } } func start(sourceView: UIView) { - if UIDevice.current.userInterfaceIdiom == .phone { - enhancedTrackingProtectionMenuVC.modalPresentationStyle = .custom - enhancedTrackingProtectionMenuVC.transitioningDelegate = self - } else { - enhancedTrackingProtectionMenuVC.asPopover = true - enhancedTrackingProtectionMenuVC.modalPresentationStyle = .popover - enhancedTrackingProtectionMenuVC.popoverPresentationController?.sourceView = sourceView - enhancedTrackingProtectionMenuVC.popoverPresentationController?.permittedArrowDirections = .up + if trackingProtectionRefactorStatus, let enhancedTrackingProtectionMenuVC { + if UIDevice.current.userInterfaceIdiom == .phone { + if let sheetPresentationController = enhancedTrackingProtectionMenuVC.sheetPresentationController { + sheetPresentationController.detents = [.medium(), .large()] + sheetPresentationController.prefersScrollingExpandsWhenScrolledToEdge = true + sheetPresentationController.preferredCornerRadius = TPMenuUX.UX.modalMenuCornerRadius + } + enhancedTrackingProtectionMenuVC.asPopover = true + router.present(enhancedTrackingProtectionMenuVC, animated: true, completion: nil) + } else { + enhancedTrackingProtectionMenuVC.asPopover = true + if trackingProtectionRefactorStatus { + enhancedTrackingProtectionMenuVC.preferredContentSize = CGSize(width: 480, height: 517) + } + enhancedTrackingProtectionMenuVC.modalPresentationStyle = .popover + enhancedTrackingProtectionMenuVC.popoverPresentationController?.sourceView = sourceView + enhancedTrackingProtectionMenuVC.popoverPresentationController?.permittedArrowDirections = .up + router.present(enhancedTrackingProtectionMenuVC, animated: true, completion: nil) + } + } else if let legacyEnhancedTrackingProtectionMenuVC { + if UIDevice.current.userInterfaceIdiom == .phone { + legacyEnhancedTrackingProtectionMenuVC.modalPresentationStyle = .custom + legacyEnhancedTrackingProtectionMenuVC.transitioningDelegate = self + } else { + legacyEnhancedTrackingProtectionMenuVC.asPopover = true + legacyEnhancedTrackingProtectionMenuVC.modalPresentationStyle = .popover + legacyEnhancedTrackingProtectionMenuVC.popoverPresentationController?.sourceView = sourceView + legacyEnhancedTrackingProtectionMenuVC.popoverPresentationController?.permittedArrowDirections = .up + } + router.present(legacyEnhancedTrackingProtectionMenuVC, animated: true, completion: nil) } - router.present(enhancedTrackingProtectionMenuVC, animated: true, completion: nil) } // MARK: - EnhancedTrackingProtectionMenuDelegate diff --git a/firefox-ios/Client/Coordinators/Router/Route.swift b/firefox-ios/Client/Coordinators/Router/Route.swift index 17ff7c864de8..86e6ea78fc7d 100644 --- a/firefox-ios/Client/Coordinators/Router/Route.swift +++ b/firefox-ios/Client/Coordinators/Router/Route.swift @@ -86,6 +86,7 @@ enum Route: Equatable { /// An enumeration representing different sections of the settings menu. enum SettingsSection: String, CaseIterable, Equatable { + case addresses case contentBlocker case clearPrivateData = "clear-private-data" case creditCard diff --git a/firefox-ios/Client/Coordinators/SettingsCoordinator.swift b/firefox-ios/Client/Coordinators/SettingsCoordinator.swift index 7a14676f1916..b9dc99fe8edd 100644 --- a/firefox-ios/Client/Coordinators/SettingsCoordinator.swift +++ b/firefox-ios/Client/Coordinators/SettingsCoordinator.swift @@ -79,6 +79,16 @@ class SettingsCoordinator: BaseCoordinator, private func getSettingsViewController(settingsSection section: Route.SettingsSection) -> UIViewController? { switch section { + case .addresses: + let viewModel = AddressAutofillSettingsViewModel( + profile: profile, + windowUUID: windowUUID + ) + let viewController = AddressAutofillSettingsViewController( + addressAutofillViewModel: viewModel, + windowUUID: windowUUID + ) + return viewController case .newTab: let viewController = NewTabContentSettingsViewController(prefs: profile.prefs, windowUUID: windowUUID) diff --git a/firefox-ios/Client/Coordinators/ShareExtensionCoordinator.swift b/firefox-ios/Client/Coordinators/ShareExtensionCoordinator.swift index 208d2432d60e..9f75fbb439b1 100644 --- a/firefox-ios/Client/Coordinators/ShareExtensionCoordinator.swift +++ b/firefox-ios/Client/Coordinators/ShareExtensionCoordinator.swift @@ -78,7 +78,7 @@ class ShareExtensionCoordinator: BaseCoordinator, showSendToDevice(url: url) case CustomActivityAction.copyLink.actionType: SimpleToast().showAlertWithText( - .AppMenu.AppMenuCopyURLConfirmMessage, + .LegacyAppMenu.AppMenuCopyURLConfirmMessage, bottomContainer: alertContainer, theme: themeManager.getCurrentTheme(for: windowUUID)) dequeueNotShownJSAlert() @@ -163,7 +163,7 @@ class ShareExtensionCoordinator: BaseCoordinator, self.router.dismiss() self.parentCoordinator?.didFinish(from: self) DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { - SimpleToast().showAlertWithText(.AppMenu.AppMenuTabSentConfirmMessage, + SimpleToast().showAlertWithText(.LegacyAppMenu.AppMenuTabSentConfirmMessage, bottomContainer: self.alertContainer, theme: self.themeManager.getCurrentTheme(for: self.windowUUID)) } diff --git a/firefox-ios/Client/Experiments/Experiments.swift b/firefox-ios/Client/Experiments/Experiments.swift index 6f6dbaeaecbb..582df5953ed2 100644 --- a/firefox-ios/Client/Experiments/Experiments.swift +++ b/firefox-ios/Client/Experiments/Experiments.swift @@ -141,6 +141,29 @@ enum Experiments { defaults.set(false, forKey: NIMBUS_IS_FIRST_RUN_KEY) } + let errorReporter: NimbusErrorReporter = { err in + DefaultLogger.shared.log("Error in Nimbus SDK", + level: .warning, + category: .experiments, + description: err.localizedDescription) + } + + let initialExperiments = Bundle.main.url(forResource: "initial_experiments", withExtension: "json") + + guard let dbPath = Experiments.dbPath else { + DefaultLogger.shared.log("Nimbus didn't get to create, because of a nil dbPath", + level: .warning, + category: .experiments) + return NimbusDisabled.shared + } + + return buildNimbus(dbPath: dbPath, + errorReporter: errorReporter, + initialExperiments: initialExperiments, + isFirstRun: isFirstRun) + }() + + private static func getAppSettings(isFirstRun: Bool) -> NimbusAppSettings { let isPhone = UIDevice.current.userInterfaceIdiom == .phone let customTargetingAttributes: [String: Any] = [ @@ -153,28 +176,25 @@ enum Experiments { // App settings, to allow experiments to target the app name and the // channel. The values given here should match what `Experimenter` // thinks it is. - let appSettings = NimbusAppSettings( + return NimbusAppSettings( appName: nimbusAppName, channel: AppConstants.buildChannel.nimbusString, customTargetingAttributes: customTargetingAttributes ) + } - let errorReporter: NimbusErrorReporter = { err in - DefaultLogger.shared.log("Error in Nimbus SDK", - level: .warning, - category: .experiments, - description: err.localizedDescription) - } - - let initialExperiments = Bundle.main.url(forResource: "initial_experiments", withExtension: "json") - - guard let dbPath = Experiments.dbPath else { - DefaultLogger.shared.log("Nimbus didn't get to create, because of a nil dbPath", - level: .warning, - category: .experiments) - return NimbusDisabled.shared + private static func isReviewCheckerEnabled() -> Bool { + var isReviewCheckerEnabled = false + if let prefs = UserDefaults(suiteName: AppInfo.sharedContainerIdentifier) { + isReviewCheckerEnabled = prefs.bool(forKey: "profile." + PrefsKeys.Shopping2023OptIn) } + return isReviewCheckerEnabled + } + private static func buildNimbus(dbPath: String, + errorReporter: @escaping NimbusErrorReporter, + initialExperiments: URL?, + isFirstRun: Bool) -> NimbusInterface { let bundles = [ Bundle.main, Strings.bundle, @@ -190,15 +210,7 @@ enum Experiments { .with(bundles: bundles) .with(featureManifest: FxNimbus.shared) .with(commandLineArgs: CommandLine.arguments) - .build(appInfo: appSettings) - }() - - private static func isReviewCheckerEnabled() -> Bool { - var isReviewCheckerEnabled = false - if let prefs = UserDefaults(suiteName: AppInfo.sharedContainerIdentifier) { - isReviewCheckerEnabled = prefs.bool(forKey: "profile." + PrefsKeys.Shopping2023OptIn) - } - return isReviewCheckerEnabled + .build(appInfo: getAppSettings(isFirstRun: isFirstRun)) } /// A convenience method to initialize the `NimbusApi` object at startup. diff --git a/firefox-ios/Client/FeatureFlags/NimbusFlaggableFeature.swift b/firefox-ios/Client/FeatureFlags/NimbusFlaggableFeature.swift index 4fce434cb1eb..8903187e7af8 100644 --- a/firefox-ios/Client/FeatureFlags/NimbusFlaggableFeature.swift +++ b/firefox-ios/Client/FeatureFlags/NimbusFlaggableFeature.swift @@ -10,7 +10,6 @@ import UIKit /// Please add new features alphabetically. enum NimbusFeatureFlagID: String, CaseIterable { case accountSettingsRedux - case addressAutofill case addressAutofillEdit case bottomSearchBar case contextualHintForToolbar @@ -28,9 +27,12 @@ enum NimbusFeatureFlagID: String, CaseIterable { case loginAutofill case menuRefactor case microsurvey + case nativeErrorPage case nightMode + case passwordGenerator case preferSwitchToOpenTabOverDuplicate case reduxSearchSettings + case closeRemoteTabs case reportSiteIssue case searchHighlights case splashScreen @@ -42,7 +44,7 @@ enum NimbusFeatureFlagID: String, CaseIterable { var debugKey: String? { switch self { - case .microsurvey: + case .microsurvey, .closeRemoteTabs: return rawValue + PrefsKeys.FeatureFlags.DebugSuffixKey default: return nil @@ -78,9 +80,9 @@ struct NimbusFlaggableFeature: HasNimbusSearchBar { // Cases where users do not have the option to manipulate a setting. case .contextualHintForToolbar, .accountSettingsRedux, - .addressAutofill, .addressAutofillEdit, .creditCardAutofillStatus, + .closeRemoteTabs, .fakespotBackInStock, .fakespotFeature, .fakespotProductAds, @@ -88,7 +90,9 @@ struct NimbusFlaggableFeature: HasNimbusSearchBar { .loginAutofill, .microsurvey, .menuRefactor, + .nativeErrorPage, .nightMode, + .passwordGenerator, .preferSwitchToOpenTabOverDuplicate, .reduxSearchSettings, .reportSiteIssue, diff --git a/firefox-ios/Client/Frontend/Autofill/Address/AddressAutoFillBottomSheetView.swift b/firefox-ios/Client/Frontend/Autofill/Address/AddressAutoFillBottomSheetView.swift index d288d856a329..916cbe54e9a5 100644 --- a/firefox-ios/Client/Frontend/Autofill/Address/AddressAutoFillBottomSheetView.swift +++ b/firefox-ios/Client/Frontend/Autofill/Address/AddressAutoFillBottomSheetView.swift @@ -8,25 +8,44 @@ import Common import ComponentLibrary struct AddressAutoFillBottomSheetView: View { - // MARK: - Properties - let windowUUID: WindowUUID @Environment(\.themeManager) var themeManager - /// The observed object for managing the address list. @ObservedObject var addressListViewModel: AddressListViewModel + @State private var backgroundColor: Color = .clear // MARK: - Body var body: some View { VStack { - AutofillHeaderView(windowUUID: windowUUID, title: .Addresses.BottomSheet.UseASavedAddress) - Spacer() - AddressScrollView(windowUUID: windowUUID, viewModel: addressListViewModel) - Spacer() + AutofillHeaderView( + windowUUID: windowUUID, + title: .Addresses.BottomSheet.UseASavedAddress + ) + AddressScrollView( + windowUUID: windowUUID, + viewModel: addressListViewModel + ) + AutofillFooterView( + windowUUID: windowUUID, + title: .Addresses.BottomSheet.ManageAddressesButton, + primaryAction: { addressListViewModel.manageAddressesInfoAction?() } + ) } .padding() - .background(Color(themeManager.getCurrentTheme(for: windowUUID).colors.layer1)) + .background(backgroundColor) + .onAppear { + applyTheme(theme: themeManager.getCurrentTheme(for: windowUUID)) + } + .onReceive(NotificationCenter.default.publisher(for: .ThemeDidChange)) { notification in + guard let uuid = notification.windowUUID, uuid == windowUUID else { return } + applyTheme(theme: themeManager.getCurrentTheme(for: windowUUID)) + } + } + + func applyTheme(theme: Theme) { + let color = theme.colors + backgroundColor = Color(color.layer1) } } diff --git a/firefox-ios/Client/Frontend/Autofill/Address/AddressListViewModel.swift b/firefox-ios/Client/Frontend/Autofill/Address/AddressListViewModel.swift index 78233fab93bc..789439ece177 100644 --- a/firefox-ios/Client/Frontend/Autofill/Address/AddressListViewModel.swift +++ b/firefox-ios/Client/Frontend/Autofill/Address/AddressListViewModel.swift @@ -41,6 +41,7 @@ final class AddressListViewModel: ObservableObject, FeatureFlaggable { var saveAction: ((@escaping (UpdatableAddressFields) -> Void) -> Void)? var toggleEditModeAction: ((Bool) -> Void)? var presentToast: ((AddressModifiedStatus) -> Void)? + var manageAddressesInfoAction: (() -> Void)? let addressProvider: AddressProvider let themeManager: ThemeManager @@ -135,12 +136,12 @@ final class AddressListViewModel: ObservableObject, FeatureFlaggable { } func saveEditButtonTap() { - toggleEditMode() saveAction? { [weak self] updatedAddress in guard let self else { return } guard case .edit(let currentAddress) = self.destination else { return } self.updateLocal(id: currentAddress.guid, updatedAddress: updatedAddress) } + toggleEditMode() } private func updateLocal(id: String, updatedAddress: UpdatableAddressFields) { diff --git a/firefox-ios/Client/Frontend/Autofill/Address/AddressUtility/AddressLocaleFeatureValidator.swift b/firefox-ios/Client/Frontend/Autofill/Address/AddressUtility/AddressLocaleFeatureValidator.swift new file mode 100644 index 000000000000..851aca5ad913 --- /dev/null +++ b/firefox-ios/Client/Frontend/Autofill/Address/AddressUtility/AddressLocaleFeatureValidator.swift @@ -0,0 +1,16 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +struct AddressLocaleFeatureValidator { + static let supportedRegions = ["CA", "US"] + + static func isValidRegion(locale: Locale = Locale.current) -> Bool { + guard let regionCode = locale.regionCode else { + return false + } + return supportedRegions.contains(regionCode) + } +} diff --git a/firefox-ios/Client/Frontend/Autofill/Address/AddressProvider.swift b/firefox-ios/Client/Frontend/Autofill/Address/AddressUtility/AddressProvider.swift similarity index 100% rename from firefox-ios/Client/Frontend/Autofill/Address/AddressProvider.swift rename to firefox-ios/Client/Frontend/Autofill/Address/AddressUtility/AddressProvider.swift diff --git a/firefox-ios/Client/Frontend/Autofill/AutofillHeaderView.swift b/firefox-ios/Client/Frontend/Autofill/AutofillHeaderView.swift index 366e88858e80..b09bbbc3c082 100644 --- a/firefox-ios/Client/Frontend/Autofill/AutofillHeaderView.swift +++ b/firefox-ios/Client/Frontend/Autofill/AutofillHeaderView.swift @@ -39,6 +39,7 @@ struct AutofillHeaderView: View { .resizable() .aspectRatio(contentMode: .fit) .frame(width: UX.logoSize, height: UX.logoSize) + .accessibilityHidden(true) VStack(alignment: .leading) { Text(title) .font(.body) diff --git a/firefox-ios/Client/Frontend/Autofill/CreditCard/CreditCardBottomSheet/CreditCardBottomSheetViewController.swift b/firefox-ios/Client/Frontend/Autofill/CreditCard/CreditCardBottomSheet/CreditCardBottomSheetViewController.swift index a14c538afd69..416ff39e6ca5 100644 --- a/firefox-ios/Client/Frontend/Autofill/CreditCard/CreditCardBottomSheet/CreditCardBottomSheetViewController.swift +++ b/firefox-ios/Client/Frontend/Autofill/CreditCard/CreditCardBottomSheet/CreditCardBottomSheetViewController.swift @@ -301,6 +301,7 @@ class CreditCardBottomSheetViewController: UIViewController, hostingCell.selectionStyle = .none hostingCell.isAccessibilityElement = true hostingCell.accessibilityAttributedLabel = viewModel.a11yLabel(for: creditCard) + hostingCell.accessibilityIdentifier = "creditCardCell_\(indexPath.row)" return hostingCell } diff --git a/firefox-ios/Client/Frontend/Autofill/CreditCard/CreditCardBottomSheet/CreditCardBottomSheetViewModel.swift b/firefox-ios/Client/Frontend/Autofill/CreditCard/CreditCardBottomSheet/CreditCardBottomSheetViewModel.swift index aa5a56c616ed..f36fe7cdc93e 100644 --- a/firefox-ios/Client/Frontend/Autofill/CreditCard/CreditCardBottomSheet/CreditCardBottomSheetViewModel.swift +++ b/firefox-ios/Client/Frontend/Autofill/CreditCard/CreditCardBottomSheet/CreditCardBottomSheetViewModel.swift @@ -62,8 +62,7 @@ enum CreditCardBottomSheetState: String, Equatable, CaseIterable { class CreditCardBottomSheetViewModel { private var logger: Logger - let profile: Profile - let autofill: RustAutofill + let autofill: CreditCardProvider var creditCard: CreditCard? { didSet { didUpdateCreditCard?() @@ -81,13 +80,12 @@ class CreditCardBottomSheetViewModel { var storedCreditCards = [CreditCard]() var state: CreditCardBottomSheetState - init(profile: Profile, + init(creditCardProvider: CreditCardProvider, creditCard: CreditCard?, decryptedCreditCard: UnencryptedCreditCardFields?, logger: Logger = DefaultLogger.shared, state: CreditCardBottomSheetState) { - self.profile = profile - self.autofill = profile.autofill + self.autofill = creditCardProvider self.state = state self.logger = logger creditCards = [CreditCard]() diff --git a/firefox-ios/Client/Frontend/Autofill/CreditCard/CreditCardBottomSheet/CreditCardProvider.swift b/firefox-ios/Client/Frontend/Autofill/CreditCard/CreditCardBottomSheet/CreditCardProvider.swift new file mode 100644 index 000000000000..047e18b84d66 --- /dev/null +++ b/firefox-ios/Client/Frontend/Autofill/CreditCard/CreditCardBottomSheet/CreditCardProvider.swift @@ -0,0 +1,23 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Storage +import struct MozillaAppServices.CreditCard + +protocol CreditCardProvider { + func addCreditCard( + creditCard: UnencryptedCreditCardFields, + completion: @escaping (CreditCard?, Error?) -> Void + ) + func decryptCreditCardNumber(encryptedCCNum: String?) -> String? + func listCreditCards(completion: @escaping ([CreditCard]?, Error?) -> Void) + func updateCreditCard( + id: String, + creditCard: UnencryptedCreditCardFields, + completion: @escaping (Bool?, Error?) -> Void + ) +} + +extension RustAutofill: CreditCardProvider {} diff --git a/firefox-ios/Client/Frontend/Autofill/CreditCard/CreditCardSettingsView/CreditCardItemRow.swift b/firefox-ios/Client/Frontend/Autofill/CreditCard/CreditCardSettingsView/CreditCardItemRow.swift index 7a9144b616c5..f85d4ffcbf6a 100644 --- a/firefox-ios/Client/Frontend/Autofill/CreditCard/CreditCardSettingsView/CreditCardItemRow.swift +++ b/firefox-ios/Client/Frontend/Autofill/CreditCard/CreditCardSettingsView/CreditCardItemRow.swift @@ -88,6 +88,7 @@ struct CreditCardItemRow: View { didSelectAction?() } } + .accessibility(addTraits: .isButton) Rectangle() .fill(separatorColor) .frame(maxWidth: .infinity) @@ -108,14 +109,14 @@ struct CreditCardItemRow: View { } func getImage(creditCard: CreditCard) -> Image { - let defaultImage = Image(StandardImageIdentifiers.Large.creditCard) + let defaultImage = Image(decorative: StandardImageIdentifiers.Large.creditCard) guard let type = CreditCardType(rawValue: creditCard.ccType.uppercased()), - let image = type.image else { + let image = type.imageName else { return defaultImage } - return Image(uiImage: image) + return Image(decorative: image) } func applyTheme(theme: Theme) { diff --git a/firefox-ios/Client/Frontend/Autofill/CreditCard/CreditCardSettingsView/CreditCardTableViewController.swift b/firefox-ios/Client/Frontend/Autofill/CreditCard/CreditCardSettingsView/CreditCardTableViewController.swift index fac8e0358285..e2f0fa07bc5b 100644 --- a/firefox-ios/Client/Frontend/Autofill/CreditCard/CreditCardSettingsView/CreditCardTableViewController.swift +++ b/firefox-ios/Client/Frontend/Autofill/CreditCard/CreditCardSettingsView/CreditCardTableViewController.swift @@ -211,6 +211,7 @@ extension CreditCardTableViewController: UITableViewDelegate, hostingCell.contentView.backgroundColor = .clear hostingCell.selectionStyle = .none hostingCell.isAccessibilityElement = true + hostingCell.accessibilityIdentifier = "creditCardCell_\(indexPath.row)" return hostingCell } } diff --git a/firefox-ios/Client/Frontend/Autofill/CreditCard/Logic/CreditCardValidator.swift b/firefox-ios/Client/Frontend/Autofill/CreditCard/Logic/CreditCardValidator.swift index be39bb6f7475..a4d4fa06c49b 100644 --- a/firefox-ios/Client/Frontend/Autofill/CreditCard/Logic/CreditCardValidator.swift +++ b/firefox-ios/Client/Frontend/Autofill/CreditCard/Logic/CreditCardValidator.swift @@ -14,24 +14,24 @@ enum CreditCardType: String, Equatable, CaseIterable { case mir = "MIR" case unionpay = "UNIONPAY" - var image: UIImage? { + var imageName: String? { switch self { case .visa: - return UIImage(named: ImageIdentifiers.logoVisa) + return ImageIdentifiers.logoVisa case .mastercard: - return UIImage(named: ImageIdentifiers.logoMastercard) + return ImageIdentifiers.logoMastercard case .amex: - return UIImage(named: ImageIdentifiers.logoAmex) + return ImageIdentifiers.logoAmex case .diners: - return UIImage(named: ImageIdentifiers.logoDiners) + return ImageIdentifiers.logoDiners case .jcb: - return UIImage(named: ImageIdentifiers.logoJcb) + return ImageIdentifiers.logoJcb case .discover: - return UIImage(named: ImageIdentifiers.logoDiscover) + return ImageIdentifiers.logoDiscover case .mir: - return UIImage(named: ImageIdentifiers.logoMir) + return ImageIdentifiers.logoMir case .unionpay: - return UIImage(named: ImageIdentifiers.logoUnionpay) + return ImageIdentifiers.logoUnionpay } } diff --git a/firefox-ios/Client/Frontend/Autofill/CreditCard/ViewComponents/CreditCardInputField.swift b/firefox-ios/Client/Frontend/Autofill/CreditCard/ViewComponents/CreditCardInputField.swift index 4d8c69234ba1..0a473463c8e3 100644 --- a/firefox-ios/Client/Frontend/Autofill/CreditCard/ViewComponents/CreditCardInputField.swift +++ b/firefox-ios/Client/Frontend/Autofill/CreditCard/ViewComponents/CreditCardInputField.swift @@ -7,7 +7,7 @@ import Foundation import SwiftUI import Shared -enum CreditCardInputType { +enum CreditCardInputType: String { case name, number, expiration } @@ -204,7 +204,10 @@ struct CreditCardInputField: View { if inputType == .expiration { _ = self.updateInputValidity() } - }.accessibilityLabel(fieldHeadline) + } + .accessibilityLabel(fieldHeadline) + .accessibilityIdentifier(inputType.rawValue) + .accessibility(addTraits: .isButton) } // MARK: Helper diff --git a/firefox-ios/Client/Frontend/Autofill/LoginCellView.swift b/firefox-ios/Client/Frontend/Autofill/LoginCellView.swift index b66a9eb531bc..b8215b7c461b 100644 --- a/firefox-ios/Client/Frontend/Autofill/LoginCellView.swift +++ b/firefox-ios/Client/Frontend/Autofill/LoginCellView.swift @@ -47,6 +47,7 @@ struct LoginCellView: View { .padding(.leading, 16) .foregroundColor(iconPrimary) .alignmentGuide(.midAccountAndName) { $0[VerticalAlignment.center] } + .accessibilityHidden(true) VStack(alignment: .leading) { Text(login.decryptedUsername.isEmpty ? String.PasswordAutofill.LoginListCellNoUsername : login.decryptedUsername) diff --git a/firefox-ios/Client/Frontend/Browser/BrowserViewController/Actions/ActionProviderBuilder.swift b/firefox-ios/Client/Frontend/Browser/BrowserViewController/Actions/ActionProviderBuilder.swift index 0cb890bc3cac..bb1d44224a8e 100644 --- a/firefox-ios/Client/Frontend/Browser/BrowserViewController/Actions/ActionProviderBuilder.swift +++ b/firefox-ios/Client/Frontend/Browser/BrowserViewController/Actions/ActionProviderBuilder.swift @@ -106,7 +106,7 @@ class ActionProviderBuilder { contentContainer: ContentContainer) { actions.append(UIAction( title: .ContextMenuShareLink, - image: UIImage.templateImageNamed(StandardImageIdentifiers.Large.shareApple), + image: UIImage.templateImageNamed(StandardImageIdentifiers.Large.share), identifier: UIAction.Identifier("linkContextMenu.share") ) { _ in guard let tab = tabManager[webView], diff --git a/firefox-ios/Client/Frontend/Browser/BrowserViewController/Actions/GeneralBrowserAction.swift b/firefox-ios/Client/Frontend/Browser/BrowserViewController/Actions/GeneralBrowserAction.swift index 2b6d7b2d305d..42dc14173afd 100644 --- a/firefox-ios/Client/Frontend/Browser/BrowserViewController/Actions/GeneralBrowserAction.swift +++ b/firefox-ios/Client/Frontend/Browser/BrowserViewController/Actions/GeneralBrowserAction.swift @@ -48,7 +48,11 @@ enum GeneralBrowserActionType: ActionType { case stopLoadingWebsite case reloadWebsite case showShare + case showReaderMode case addNewTab + case showNewTabLongPressActions + case addToReadingListLongPressAction + case clearData } class GeneralBrowserMiddlewareAction: Action { diff --git a/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+FindInPage.swift b/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+FindInPage.swift index 7b0aff2dbcd4..d47e14bd0a89 100644 --- a/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+FindInPage.swift +++ b/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+FindInPage.swift @@ -13,6 +13,7 @@ extension BrowserViewController { } else { useCustomFindInteraction(isVisible: isVisible, tab: tab) } + tabManager.selectedTab?.isFindInPageMode = isVisible && isBottomSearchBar } @available(iOS 16, *) @@ -21,7 +22,7 @@ extension BrowserViewController { if isVisible { webView.isFindInteractionEnabled = true - webView.findInteraction?.searchText = searchText + webView.findInteraction?.searchText = searchText ?? "" webView.findInteraction?.presentFindNavigator(showingReplace: false) } else { webView.findInteraction?.dismissFindNavigator() diff --git a/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+ReaderMode.swift b/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+ReaderMode.swift index fe5c1c73e031..0675dff407bf 100644 --- a/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+ReaderMode.swift +++ b/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+ReaderMode.swift @@ -4,13 +4,13 @@ import WebEngine import Shared +import Common extension BrowserViewController: ReaderModeDelegate { func readerMode(_ readerMode: ReaderMode, didChangeReaderModeState state: ReaderModeState, forTab tab: Tab) { // Update reader mode state if is the selected tab. Otherwise it will update once is active - if !isToolbarRefactorEnabled, tabManager.selectedTab === tab, !isToolbarRefactorEnabled { - urlBar.updateReaderModeState(state) - } + guard tabManager.selectedTab === tab else { return } + updateReaderModeState(for: tab, readerModeState: state) } func readerMode(_ readerMode: ReaderMode, didDisplayReaderizedContentForTab tab: Tab) { @@ -91,7 +91,7 @@ extension BrowserViewController { } func hideReaderModeBar(animated: Bool) { - guard let readerModeBar = readerModeBar else { return } + guard let readerModeBar else { return } if isBottomSearchBar { overKeyboardContainer.removeArrangedView(readerModeBar) diff --git a/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+TabToolbarDelegate.swift b/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+TabToolbarDelegate.swift index fe650cca0e39..464ef4ff8547 100644 --- a/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+TabToolbarDelegate.swift +++ b/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+TabToolbarDelegate.swift @@ -15,17 +15,16 @@ extension BrowserViewController: TabToolbarDelegate, PhotonActionSheetProtocol { dataClearanceContextHintVC.stopTimer() } - func configureDataClearanceContextualHint() { - guard !isToolbarRefactorEnabled, - contentContainer.hasWebView, + func configureDataClearanceContextualHint(_ view: UIView) { + guard contentContainer.hasWebView, tabManager.selectedTab?.url?.displayURL?.isWebPage() == true else { resetDataClearanceCFRTimer() return } dataClearanceContextHintVC.configure( - anchor: navigationToolbar.multiStateButton, - withArrowDirection: topTabsVisible ? .up : .down, + anchor: view, + withArrowDirection: ToolbarHelper().shouldShowNavigationToolbar(for: traitCollection) ? .down : .up, andDelegate: self, presentedUsing: { [weak self] in self?.presentDataClearanceContextualHint() }, andActionForButton: { }, @@ -42,7 +41,11 @@ extension BrowserViewController: TabToolbarDelegate, PhotonActionSheetProtocol { } // Presents alert to clear users private session data - func tabToolbarDidPressFire(_ tabToolbar: TabToolbarProtocol, button: UIButton) { + func tabToolbarDidPressDataClearance(_ tabToolbar: TabToolbarProtocol, button: UIButton) { + didTapOnDataClearance() + } + + func didTapOnDataClearance() { let alert = UIAlertController( title: .Alerts.FeltDeletion.Title, message: .Alerts.FeltDeletion.Body, @@ -114,7 +117,9 @@ extension BrowserViewController: TabToolbarDelegate, PhotonActionSheetProtocol { } func dismissUrlBar() { - if !isToolbarRefactorEnabled, urlBar.inOverlayMode { + if isToolbarRefactorEnabled, addressToolbarContainer.inOverlayMode { + addressToolbarContainer.leaveOverlayMode(reason: .finished, shouldCancelLoading: false) + } else if !isToolbarRefactorEnabled, urlBar.inOverlayMode { urlBar.leaveOverlayMode(reason: .finished, shouldCancelLoading: false) } } @@ -198,48 +203,80 @@ extension BrowserViewController: TabToolbarDelegate, PhotonActionSheetProtocol { if let tab = self.tabManager.selectedTab { return tab.isPrivate ? [normalBrowsingMode] : [privateBrowsingMode] } + return [privateBrowsingMode] } func getMoreTabToolbarLongPressActions() -> [PhotonRowActions] { - let newTab = SingleActionViewModel(title: .KeyboardShortcuts.NewTab, - iconString: StandardImageIdentifiers.Large.plus, - iconType: .Image) { _ in + let newTab = getNewTabAction() + let newPrivateTab = getNewPrivateTabAction() + let closeTab = getCloseTabAction() + + if let tab = self.tabManager.selectedTab { + return tab.isPrivate ? [newPrivateTab, closeTab] : [newTab, closeTab] + } + + return [newTab, closeTab] + } + + func getTabToolbarRefactorLongPressActions() -> [[PhotonRowActions]] { + let newTab = getNewTabAction() + let newPrivateTab = getNewPrivateTabAction() + let closeTab = getCloseTabAction() + + return [[newTab, newPrivateTab], [closeTab]] + } + + func getNewTabLongPressActions() -> [[PhotonRowActions]] { + let newTab = getNewTabAction() + let newPrivateTab = getNewPrivateTabAction() + + return [[newTab, newPrivateTab]] + } + + private func getNewTabAction() -> PhotonRowActions { + return SingleActionViewModel(title: .KeyboardShortcuts.NewTab, + iconString: StandardImageIdentifiers.Large.plus, + iconType: .Image) { _ in let shouldFocusLocationField = self.newTabSettings == .blankPage self.overlayManager.openNewTab(url: nil, newTabSettings: self.newTabSettings) self.openBlankNewTab(focusLocationField: shouldFocusLocationField, isPrivate: false) }.items + } - let newPrivateTab = SingleActionViewModel(title: .KeyboardShortcuts.NewPrivateTab, - iconString: StandardImageIdentifiers.Large.plus, - iconType: .Image) { _ in + private func getNewPrivateTabAction() -> PhotonRowActions { + let isRefactorEnabled = isToolbarRefactorEnabled && isOneTapNewTabEnabled + let iconString = isRefactorEnabled ? StandardImageIdentifiers.Large.privateMode : StandardImageIdentifiers.Large.plus + return SingleActionViewModel(title: .KeyboardShortcuts.NewPrivateTab, + iconString: iconString, + iconType: .Image) { _ in let shouldFocusLocationField = self.newTabSettings == .blankPage self.overlayManager.openNewTab(url: nil, newTabSettings: self.newTabSettings) self.openBlankNewTab(focusLocationField: shouldFocusLocationField, isPrivate: true) TelemetryWrapper.recordEvent(category: .action, method: .tap, object: .newPrivateTab, value: .tabTray) }.items + } - let closeTab = SingleActionViewModel(title: .KeyboardShortcuts.CloseCurrentTab, - iconString: StandardImageIdentifiers.Large.cross, - iconType: .Image) { _ in + private func getCloseTabAction() -> PhotonRowActions { + let isRefactorEnabled = isToolbarRefactorEnabled && isOneTapNewTabEnabled + let title = isRefactorEnabled ? String.Toolbars.TabToolbarLongPressActionsMenu.CloseThisTabButton : + String.KeyboardShortcuts.CloseCurrentTab + return SingleActionViewModel(title: title, + iconString: StandardImageIdentifiers.Large.cross, + iconType: .Image) { _ in if let tab = self.tabManager.selectedTab { self.tabManager.removeTab(tab) self.updateTabCountUsingTabManager(self.tabManager) self.showToast(message: .TabsTray.CloseTabsToast.SingleTabTitle, toastAction: .closeTab) } }.items - - if let tab = self.tabManager.selectedTab { - return tab.isPrivate ? [newPrivateTab, closeTab] : [newTab, closeTab] - } - return [newTab, closeTab] } func tabToolbarDidLongPressTabs(_ tabToolbar: TabToolbarProtocol, button: UIButton) { let generator = UIImpactFeedbackGenerator(style: .heavy) generator.impactOccurred() - presentActionSheet(from: button) + presentTabsLongPressAction(from: button) } func tabToolbarDidPressSearch(_ tabToolbar: TabToolbarProtocol, button: UIButton) { @@ -329,7 +366,7 @@ extension BrowserViewController: ToolBarActionMenuDelegate, UIDocumentPickerDele func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { if !urls.isEmpty { - showToast(message: .AppMenu.AppMenuDownloadPDFConfirmMessage, toastAction: .downloadPDF) + showToast(message: .LegacyAppMenu.AppMenuDownloadPDFConfirmMessage, toastAction: .downloadPDF) } } diff --git a/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+URLBarDelegate.swift b/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+URLBarDelegate.swift index ec06b200ce55..ca06c568a6b4 100644 --- a/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+URLBarDelegate.swift +++ b/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+URLBarDelegate.swift @@ -181,55 +181,11 @@ extension BrowserViewController: URLBarDelegate { } func urlBarDidPressReaderMode(_ urlBar: URLBarView) { - guard let tab = tabManager.selectedTab, - let readerMode = tab.getContentScript(name: "ReaderMode") as? ReaderMode - else { return } - - switch readerMode.state { - case .available: - enableReaderMode() - TelemetryWrapper.recordEvent(category: .action, method: .tap, object: .readerModeOpenButton) - case .active: - disableReaderMode() - TelemetryWrapper.recordEvent(category: .action, method: .tap, object: .readerModeCloseButton) - case .unavailable: - break - } + toggleReaderMode() } func urlBarDidLongPressReaderMode(_ urlBar: URLBarView) -> Bool { - guard let tab = tabManager.selectedTab, - let url = tab.url?.displayURL - else { - UIAccessibility.post( - notification: UIAccessibility.Notification.announcement, - argument: String.ReaderModeAddPageGeneralErrorAccessibilityLabel - ) - return false - } - - let result = profile.readingList.createRecordWithURL( - url.absoluteString, - title: tab.title ?? "", - addedBy: UIDevice.current.name - ) - - switch result.value { - case .success: - UIAccessibility.post( - notification: UIAccessibility.Notification.announcement, - argument: String.ReaderModeAddPageSuccessAcessibilityLabel - ) - SimpleToast().showAlertWithText(.ShareAddToReadingListDone, - bottomContainer: contentContainer, - theme: currentTheme()) - case .failure: - UIAccessibility.post( - notification: UIAccessibility.Notification.announcement, - argument: String.ReaderModeAddPageMaybeExistsErrorAccessibilityLabel - ) - } - return true + toggleReaderModeLongPressAction() } func urlBarDidLongPressReload(_ urlBar: URLBarView, from button: UIButton) { @@ -287,20 +243,11 @@ extension BrowserViewController: URLBarDelegate { } func urlBar(_ urlBar: URLBarView, didEnterText text: String) { - if text.isEmpty { - hideSearchController() - } else { - configureOverlayView() - } + searchSuggestions(searchTerm: text) urlBar.locationTextField?.applyUIMode( isPrivate: tabManager.selectedTab?.isPrivate ?? false, theme: self.currentTheme() ) - searchController?.viewModel.searchQuery = text - searchController?.searchTelemetry?.searchQuery = text - searchController?.searchTelemetry?.clearVisibleResults() - searchLoader?.query = text - searchController?.searchTelemetry?.determineInteractionType() } func urlBar(_ urlBar: URLBarView, didSubmitText text: String) { @@ -335,38 +282,11 @@ extension BrowserViewController: URLBarDelegate { func urlBarDidEnterOverlayMode(_ urlBar: URLBarView) { urlBar.searchEnginesDidUpdate() - guard let profile = profile as? BrowserProfile else { return } - - if .blankPage == NewTabAccessors.getNewTabPage(profile.prefs) { - UIAccessibility.post( - notification: UIAccessibility.Notification.screenChanged, - argument: UIAccessibility.Notification.screenChanged - ) - } else { - if let toast = clipboardBarDisplayHandler?.clipboardToast { - toast.removeFromSuperview() - } - - showEmbeddedHomepage(inline: false, isPrivate: tabManager.selectedTab?.isPrivate ?? false) - } - - urlBar.applyTheme(theme: currentTheme()) + addressToolbarDidEnterOverlayMode(urlBar) } func urlBar(_ urlBar: URLBarView, didLeaveOverlayModeForReason reason: URLBarLeaveOverlayModeReason) { - if searchSessionState == .active { - // This delegate method may be called even if the user isn't - // currently searching, but we only want to update the search - // session state if they are. - searchSessionState = switch reason { - case .finished: .engaged - case .cancelled: .abandoned - } - } - destroySearchController() - updateInContentHomePanel(tabManager.selectedTab?.url as URL?) - - urlBar.applyTheme(theme: currentTheme()) + addressToolbar(urlBar, didLeaveOverlayModeForReason: reason) } func urlBarDidBeginDragInteraction(_ urlBar: URLBarView) { diff --git a/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+WebViewDelegates.swift b/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+WebViewDelegates.swift index 50ab02b2d1c5..8d6d3f7b770a 100644 --- a/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+WebViewDelegates.swift +++ b/firefox-ios/Client/Frontend/Browser/BrowserViewController/Extensions/BrowserViewController+WebViewDelegates.swift @@ -377,10 +377,9 @@ extension BrowserViewController: WKNavigationDelegate { // are going to a about:reader page. Then we keep it on screen: it will change status // (orange color) as soon as the page has loaded. if let url = webView.url { - if !url.isReaderModeURL, !isToolbarRefactorEnabled { - urlBar.updateReaderModeState(ReaderModeState.unavailable) - hideReaderModeBar(animated: false) - } + guard !url.isReaderModeURL else { return } + updateReaderModeState(for: tabManager.selectedTab, readerModeState: .unavailable) + hideReaderModeBar(animated: false) } } diff --git a/firefox-ios/Client/Frontend/Browser/BrowserViewController/State/BrowserViewControllerState.swift b/firefox-ios/Client/Frontend/Browser/BrowserViewController/State/BrowserViewControllerState.swift index fd9127a98da0..e850a2ff00b8 100644 --- a/firefox-ios/Client/Frontend/Browser/BrowserViewController/State/BrowserViewControllerState.swift +++ b/firefox-ios/Client/Frontend/Browser/BrowserViewController/State/BrowserViewControllerState.swift @@ -27,6 +27,10 @@ struct BrowserViewControllerState: ScreenState, Equatable { case reloadLongPressAction case tabTray case share + case readerMode + case newTabLongPressActions + case readerModeLongPressAction + case dataClearance } let windowUUID: WindowUUID @@ -363,6 +367,45 @@ struct BrowserViewControllerState: ScreenState, Equatable { displayView: .share, buttonTapped: action.buttonTapped, microsurveyState: MicrosurveyPromptState.reducer(state.microsurveyState, action)) + case GeneralBrowserActionType.showReaderMode: + return BrowserViewControllerState( + searchScreenState: state.searchScreenState, + showDataClearanceFlow: state.showDataClearanceFlow, + fakespotState: state.fakespotState, + toast: state.toast, + windowUUID: state.windowUUID, + browserViewType: state.browserViewType, + displayView: .readerMode, + microsurveyState: MicrosurveyPromptState.reducer(state.microsurveyState, action)) + case GeneralBrowserActionType.showNewTabLongPressActions: + return BrowserViewControllerState( + searchScreenState: state.searchScreenState, + showDataClearanceFlow: state.showDataClearanceFlow, + fakespotState: state.fakespotState, + toast: state.toast, + windowUUID: state.windowUUID, + browserViewType: state.browserViewType, + displayView: .newTabLongPressActions, + microsurveyState: MicrosurveyPromptState.reducer(state.microsurveyState, action)) + case GeneralBrowserActionType.addToReadingListLongPressAction: + return BrowserViewControllerState( + searchScreenState: state.searchScreenState, + showDataClearanceFlow: state.showDataClearanceFlow, + fakespotState: state.fakespotState, + toast: state.toast, + windowUUID: state.windowUUID, + browserViewType: state.browserViewType, + displayView: .readerModeLongPressAction, + microsurveyState: MicrosurveyPromptState.reducer(state.microsurveyState, action)) + case GeneralBrowserActionType.clearData: + return BrowserViewControllerState( + searchScreenState: state.searchScreenState, + showDataClearanceFlow: state.showDataClearanceFlow, + fakespotState: state.fakespotState, + windowUUID: state.windowUUID, + browserViewType: state.browserViewType, + displayView: .dataClearance, + microsurveyState: MicrosurveyPromptState.reducer(state.microsurveyState, action)) default: return state } diff --git a/firefox-ios/Client/Frontend/Browser/BrowserViewController/Views/BrowserViewController.swift b/firefox-ios/Client/Frontend/Browser/BrowserViewController/Views/BrowserViewController.swift index 82606206faa6..97a48cf6d585 100644 --- a/firefox-ios/Client/Frontend/Browser/BrowserViewController/Views/BrowserViewController.swift +++ b/firefox-ios/Client/Frontend/Browser/BrowserViewController/Views/BrowserViewController.swift @@ -29,6 +29,7 @@ class BrowserViewController: UIViewController, QRCodeViewControllerDelegate, StoreSubscriber, BrowserFrameInfoProvider, + NavigationToolbarContainerDelegate, AddressToolbarContainerDelegate, FeatureFlaggable { private enum UX { @@ -122,6 +123,9 @@ class BrowserViewController: UIViewController, var isToolbarRefactorEnabled: Bool { return featureFlags.isFeatureEnabled(.toolbarRefactor, checking: .buildOnly) } + var isOneTapNewTabEnabled: Bool { + return featureFlags.isFeatureEnabled(.toolbarOneTapNewTab, checking: .buildOnly) + } private var browserViewControllerState: BrowserViewControllerState? // Header stack view can contain the top url bar, top reader mode, top ZoomPageBar @@ -365,8 +369,11 @@ class BrowserViewController: UIViewController, if isToolbarRefactorEnabled { let badgeImageName = showWarningBadge ? StandardImageIdentifiers.Large.warningFill : nil + let maskImageName = showWarningBadge ? ImageIdentifiers.menuWarningMask : nil + let action = ToolbarMiddlewareAction( badgeImageName: badgeImageName, + maskImageName: maskImageName, windowUUID: windowUUID, actionType: ToolbarMiddlewareActionType.showMenuWarningBadge ) @@ -529,8 +536,10 @@ class BrowserViewController: UIViewController, self.view.sendSubviewToBack(self.webViewContainerBackdrop) }) - // Re-show toolbar which might have been hidden during scrolling (prior to app moving into the background) - scrollController.showToolbars(animated: false) + if let tab = tabManager.selectedTab, !tab.isFindInPageMode { + // Re-show toolbar which might have been hidden during scrolling (prior to app moving into the background) + scrollController.showToolbars(animated: false) + } browserDidBecomeActive() } @@ -634,20 +643,7 @@ class BrowserViewController: UIViewController, executeToolbarActions() - // Microsurveys - if !state.microsurveyState.showPrompt { - guard microsurvey != nil else { return } - removeMicrosurveyPrompt() - } else if state.microsurveyState.showSurvey { - guard let model = state.microsurveyState.model else { - logger.log("Microsurvey model should not be nil", level: .warning, category: .redux) - return - } - navigationHandler?.showMicrosurvey(model: model) - } else if state.microsurveyState.showPrompt { - guard microsurvey == nil else { return } - createMicrosurveyPrompt(with: state.microsurveyState) - } + handleMicrosurvey(state: state) } } @@ -667,6 +663,22 @@ class BrowserViewController: UIViewController, show(toast: toast) } + private func handleMicrosurvey(state: BrowserViewControllerState) { + if !state.microsurveyState.showPrompt { + guard microsurvey != nil else { return } + removeMicrosurveyPrompt() + } else if state.microsurveyState.showSurvey { + guard let model = state.microsurveyState.model else { + logger.log("Microsurvey model should not be nil", level: .warning, category: .redux) + return + } + navigationHandler?.showMicrosurvey(model: model) + } else if state.microsurveyState.showPrompt { + guard microsurvey == nil else { return } + createMicrosurveyPrompt(with: state.microsurveyState) + } + } + // MARK: - Lifecycle override func viewDidLoad() { @@ -681,6 +693,8 @@ class BrowserViewController: UIViewController, clipboardBarDisplayHandler = ClipboardBarDisplayHandler(prefs: profile.prefs, tabManager: tabManager) clipboardBarDisplayHandler?.delegate = self + navigationToolbarContainer.toolbarDelegate = self + scrollController.header = header scrollController.overKeyboardContainer = overKeyboardContainer scrollController.bottomContainer = bottomContainer @@ -699,9 +713,7 @@ class BrowserViewController: UIViewController, // Awesomebar Location Telemetry SearchBarSettingsViewModel.recordLocationTelemetry(for: isBottomSearchBar ? .bottom : .top) - if !isToolbarRefactorEnabled { - overlayManager.setURLBar(urlBarView: urlBar) - } + overlayManager.setURLBar(urlBarView: isToolbarRefactorEnabled ? addressToolbarContainer : urlBar) // Update theme of already existing views let theme = currentTheme() @@ -745,7 +757,11 @@ class BrowserViewController: UIViewController, pasteAction = AccessibleAction(name: .PasteTitle, handler: { [weak self] () -> Bool in guard let self, let pasteboardContents = UIPasteboard.general.string else { return false } // Enter overlay mode and make the search controller appear. - overlayManager.openSearch(with: pasteboardContents) + if isToolbarRefactorEnabled { + addressToolbarContainer.enterOverlayMode(pasteboardContents, pasted: true, search: true) + } else { + overlayManager.openSearch(with: pasteboardContents) + } searchController?.searchTelemetry?.interactionType = .pasted return true }) @@ -1024,6 +1040,13 @@ class BrowserViewController: UIViewController, themeManager.applyThemeUpdatesToWindows() } setupMiddleButtonStatus(isLoading: false) + + // Everything works fine on iPad orientation switch (because CFR remains anchored to the same button), + // so only necessary to dismiss when vertical size class changes + if dataClearanceContextHintVC.isPresenting && + previousTraitCollection?.verticalSizeClass != traitCollection.verticalSizeClass { + dataClearanceContextHintVC.dismiss(animated: true) + } } // MARK: - Constraints @@ -1107,7 +1130,11 @@ class BrowserViewController: UIViewController, adjustBottomContentStackView(remake) } - adjustBottomSearchBarForKeyboard() + if let tab = tabManager.selectedTab, tab.isFindInPageMode { + scrollController.hideToolbars(animated: true, isFindInPageMode: true) + } else { + adjustBottomSearchBarForKeyboard() + } } private func adjustBottomContentStackView(_ remake: ConstraintMaker) { @@ -1359,6 +1386,13 @@ class BrowserViewController: UIViewController, updateBarBordersForMicrosurvey() updateViewConstraints() } + + // MARK: - Native Error Page + + private func setupNativeErrorPage() { + guard featureFlags.isFeatureEnabled(.nativeErrorPage, checking: .buildOnly) else { return } + } + // MARK: - Update content func updateContentInHomePanel(_ browserViewType: BrowserViewType) { @@ -1430,11 +1464,11 @@ class BrowserViewController: UIViewController, searchViewModel.searchEngines = profile.searchEngines searchController.searchDelegate = self - if !isToolbarRefactorEnabled { - let searchLoader = SearchLoader(profile: profile, urlBar: urlBar) - searchLoader.addListener(searchViewModel) - self.searchLoader = searchLoader - } + let searchLoader = SearchLoader( + profile: profile, + autocompleteView: isToolbarRefactorEnabled ? addressToolbarContainer : urlBar) + searchLoader.addListener(searchViewModel) + self.searchLoader = searchLoader self.searchController = searchController self.searchSessionState = .active @@ -1536,9 +1570,14 @@ class BrowserViewController: UIViewController, private func showBookmarkToast(bookmarkURL: URL? = nil, title: String? = nil, action: BookmarkAction) { switch action { case .add: - self.showToast(message: .AppMenu.AddBookmarkConfirmMessage, toastAction: .bookmarkPage) + self.showToast(message: .LegacyAppMenu.AddBookmarkConfirmMessage, toastAction: .bookmarkPage) case .remove: - self.showToast(bookmarkURL, title, message: .AppMenu.RemoveBookmarkConfirmMessage, toastAction: .removeBookmark) + self.showToast( + bookmarkURL, + title, + message: .LegacyAppMenu.RemoveBookmarkConfirmMessage, + toastAction: .removeBookmark + ) } } @@ -1641,8 +1680,8 @@ class BrowserViewController: UIViewController, guard !showFireButton else { if !isToolbarRefactorEnabled { navigationToolbar.updateMiddleButtonState(.fire) + configureDataClearanceContextualHint(navigationToolbar.multiStateButton) } - configureDataClearanceContextualHint() return } resetDataClearanceCFRTimer() @@ -1698,6 +1737,7 @@ class BrowserViewController: UIViewController, if isToolbarRefactorEnabled { let action = ToolbarMiddlewareAction( isLoading: loading, + url: tab.url?.displayURL, windowUUID: windowUUID, actionType: ToolbarMiddlewareActionType.websiteLoadingStateDidChange ) @@ -1783,19 +1823,38 @@ class BrowserViewController: UIViewController, } } + func lockIconImageName(for tab: Tab?) -> String? { + guard let tab, let hasSecureContent = tab.webView?.hasOnlySecureContent else { return nil } + + let lockIconImageName = hasSecureContent ? + StandardImageIdentifiers.Large.lockFill : + StandardImageIdentifiers.Large.lockSlashFill + + return tab.url?.isReaderModeURL == false ? lockIconImageName : nil + } + + func updateReaderModeState(for tab: Tab?, readerModeState: ReaderModeState) { + if isToolbarRefactorEnabled { + let action = ToolbarMiddlewareAction( + lockIconImageName: lockIconImageName(for: tab), + url: tab?.url?.displayURL, + readerModeState: readerModeState, + windowUUID: windowUUID, + actionType: ToolbarMiddlewareActionType.readerModeStateChanged + ) + store.dispatch(action) + } else { + urlBar.updateReaderModeState(readerModeState) + } + } + /// Updates the URL bar text and button states. /// Call this whenever the page URL changes. fileprivate func updateURLBarDisplayURL(_ tab: Tab) { guard !isToolbarRefactorEnabled else { - guard let hasSecureContent = tab.webView?.hasOnlySecureContent else { return } - - let lockIconImageName = hasSecureContent ? - StandardImageIdentifiers.Large.lockFill : - StandardImageIdentifiers.Large.lockSlashFill - let action = ToolbarMiddlewareAction( isShowingNavigationToolbar: ToolbarHelper().shouldShowNavigationToolbar(for: traitCollection), - lockIconImageName: lockIconImageName, + lockIconImageName: lockIconImageName(for: tab), url: tab.url?.displayURL, canGoBack: tab.canGoBack, canGoForward: tab.canGoForward, @@ -1804,6 +1863,7 @@ class BrowserViewController: UIViewController, store.dispatch(action) return } + if tab == tabManager.selectedTab, let displayUrl = tab.url?.displayURL, urlBar.currentURL != displayUrl { let searchData = tab.metadataManager?.tabGroupData ?? LegacyTabGroupData() searchData.tabAssociatedNextUrl = displayUrl.absoluteString @@ -1812,13 +1872,11 @@ class BrowserViewController: UIViewController, searchData: searchData, isPrivate: tab.isPrivate) } + urlBar.currentURL = tab.url?.displayURL urlBar.locationView.updateShoppingButtonVisibility(for: tab) let isPage = tab.url?.displayURL?.isWebPage() ?? false - - if !isToolbarRefactorEnabled { - navigationToolbar.updatePageStatus(isPage) - } + navigationToolbar.updatePageStatus(isPage) } func didSubmitSearchText(_ text: String) { @@ -1893,7 +1951,7 @@ class BrowserViewController: UIViewController, case .backForwardList: navigationHandler?.showBackForwardList() case .tabsLongPressActions: - presentActionSheet(from: view) + presentTabsLongPressAction(from: view) case .locationViewLongPressAction: presentLocationViewActionSheet(from: addressToolbarContainer) case .trackingProtectionDetails: @@ -1915,6 +1973,14 @@ class BrowserViewController: UIViewController, case .share: guard let button = state.buttonTapped else { return } didTapOnShare(from: button) + case .readerMode: + toggleReaderMode() + case .readerModeLongPressAction: + _ = toggleReaderModeLongPressAction() + case .newTabLongPressActions: + presentNewTabLongPressActionSheet(from: view) + case .dataClearance: + didTapOnDataClearance() } } @@ -1980,6 +2046,19 @@ class BrowserViewController: UIViewController, presentSheetWith(viewModel: viewModel, on: self, from: button) } + func presentNewTabLongPressActionSheet(from view: UIView) { + let actions = getNewTabLongPressActions() + + let shouldPresentAsPopover = ToolbarHelper().shouldShowTopTabs(for: traitCollection) + let style: UIModalPresentationStyle = shouldPresentAsPopover ? .popover : .overCurrentContext + let viewModel = PhotonActionSheetViewModel( + actions: actions, + closeButtonTitle: .CloseButtonTitle, + modalStyle: style + ) + presentSheetWith(viewModel: viewModel, on: self, from: view) + } + func didTapOnHome() { let shouldUpdateWithRedux = isToolbarRefactorEnabled && browserViewControllerState?.navigateTo == .home guard shouldUpdateWithRedux || !isToolbarRefactorEnabled else { return } @@ -2017,6 +2096,60 @@ class BrowserViewController: UIViewController, } } + func toggleReaderMode() { + guard let tab = tabManager.selectedTab, + let readerMode = tab.getContentScript(name: "ReaderMode") as? ReaderMode + else { return } + + switch readerMode.state { + case .available: + enableReaderMode() + TelemetryWrapper.recordEvent(category: .action, method: .tap, object: .readerModeOpenButton) + case .active: + disableReaderMode() + TelemetryWrapper.recordEvent(category: .action, method: .tap, object: .readerModeCloseButton) + case .unavailable: + break + } + } + + func toggleReaderModeLongPressAction() -> Bool { + guard let tab = tabManager.selectedTab, + let url = tab.url?.displayURL + else { + UIAccessibility.post( + notification: UIAccessibility.Notification.announcement, + argument: String.ReaderModeAddPageGeneralErrorAccessibilityLabel + ) + + return false + } + + let result = profile.readingList.createRecordWithURL( + url.absoluteString, + title: tab.title ?? "", + addedBy: UIDevice.current.name + ) + + switch result.value { + case .success: + UIAccessibility.post( + notification: UIAccessibility.Notification.announcement, + argument: String.ReaderModeAddPageSuccessAcessibilityLabel + ) + SimpleToast().showAlertWithText(.ShareAddToReadingListDone, + bottomContainer: contentContainer, + theme: currentTheme()) + case .failure: + UIAccessibility.post( + notification: UIAccessibility.Notification.announcement, + argument: String.ReaderModeAddPageMaybeExistsErrorAccessibilityLabel + ) + } + + return true + } + private func showPhotonMainMenu(from button: UIButton?) { guard let button else { return } @@ -2072,12 +2205,18 @@ class BrowserViewController: UIViewController, } } - func presentActionSheet(from view: UIView) { + func presentTabsLongPressAction(from view: UIView) { guard presentedViewController == nil else { return } var actions: [[PhotonRowActions]] = [] - actions.append(getTabToolbarLongPressActionsForModeSwitching()) - actions.append(getMoreTabToolbarLongPressActions()) + let useToolbarRefactorLongPressActions = featureFlags.isFeatureEnabled(.toolbarRefactor, checking: .buildOnly) && + featureFlags.isFeatureEnabled(.toolbarOneTapNewTab, checking: .buildOnly) + if useToolbarRefactorLongPressActions { + actions = getTabToolbarRefactorLongPressActions() + } else { + actions.append(getTabToolbarLongPressActionsForModeSwitching()) + actions.append(getMoreTabToolbarLongPressActions()) + } let viewModel = PhotonActionSheetViewModel( actions: actions, @@ -2510,10 +2649,6 @@ class BrowserViewController: UIViewController, return (userDefaults.object(forKey: keyCreditCardAutofill) as? Bool ?? true) } - private func addressAutofillNimbusFeatureFlag() -> Bool { - return featureFlags.isFeatureEnabled(.addressAutofill, checking: .buildOnly) - } - private func addressAutofillSettingsUserDefaultsIsEnabled() -> Bool { let userDefaults = UserDefaults.standard let keyAddressAutofill = PrefsKeys.KeyAutofillAddressStatus @@ -2533,7 +2668,7 @@ class BrowserViewController: UIViewController, switch fieldValues.fieldValue { case .address: guard addressAutofillSettingsUserDefaultsIsEnabled(), - addressAutofillNimbusFeatureFlag(), + AddressLocaleFeatureValidator.isValidRegion(), // FXMO-376: Phase 2 let addressPayload = fieldValues.fieldData as? UnencryptedAddressFields, let type = type else { return } @@ -2741,8 +2876,8 @@ class BrowserViewController: UIViewController, $0.applyTheme(theme: currentTheme) } - let isPrivate = (currentTheme.type == .privateMode) if !isToolbarRefactorEnabled { + let isPrivate = (currentTheme.type == .privateMode) urlBar.applyUIMode(isPrivate: isPrivate, theme: currentTheme) } @@ -2801,9 +2936,10 @@ class BrowserViewController: UIViewController, afterTab: self.tabManager.selectedTab, isPrivate: isPrivate ) + let toolbar: URLBarViewProtocol = isToolbarRefactorEnabled ? addressToolbarContainer : urlBar // If we are showing toptabs a user can just use the top tab bar // If in overlay mode switching doesnt correctly dismiss the homepanels - guard !isToolbarRefactorEnabled, !topTabsVisible, !self.urlBar.inOverlayMode else { return } + guard !topTabsVisible, !toolbar.inOverlayMode else { return } // We're not showing the top tabs; show a toast to quick switch to the fresh new tab. let viewModel = ButtonToastViewModel(labelText: .ContextMenuButtonToastNewTabOpenedLabelText, buttonText: .ContextMenuButtonToastNewTabOpenedButtonText) @@ -2879,17 +3015,70 @@ class BrowserViewController: UIViewController, } // MARK: - AddressToolbarContainerDelegate - func searchSuggestions(searchTerm: String) {} + func searchSuggestions(searchTerm: String) { + openSuggestions(searchTerm: searchTerm) + searchLoader?.query = searchTerm + } func openBrowser(searchTerm: String) { didSubmitSearchText(searchTerm) } - func openSuggestions(searchTerm: String) {} + func openSuggestions(searchTerm: String) { + if searchTerm.isEmpty { + hideSearchController() + } else { + configureOverlayView() + } + searchController?.viewModel.searchQuery = searchTerm + searchController?.searchTelemetry?.searchQuery = searchTerm + searchController?.searchTelemetry?.clearVisibleResults() + searchController?.searchTelemetry?.determineInteractionType() + } + + // Also implements NavigationToolbarContainerDelegate::configureContextualHint(for button: UIButton) + func configureContextualHint(for button: UIButton) { + configureDataClearanceContextualHint(button) + } func addressToolbarContainerAccessibilityActions() -> [UIAccessibilityCustomAction]? { locationActionsForURLBar().map { $0.accessibilityCustomAction } } + + func addressToolbarDidEnterOverlayMode(_ view: UIView) { + guard let profile = profile as? BrowserProfile else { return } + + if .blankPage == NewTabAccessors.getNewTabPage(profile.prefs) { + UIAccessibility.post( + notification: UIAccessibility.Notification.screenChanged, + argument: UIAccessibility.Notification.screenChanged + ) + } else { + if let toast = clipboardBarDisplayHandler?.clipboardToast { + toast.removeFromSuperview() + } + + showEmbeddedHomepage(inline: false, isPrivate: tabManager.selectedTab?.isPrivate ?? false) + } + + (view as? ThemeApplicable)?.applyTheme(theme: currentTheme()) + } + + func addressToolbar(_ view: UIView, didLeaveOverlayModeForReason reason: URLBarLeaveOverlayModeReason) { + if searchSessionState == .active { + // This delegate method may be called even if the user isn't + // currently searching, but we only want to update the search + // session state if they are. + searchSessionState = switch reason { + case .finished: .engaged + case .cancelled: .abandoned + } + } + destroySearchController() + updateInContentHomePanel(tabManager.selectedTab?.url as URL?) + + (view as? ThemeApplicable)?.applyTheme(theme: currentTheme()) + } } extension BrowserViewController: ClipboardBarDisplayHandlerDelegate { @@ -3297,13 +3486,11 @@ extension BrowserViewController: SearchViewControllerDelegate { case .engaged: let visibleSuggestionsTelemetryInfo = searchViewController.visibleSuggestionsTelemetryInfo visibleSuggestionsTelemetryInfo.forEach { trackVisibleSuggestion(telemetryInfo: $0) } - TelemetryWrapper.gleanRecordEvent(category: .action, method: .engagement, object: .locationBar) searchViewController.searchTelemetry?.recordURLBarSearchEngagementTelemetryEvent() case .abandoned: searchViewController.searchTelemetry?.engagementType = .dismiss let visibleSuggestionsTelemetryInfo = searchViewController.visibleSuggestionsTelemetryInfo visibleSuggestionsTelemetryInfo.forEach { trackVisibleSuggestion(telemetryInfo: $0) } - TelemetryWrapper.gleanRecordEvent(category: .action, method: .abandonment, object: .locationBar) searchViewController.searchTelemetry?.recordURLBarSearchAbandonmentTelemetryEvent() default: break @@ -3346,46 +3533,56 @@ extension BrowserViewController: SearchViewControllerDelegate { } extension BrowserViewController: TabManagerDelegate { - func tabManager(_ tabManager: TabManager, didSelectedTabChange selected: Tab?, previous: Tab?, isRestoring: Bool) { + func tabManager(_ tabManager: TabManager, didSelectedTabChange selectedTab: Tab, previousTab: Tab?, isRestoring: Bool) { + // Failing to have a non-nil webView by this point will cause the toolbar scrolling behaviour to regress, back/foward + // buttons never to become enabled, etc. on tab restore after launch. [FXIOS-9785, FXIOS-9781] + assert(selectedTab.webView != nil, "Setup will fail if the webView is not initialized for selectedTab") + // Remove the old accessibilityLabel. Since this webview shouldn't be visible, it doesn't need it // and having multiple views with the same label confuses tests. - if let webView = previous?.webView { - webView.endEditing(true) - webView.accessibilityLabel = nil - webView.accessibilityElementsHidden = true - webView.accessibilityIdentifier = nil - webView.removeFromSuperview() + if let previousWebView = previousTab?.webView { + previousWebView.endEditing(true) + previousWebView.accessibilityLabel = nil + previousWebView.accessibilityElementsHidden = true + previousWebView.accessibilityIdentifier = nil + previousWebView.removeFromSuperview() } - if let tab = selected, let webView = tab.webView { - updateURLBarDisplayURL(tab) - if !isToolbarRefactorEnabled, urlBar.inOverlayMode, tab.url?.displayURL != nil { - urlBar.leaveOverlayMode(reason: .finished, shouldCancelLoading: false) - } + if previousTab == nil || selectedTab.isPrivate != previousTab?.isPrivate { + applyTheme() - if previous == nil || tab.isPrivate != previous?.isPrivate { - applyTheme() - - if !isToolbarRefactorEnabled { - let ui: [PrivateModeUI?] = [toolbar, topTabsViewController, urlBar] - ui.forEach { $0?.applyUIMode(isPrivate: tab.isPrivate, theme: currentTheme()) } - } + // TODO: [FXIOS-8907] Ideally we shouldn't create tabs as a side-effect of UI theme updates. + var ui = [PrivateModeUI?]() + if isToolbarRefactorEnabled { + ui = [topTabsViewController] } else { - // Theme is applied to the tab and webView in the else case - // because in the if block is applied already to all the tabs and web views - tab.applyTheme(theme: currentTheme()) - webView.applyTheme(theme: currentTheme()) + ui = [toolbar, topTabsViewController, urlBar] } + ui.forEach { $0?.applyUIMode(isPrivate: selectedTab.isPrivate, theme: currentTheme()) } + } else { + // Theme is applied to the tab and webView in the else case + // because in the if block is applied already to all the tabs and web views + selectedTab.applyTheme(theme: currentTheme()) + selectedTab.webView?.applyTheme(theme: currentTheme()) + } - readerModeCache = tab.isPrivate ? MemoryReaderModeCache.sharedInstance : DiskReaderModeCache.sharedInstance - if let privateModeButton = topTabsViewController?.privateModeButton, - previous != nil && previous?.isPrivate != tab.isPrivate { - privateModeButton.setSelected(tab.isPrivate, animated: true) - } - ReaderModeHandlers.readerModeCache = readerModeCache + updateURLBarDisplayURL(selectedTab) + if isToolbarRefactorEnabled, addressToolbarContainer.inOverlayMode, selectedTab.url?.displayURL != nil { + addressToolbarContainer.leaveOverlayMode(reason: .finished, shouldCancelLoading: false) + } else if !isToolbarRefactorEnabled, urlBar.inOverlayMode, selectedTab.url?.displayURL != nil { + urlBar.leaveOverlayMode(reason: .finished, shouldCancelLoading: false) + } + + if let privateModeButton = topTabsViewController?.privateModeButton, + previousTab != nil && previousTab?.isPrivate != selectedTab.isPrivate { + privateModeButton.setSelected(selectedTab.isPrivate, animated: true) + } + readerModeCache = selectedTab.isPrivate ? MemoryReaderModeCache.sharedInstance : DiskReaderModeCache.sharedInstance + ReaderModeHandlers.readerModeCache = readerModeCache - scrollController.tab = tab + scrollController.tab = selectedTab + if let webView = selectedTab.webView { webView.accessibilityLabel = .WebViewAccessibilityLabel webView.accessibilityIdentifier = "contentView" webView.accessibilityElementsHidden = false @@ -3393,56 +3590,51 @@ extension BrowserViewController: TabManagerDelegate { browserDelegate?.show(webView: webView) if webView.url == nil { - // The web view can go gray if it was zombified due to memory pressure. + // The webView can go gray if it was zombified due to memory pressure. // When this happens, the URL is nil, so try restoring the page upon selection. - tab.reload() + selectedTab.reload() } - - // Update Fakespot sidebar if necessary - updateFakespot(tab: tab) } + // Update Fakespot sidebar if necessary + updateFakespot(tab: selectedTab) + updateTabCountUsingTabManager(tabManager) bottomContentStackView.removeAllArrangedViews() - if let bars = selected?.bars { - bars.forEach { bar in - bottomContentStackView.addArrangedViewToBottom(bar, completion: { self.view.layoutIfNeeded() }) - } + + selectedTab.bars.forEach { bar in + bottomContentStackView.addArrangedViewToBottom(bar, completion: { self.view.layoutIfNeeded() }) } - updateFindInPageVisibility(isVisible: false, tab: previous) - setupMiddleButtonStatus(isLoading: selected?.loading ?? false) + updateFindInPageVisibility(isVisible: false, tab: previousTab) + setupMiddleButtonStatus(isLoading: selectedTab.loading) if isToolbarRefactorEnabled { - dispatchBackForwardToolbarAction(selected?.canGoBack, windowUUID, .backButtonStateChanged) - dispatchBackForwardToolbarAction(selected?.canGoForward, windowUUID, .forwardButtonStateChanged) + dispatchBackForwardToolbarAction(selectedTab.canGoBack, windowUUID, .backButtonStateChanged) + dispatchBackForwardToolbarAction(selectedTab.canGoForward, windowUUID, .forwardButtonStateChanged) } else { - navigationToolbar.updateBackStatus(selected?.canGoBack ?? false) - navigationToolbar.updateForwardStatus(selected?.canGoForward ?? false) + navigationToolbar.updateBackStatus(selectedTab.canGoBack) + navigationToolbar.updateForwardStatus(selectedTab.canGoForward) } - if let url = selected?.webView?.url, !InternalURL.isValid(url: url) { + if let url = selectedTab.webView?.url, !InternalURL.isValid(url: url) { if isToolbarRefactorEnabled { - addressToolbarContainer.updateProgressBar(progress: selected?.estimatedProgress ?? 0) + addressToolbarContainer.updateProgressBar(progress: selectedTab.estimatedProgress) } else { - urlBar.updateProgressBar(Float(selected?.estimatedProgress ?? 0)) + urlBar.updateProgressBar(Float(selectedTab.estimatedProgress)) } } - if let readerMode = selected?.getContentScript(name: ReaderMode.name()) as? ReaderMode { - if !isToolbarRefactorEnabled { - urlBar.updateReaderModeState(readerMode.state) - } + if let readerMode = selectedTab.getContentScript(name: ReaderMode.name()) as? ReaderMode { + updateReaderModeState(for: selectedTab, readerModeState: readerMode.state) if readerMode.state == .active { showReaderModeBar(animated: false) } else { hideReaderModeBar(animated: false) } } else { - if !isToolbarRefactorEnabled { - urlBar.updateReaderModeState(ReaderModeState.unavailable) - } + updateReaderModeState(for: selectedTab, readerModeState: .unavailable) } if topTabsVisible { @@ -3450,8 +3642,8 @@ extension BrowserViewController: TabManagerDelegate { } /// If the selectedTab is showing an error page trigger a reload - if let url = selected?.url, let internalUrl = InternalURL(url), internalUrl.isErrorPage { - selected?.reloadPage() + if let url = selectedTab.url, let internalUrl = InternalURL(url), internalUrl.isErrorPage { + selectedTab.reloadPage() return } } @@ -3638,6 +3830,11 @@ extension BrowserViewController: KeyboardHelperDelegate { finishEditionMode() } + func keyboardHelper(_ keyboardHelper: KeyboardHelper, keyboardDidHideWithState state: KeyboardState) { + tabManager.selectedTab?.setFindInPage(isBottomSearchBar: isBottomSearchBar, + doesFindInPageBarExist: findInPageBar != nil) + } + func keyboardHelper(_ keyboardHelper: KeyboardHelper, keyboardWillChangeWithState state: KeyboardState) { keyboardState = state updateViewConstraints() @@ -3731,6 +3928,10 @@ extension BrowserViewController: TopTabsDelegate { newTabSettings: newTabSettings) } + func topTabsDidLongPressNewTab(button: UIButton) { + presentNewTabLongPressActionSheet(from: button) + } + func topTabsDidChangeTab() { // Only for iPad leave overlay mode on tab change overlayManager.switchTab(shouldCancelLoading: true) @@ -3774,7 +3975,7 @@ extension BrowserViewController: DevicePickerViewControllerDelegate, Instruction profile.sendItem(shareItem, toDevices: devices).uponQueue(.main) { _ in self.popToBVC() DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { - SimpleToast().showAlertWithText(.AppMenu.AppMenuTabSentConfirmMessage, + SimpleToast().showAlertWithText(.LegacyAppMenu.AppMenuTabSentConfirmMessage, bottomContainer: self.contentContainer, theme: self.currentTheme()) } diff --git a/firefox-ios/Client/Frontend/Browser/DownloadQueue.swift b/firefox-ios/Client/Frontend/Browser/DownloadQueue.swift index 89149ef956fd..915b4c99c9d2 100644 --- a/firefox-ios/Client/Frontend/Browser/DownloadQueue.swift +++ b/firefox-ios/Client/Frontend/Browser/DownloadQueue.swift @@ -24,6 +24,8 @@ class Download: NSObject { fileprivate(set) var totalBytesExpected: Int64? fileprivate(set) var bytesDownloaded: Int64 + // Whether the server has indicated it will send encoded data (via response `Content-Encoding` header) (FXIOS-9039) + fileprivate(set) var hasContentEncoding: Bool? init(originWindow: WindowUUID) { self.filename = "unknown" @@ -117,6 +119,17 @@ class HTTPDownload: Download { } self.totalBytesExpected = preflightResponse.expectedContentLength > 0 ? preflightResponse.expectedContentLength : nil + if let contentEncodingHeader = (preflightResponse as? HTTPURLResponse)? + .allHeaderFields["Content-Encoding"] as? String, + !contentEncodingHeader.isEmpty { + // If this header is present, some encoding has been applied to the payload (likely gzip compression), so the + // above `preflightResponse.expectedContentLength` reflects the size of the encoded content, not the actual file + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding + // FXIOS-9039 + self.hasContentEncoding = true + } else { + self.hasContentEncoding = false + } self.session = URLSession(configuration: .ephemeral, delegate: self, delegateQueue: .main) self.task = session?.downloadTask(with: self.request) diff --git a/firefox-ios/Client/Frontend/Browser/DownloadToast.swift b/firefox-ios/Client/Frontend/Browser/DownloadToast.swift index 0e841d5ec494..f037f9cd026b 100644 --- a/firefox-ios/Client/Frontend/Browser/DownloadToast.swift +++ b/firefox-ios/Client/Frontend/Browser/DownloadToast.swift @@ -49,11 +49,26 @@ class DownloadToast: Toast { var downloads: [Download] = [] - var percent: CGFloat = 0.0 { + // Returns true if one or more downloads have encoded data (indicated via response `Content-Encoding` header). + // If at least one download has encoded data, we cannot get a correct total estimate for all the downloads. + // In that case, we do not show descriptive text. This will be improved in a later rework of the download manager. + // FXIOS-9039 + var hasContentEncoding: Bool { + return downloads.contains(where: { $0.hasContentEncoding ?? false }) + } + + var percent: CGFloat? = 0.0 { didSet { UIView.animate(withDuration: 0.05) { self.descriptionLabel.text = self.descriptionText - self.progressWidthConstraint?.constant = self.toastView.frame.width * self.percent + + if let percent = self.percent { + self.progressView.isHidden = false + self.progressWidthConstraint?.constant = self.toastView.frame.width * percent + } else { + self.progressView.isHidden = true + } + self.layoutIfNeeded() } } @@ -72,6 +87,11 @@ class DownloadToast: Toast { } var descriptionText: String { + guard !hasContentEncoding else { + // We cannot get a correct estimate of encoded downloaded bytes (FXIOS-9039) + return String() + } + let downloadedSize = ByteCountFormatter.string( fromByteCount: combinedBytesDownloaded, countStyle: .file @@ -144,6 +164,12 @@ class DownloadToast: Toast { func updatePercent() { DispatchQueue.main.async { + guard !self.hasContentEncoding else { + // We cannot get a correct estimate of encoded downloaded bytes (FXIOS-9039) + self.percent = nil + return + } + guard let combinedTotalBytesExpected = self.combinedTotalBytesExpected else { self.percent = 0.0 return diff --git a/firefox-ios/Client/Frontend/Browser/EnhancedTrackingProtection/SlideoverPresentationController.swift b/firefox-ios/Client/Frontend/Browser/EnhancedTrackingProtection/SlideoverPresentationController.swift index 7e48c68f3c4f..56a250661bac 100644 --- a/firefox-ios/Client/Frontend/Browser/EnhancedTrackingProtection/SlideoverPresentationController.swift +++ b/firefox-ios/Client/Frontend/Browser/EnhancedTrackingProtection/SlideoverPresentationController.swift @@ -8,11 +8,12 @@ struct SlideOverUXConstants { static let ETPMenuCornerRadius: CGFloat = 8 } -class SlideOverPresentationController: UIPresentationController { +class SlideOverPresentationController: UIPresentationController, FeatureFlaggable { let blurEffectView: UIVisualEffectView! var tapGestureRecognizer = UITapGestureRecognizer() var globalETPStatus: Bool weak var enhancedTrackingProtectionMenuDelegate: EnhancedTrackingProtectionMenuDelegate? + weak var legacyTrackingProtectionMenuDelegate: TrackingProtectionMenuDelegate? init( presentedViewController: UIViewController, @@ -88,7 +89,13 @@ class SlideOverPresentationController: UIPresentationController { @objc func dismissController() { - enhancedTrackingProtectionMenuDelegate?.didFinish() + let trackingProtectionRefactorStatus = featureFlags.isFeatureEnabled(.trackingProtectionRefactor, + checking: .buildOnly) + if trackingProtectionRefactorStatus { + enhancedTrackingProtectionMenuDelegate?.didFinish() + } else { + legacyTrackingProtectionMenuDelegate?.didFinish() + } } override func preferredContentSizeDidChange(forChildContentContainer container: UIContentContainer) { diff --git a/firefox-ios/Client/Frontend/Browser/MainMenuActionHelper.swift b/firefox-ios/Client/Frontend/Browser/MainMenuActionHelper.swift index 9b641cc3b25f..6e61d004ccae 100644 --- a/firefox-ios/Client/Frontend/Browser/MainMenuActionHelper.swift +++ b/firefox-ios/Client/Frontend/Browser/MainMenuActionHelper.swift @@ -309,7 +309,7 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, private func getNewTabAction() -> PhotonRowActions? { guard let tab = selectedTab else { return nil } - return SingleActionViewModel(title: tab.isPrivate ? .AppMenu.NewPrivateTab : .AppMenu.NewTab, + return SingleActionViewModel(title: tab.isPrivate ? .LegacyAppMenu.NewPrivateTab : .LegacyAppMenu.NewTab, iconString: StandardImageIdentifiers.Large.plus) { _ in let shouldFocusLocationField = NewTabAccessors.getNewTabPage(self.profile.prefs) != .homePage self.delegate?.openNewTabFromMenu(focusLocationField: shouldFocusLocationField, isPrivate: tab.isPrivate) @@ -318,7 +318,7 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, } private func getHistoryLibraryAction() -> PhotonRowActions { - return SingleActionViewModel(title: .AppMenu.AppMenuHistory, + return SingleActionViewModel(title: .LegacyAppMenu.AppMenuHistory, iconString: StandardImageIdentifiers.Large.history) { _ in self.delegate?.showLibrary(panel: .history) TelemetryWrapper.recordEvent(category: .action, method: .tap, object: .viewHistoryPanel) @@ -326,7 +326,7 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, } private func getDownloadsLibraryAction() -> PhotonRowActions { - return SingleActionViewModel(title: .AppMenu.AppMenuDownloads, + return SingleActionViewModel(title: .LegacyAppMenu.AppMenuDownloads, iconString: StandardImageIdentifiers.Large.download) { _ in self.delegate?.showLibrary(panel: .downloads) TelemetryWrapper.recordEvent(category: .action, method: .tap, object: .viewDownloadsPanel) @@ -338,7 +338,7 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, private func getZoomAction() -> PhotonRowActions? { guard let tab = selectedTab else { return nil } let zoomLevel = NumberFormatter.localizedString(from: NSNumber(value: tab.pageZoom), number: .percent) - let title = String(format: .AppMenu.ZoomPageTitle, zoomLevel) + let title = String(format: .LegacyAppMenu.ZoomPageTitle, zoomLevel) let zoomAction = SingleActionViewModel(title: title, iconString: StandardImageIdentifiers.Large.pageZoom) { _ in self.delegate?.showZoomPage(tab: tab) @@ -347,7 +347,7 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, } private func getFindInPageAction() -> PhotonRowActions { - return SingleActionViewModel(title: .AppMenu.AppMenuFindInPageTitleString, + return SingleActionViewModel(title: .LegacyAppMenu.AppMenuFindInPageTitleString, iconString: StandardImageIdentifiers.Large.search) { _ in TelemetryWrapper.recordEvent(category: .action, method: .tap, object: .findInPage) self.delegate?.showFindInPage() @@ -363,11 +363,11 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, let siteTypeTelemetryObject: TelemetryWrapper.EventObject // swiftlint:disable line_length if defaultUAisDesktop { - toggleActionTitle = tab.changedUserAgent ? .AppMenu.AppMenuViewDesktopSiteTitleString : .AppMenu.AppMenuViewMobileSiteTitleString + toggleActionTitle = tab.changedUserAgent ? .LegacyAppMenu.AppMenuViewDesktopSiteTitleString : .LegacyAppMenu.AppMenuViewMobileSiteTitleString toggleActionIcon = tab.changedUserAgent ? StandardImageIdentifiers.Large.deviceDesktop : StandardImageIdentifiers.Large.deviceMobile siteTypeTelemetryObject = .requestDesktopSite } else { - toggleActionTitle = tab.changedUserAgent ? .AppMenu.AppMenuViewMobileSiteTitleString : .AppMenu.AppMenuViewDesktopSiteTitleString + toggleActionTitle = tab.changedUserAgent ? .LegacyAppMenu.AppMenuViewMobileSiteTitleString : .LegacyAppMenu.AppMenuViewDesktopSiteTitleString toggleActionIcon = tab.changedUserAgent ? StandardImageIdentifiers.Large.deviceMobile : StandardImageIdentifiers.Large.deviceDesktop siteTypeTelemetryObject = .requestMobileSite } @@ -388,19 +388,19 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, } private func getCopyAction() -> PhotonRowActions? { - return SingleActionViewModel(title: .AppMenu.AppMenuCopyLinkTitleString, + return SingleActionViewModel(title: .LegacyAppMenu.AppMenuCopyLinkTitleString, iconString: StandardImageIdentifiers.Large.link) { _ in TelemetryWrapper.recordEvent(category: .action, method: .tap, object: .copyAddress) if let url = self.selectedTab?.canonicalURL?.displayURL { UIPasteboard.general.url = url - self.delegate?.showToast(message: .AppMenu.AppMenuCopyURLConfirmMessage, toastAction: .copyUrl) + self.delegate?.showToast(message: .LegacyAppMenu.AppMenuCopyURLConfirmMessage, toastAction: .copyUrl) } }.items } private func getSendToDevice() -> PhotonRowActions { let uuid = windowUUID - return SingleActionViewModel(title: .AppMenu.TouchActions.SendLinkToDeviceTitle, + return SingleActionViewModel(title: .LegacyAppMenu.TouchActions.SendLinkToDeviceTitle, iconString: StandardImageIdentifiers.Large.deviceDesktopSend) { _ in guard let delegate = self.sendToDeviceDelegate, let selectedTab = self.selectedTab, @@ -427,7 +427,7 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, private func getReportSiteIssueAction() -> PhotonRowActions? { guard featureFlags.isFeatureEnabled(.reportSiteIssue, checking: .buildOnly) else { return nil } - return SingleActionViewModel(title: .AppMenu.AppMenuReportSiteIssueTitleString, + return SingleActionViewModel(title: .LegacyAppMenu.AppMenuReportSiteIssueTitleString, iconString: StandardImageIdentifiers.Large.lightbulb) { _ in guard let tabURL = self.selectedTab?.url?.absoluteString else { return } self.delegate?.openURLInNewTab(SupportUtils.URLForReportSiteIssue(tabURL), isPrivate: false) @@ -436,7 +436,7 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, } private func getHelpAction() -> PhotonRowActions { - return SingleActionViewModel(title: .AppMenu.Help, + return SingleActionViewModel(title: .LegacyAppMenu.Help, iconString: StandardImageIdentifiers.Large.helpCircle) { _ in if let url = URL(string: "https://support.mozilla.org/products/ios") { self.delegate?.openURLInNewTab(url, isPrivate: self.tabManager.selectedTab?.isPrivate ?? false) @@ -446,7 +446,7 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, } private func getCustomizeHomePageAction() -> PhotonRowActions? { - return SingleActionViewModel(title: .AppMenu.CustomizeHomePage, + return SingleActionViewModel(title: .LegacyAppMenu.CustomizeHomePage, iconString: StandardImageIdentifiers.Large.edit) { _ in self.delegate?.showCustomizeHomePage() TelemetryWrapper.recordEvent(category: .action, method: .tap, object: .customizeHomePage) @@ -454,7 +454,7 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, } private func getSettingsAction() -> PhotonRowActions { - let openSettings = SingleActionViewModel(title: .AppMenu.AppMenuSettingsTitleString, + let openSettings = SingleActionViewModel(title: .LegacyAppMenu.AppMenuSettingsTitleString, iconString: StandardImageIdentifiers.Large.settings) { _ in TelemetryWrapper.recordEvent(category: .action, method: .open, object: .settings) @@ -470,7 +470,12 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, var items: [PhotonRowActions] = [] let nightModeEnabled = NightModeHelper.isActivated() - let nightModeTitle: String = nightModeEnabled ? .AppMenu.AppMenuTurnOffNightMode : .AppMenu.AppMenuTurnOnNightMode + let nightModeTitle: String = if nightModeEnabled { + .LegacyAppMenu.AppMenuTurnOffNightMode + } else { + .LegacyAppMenu.AppMenuTurnOnNightMode + } + let nightMode = SingleActionViewModel( title: nightModeTitle, iconString: StandardImageIdentifiers.Large.nightMode, @@ -505,7 +510,7 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, let needsReAuth = rustAccount.accountNeedsReauth() guard let userProfile = rustAccount.userProfile else { - return SingleActionViewModel(title: .AppMenu.SyncAndSaveData, + return SingleActionViewModel(title: .LegacyAppMenu.SyncAndSaveData, iconString: StandardImageIdentifiers.Large.sync, tapHandler: action).items } @@ -549,7 +554,7 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, delegate?.updateToolbarState() } - whatsNewAction = SingleActionViewModel(title: .AppMenu.WhatsNewString, + whatsNewAction = SingleActionViewModel(title: .LegacyAppMenu.WhatsNewString, iconString: StandardImageIdentifiers.Large.whatsNew, isEnabled: showBadgeForWhatsNew) { _ in if let whatsNewURL = SupportUtils.URLForWhatsNew { @@ -567,8 +572,8 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, // MARK: Share private func getShareFileAction() -> PhotonRowActions { - return SingleActionViewModel(title: .AppMenu.AppMenuSharePageTitleString, - iconString: StandardImageIdentifiers.Large.shareApple) { _ in + return SingleActionViewModel(title: .LegacyAppMenu.AppMenuSharePageTitleString, + iconString: StandardImageIdentifiers.Large.share) { _ in guard let tab = self.selectedTab, let url = tab.url else { return } @@ -578,8 +583,8 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, } private func getShareAction() -> PhotonRowActions { - return SingleActionViewModel(title: .AppMenu.Share, - iconString: StandardImageIdentifiers.Large.shareApple) { _ in + return SingleActionViewModel(title: .LegacyAppMenu.Share, + iconString: StandardImageIdentifiers.Large.share) { _ in guard let tab = self.selectedTab, let url = tab.canonicalURL?.displayURL else { return } TelemetryWrapper.recordEvent(category: .action, method: .tap, object: .sharePageWith) @@ -613,7 +618,7 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, } private func getDownloadPDFAction() -> PhotonRowActions { - return SingleActionViewModel(title: .AppMenu.AppMenuDownloadPDF, + return SingleActionViewModel(title: .LegacyAppMenu.AppMenuDownloadPDF, iconString: StandardImageIdentifiers.Large.folder) { _ in guard let tab = self.selectedTab, let temporaryDocument = tab.temporaryDocument else { return } temporaryDocument.getURL { fileURL in @@ -652,7 +657,7 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, } private func getReadingListLibraryAction() -> SingleActionViewModel { - return SingleActionViewModel(title: .AppMenu.ReadingList, + return SingleActionViewModel(title: .LegacyAppMenu.ReadingList, iconString: StandardImageIdentifiers.Large.readingList) { _ in self.delegate?.showLibrary(panel: .readingList) } @@ -663,7 +668,7 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, } private func getAddReadingListAction() -> SingleActionViewModel { - return SingleActionViewModel(title: .AppMenu.AddReadingList, + return SingleActionViewModel(title: .LegacyAppMenu.AddReadingList, iconString: StandardImageIdentifiers.Large.readingListAdd) { _ in guard let tab = self.selectedTab, let url = self.tabUrl?.displayURL @@ -680,19 +685,19 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, object: .readingListItem, value: .pageActionMenu ) - self.delegate?.showToast(message: .AppMenu.AddToReadingListConfirmMessage, toastAction: .addToReadingList) + self.delegate?.showToast(message: .LegacyAppMenu.AddToReadingListConfirmMessage, toastAction: .addToReadingList) } } private func getRemoveReadingListAction() -> SingleActionViewModel { - return SingleActionViewModel(title: .AppMenu.RemoveReadingList, + return SingleActionViewModel(title: .LegacyAppMenu.RemoveReadingList, iconString: StandardImageIdentifiers.Large.delete) { _ in guard let url = self.tabUrl?.displayURL?.absoluteString, let record = self.profile.readingList.getRecordWithURL(url).value.successValue else { return } self.profile.readingList.deleteRecord(record, completion: nil) - self.delegate?.showToast(message: .AppMenu.RemoveFromReadingListConfirmMessage, + self.delegate?.showToast(message: .LegacyAppMenu.RemoveFromReadingListConfirmMessage, toastAction: .removeFromReadingList) TelemetryWrapper.recordEvent(category: .action, method: .delete, @@ -716,7 +721,7 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, } private func getBookmarkLibraryAction() -> SingleActionViewModel { - return SingleActionViewModel(title: .AppMenu.Bookmarks, + return SingleActionViewModel(title: .LegacyAppMenu.Bookmarks, iconString: StandardImageIdentifiers.Large.bookmarkTrayFill) { _ in self.delegate?.showLibrary(panel: .bookmarks) } @@ -727,7 +732,7 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, } private func getAddBookmarkAction() -> SingleActionViewModel { - return SingleActionViewModel(title: .AppMenu.AddBookmark, + return SingleActionViewModel(title: .LegacyAppMenu.AddBookmark, iconString: StandardImageIdentifiers.Large.bookmark) { _ in guard let tab = self.selectedTab, let url = tab.canonicalURL?.displayURL @@ -745,14 +750,14 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, } private func getRemoveBookmarkAction() -> SingleActionViewModel { - return SingleActionViewModel(title: .AppMenu.RemoveBookmark, + return SingleActionViewModel(title: .LegacyAppMenu.RemoveBookmark, iconString: StandardImageIdentifiers.Large.bookmarkSlash) { _ in guard let url = self.tabUrl?.displayURL else { return } self.profile.places.deleteBookmarksWithURL(url: url.absoluteString).uponQueue(.main) { result in guard result.isSuccess else { return } self.delegate?.showToast( - message: .AppMenu.RemoveBookmarkConfirmMessage, + message: .LegacyAppMenu.RemoveBookmarkConfirmMessage, toastAction: .removeBookmark ) self.removeBookmarkShortcut() @@ -781,7 +786,7 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, let site = Site(url: url.absoluteString, title: title) self.profile.pinnedSites.addPinnedTopSite(site).uponQueue(.main) { result in guard result.isSuccess else { return } - self.delegate?.showToast(message: .AppMenu.AddPinToShortcutsConfirmMessage, toastAction: .pinPage) + self.delegate?.showToast(message: .LegacyAppMenu.AddPinToShortcutsConfirmMessage, toastAction: .pinPage) } TelemetryWrapper.recordEvent(category: .action, method: .tap, object: .pinToTopSites) @@ -789,7 +794,7 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, } private func getRemoveShortcutAction() -> SingleActionViewModel { - return SingleActionViewModel(title: .AppMenu.RemoveFromShortcuts, + return SingleActionViewModel(title: .LegacyAppMenu.RemoveFromShortcuts, iconString: StandardImageIdentifiers.Large.pinSlash) { _ in guard let url = self.selectedTab?.url?.displayURL, let title = self.selectedTab?.displayTitle else { return } @@ -797,7 +802,7 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, self.profile.pinnedSites.removeFromPinnedTopSites(site).uponQueue(.main) { result in if result.isSuccess { self.delegate?.showToast( - message: .AppMenu.RemovePinFromShortcutsConfirmMessage, + message: .LegacyAppMenu.RemovePinFromShortcutsConfirmMessage, toastAction: .removePinPage ) } @@ -811,7 +816,7 @@ class MainMenuActionHelper: PhotonActionSheetProtocol, private func getPasswordAction(navigationController: UINavigationController?) -> PhotonRowActions? { guard PasswordManagerListViewController.shouldShowAppMenuShortcut(forPrefs: profile.prefs) else { return nil } TelemetryWrapper.recordEvent(category: .action, method: .open, object: .logins) - return SingleActionViewModel(title: .AppMenu.AppMenuPasswords, + return SingleActionViewModel(title: .LegacyAppMenu.AppMenuPasswords, iconString: StandardImageIdentifiers.Large.login, iconType: .Image, iconAlignment: .left) { _ in diff --git a/firefox-ios/Client/Frontend/Browser/OpenInHelper/DownloadHelper.swift b/firefox-ios/Client/Frontend/Browser/OpenInHelper/DownloadHelper.swift index b2424f6f7402..df13e3d8ac60 100644 --- a/firefox-ios/Client/Frontend/Browser/OpenInHelper/DownloadHelper.swift +++ b/firefox-ios/Client/Frontend/Browser/OpenInHelper/DownloadHelper.swift @@ -119,6 +119,8 @@ class DownloadHelper: NSObject { var filenameItem: SingleActionViewModel var modelText = host + // This size reflects the (possibly compressed) download size of the file, not necessarily its true size. + // e.g. In the case of gzip content (FXIOS-9039) if let expectedSize = expectedSize { modelText = "\(expectedSize) — \(host)" } diff --git a/firefox-ios/Client/Frontend/Browser/SearchLoader.swift b/firefox-ios/Client/Frontend/Browser/SearchLoader.swift index eb6acb105f77..bf5cd2c2d808 100644 --- a/firefox-ios/Client/Frontend/Browser/SearchLoader.swift +++ b/firefox-ios/Client/Frontend/Browser/SearchLoader.swift @@ -16,14 +16,14 @@ private let URLBeforePathRegex = try? NSRegularExpression(pattern: "^https?://([ */ class SearchLoader: Loader, SearchViewModel>, FeatureFlaggable { fileprivate let profile: Profile - fileprivate let urlBar: URLBarView + fileprivate let autocompleteView: Autocompletable private let logger: Logger private var skipNextAutocomplete: Bool - init(profile: Profile, urlBar: URLBarView, logger: Logger = DefaultLogger.shared) { + init(profile: Profile, autocompleteView: Autocompletable, logger: Logger = DefaultLogger.shared) { self.profile = profile - self.urlBar = urlBar + self.autocompleteView = autocompleteView self.skipNextAutocomplete = false self.logger = logger @@ -115,49 +115,59 @@ class SearchLoader: Loader, SearchViewModel>, FeatureFlaggable { } DispatchQueue.main.async { - let results = queries - defer { - GleanMetrics.Awesomebar.queryTime.stopAndAccumulate(timerid) - } + self.updateUIWithBookmarksAsSitesResults(queries: queries, + timerid: timerid, + historyHighlightsEnabled: historyHighlightsEnabled, + oldValue: oldValue) + } + } + } + } - let bookmarksSites = results[safe: 0] ?? [] - var combinedSites = bookmarksSites - if !historyHighlightsEnabled { - let historySites = results[safe: 1] ?? [] - combinedSites += historySites - } + private func updateUIWithBookmarksAsSitesResults(queries: [[Site]], + timerid: TimerId, + historyHighlightsEnabled: Bool, + oldValue: String) { + let results = queries + defer { + GleanMetrics.Awesomebar.queryTime.stopAndAccumulate(timerid) + } + + let bookmarksSites = results[safe: 0] ?? [] + var combinedSites = bookmarksSites + if !historyHighlightsEnabled { + let historySites = results[safe: 1] ?? [] + combinedSites += historySites + } - // Load the data in the table view. - self.load(ArrayCursor(data: combinedSites)) + // Load the data in the table view. + load(ArrayCursor(data: combinedSites)) - // If the new search string is not longer than the previous - // we don't need to find an autocomplete suggestion. - guard oldValue.count < self.query.count else { return } + // If the new search string is not longer than the previous + // we don't need to find an autocomplete suggestion. + guard oldValue.count < query.count else { return } - // If we should skip the next autocomplete, reset - // the flag and bail out here. - guard !self.skipNextAutocomplete else { - self.skipNextAutocomplete = false - return - } + // If we should skip the next autocomplete, reset + // the flag and bail out here. + guard !skipNextAutocomplete else { + skipNextAutocomplete = false + return + } - // First, see if the query matches any URLs from the user's search history. - for site in combinedSites { - if let completion = self.completionForURL(site.url) { - self.urlBar.setAutocompleteSuggestion(completion) - return - } - } + // First, see if the query matches any URLs from the user's search history. + for site in combinedSites { + if let completion = completionForURL(site.url) { + autocompleteView.setAutocompleteSuggestion(completion) + return + } + } - // If there are no search history matches, try matching one of the Alexa top domains. - if let topDomains = self.topDomains { - for domain in topDomains { - if let completion = self.completionForDomain(domain) { - self.urlBar.setAutocompleteSuggestion(completion) - return - } - } - } + // If there are no search history matches, try matching one of the Alexa top domains. + if let topDomains = topDomains { + for domain in topDomains { + if let completion = completionForDomain(domain) { + autocompleteView.setAutocompleteSuggestion(completion) + return } } } diff --git a/firefox-ios/Client/Frontend/Browser/SearchSuggestClient.swift b/firefox-ios/Client/Frontend/Browser/SearchSuggestClient.swift index 6b12ff35e4f5..5e70965fad28 100644 --- a/firefox-ios/Client/Frontend/Browser/SearchSuggestClient.swift +++ b/firefox-ios/Client/Frontend/Browser/SearchSuggestClient.swift @@ -54,12 +54,7 @@ class SearchSuggestClient { guard let data = data, validatedHTTPResponse(response, statusCode: 200..<300) != nil else { - let error = NSError( - domain: SearchSuggestClientErrorDomain, - code: SearchSuggestClientErrorInvalidResponse, - userInfo: nil - ) - callback(nil, error as NSError?) + self.handleInvalidResponseError(callback: callback) return } @@ -71,22 +66,12 @@ class SearchSuggestClient { // That is, an array of at least two elements: the search term and an array of suggestions. if array?.count ?? 0 < 2 { - let error = NSError( - domain: SearchSuggestClientErrorDomain, - code: SearchSuggestClientErrorInvalidResponse, - userInfo: nil - ) - callback(nil, error) + self.handleInvalidResponseError(callback: callback) return } guard let suggestions = array?[1] as? [String] else { - let error = NSError( - domain: SearchSuggestClientErrorDomain, - code: SearchSuggestClientErrorInvalidResponse, - userInfo: nil - ) - callback(nil, error) + self.handleInvalidResponseError(callback: callback) return } @@ -95,6 +80,15 @@ class SearchSuggestClient { task?.resume() } + private func handleInvalidResponseError(callback: @escaping (_ response: [String]?, _ error: NSError?) -> Void) { + let error = NSError( + domain: SearchSuggestClientErrorDomain, + code: SearchSuggestClientErrorInvalidResponse, + userInfo: nil + ) + callback(nil, error) + } + func cancelPendingRequest() { task?.cancel() } diff --git a/firefox-ios/Client/Frontend/Browser/TabDisplayManager.swift b/firefox-ios/Client/Frontend/Browser/TabDisplayManager.swift index edb8dc42304e..a66cc71853ce 100644 --- a/firefox-ios/Client/Frontend/Browser/TabDisplayManager.swift +++ b/firefox-ios/Client/Frontend/Browser/TabDisplayManager.swift @@ -937,19 +937,12 @@ extension LegacyTabDisplayManager: TabEventHandler { // MARK: - TabManagerDelegate extension LegacyTabDisplayManager: TabManagerDelegate { - func tabManager( - _ tabManager: TabManager, - didSelectedTabChange selected: Tab?, - previous: Tab?, - isRestoring: Bool - ) { + func tabManager(_ tabManager: TabManager, didSelectedTabChange selectedTab: Tab, previousTab: Tab?, isRestoring: Bool) { cancelDragAndGestures() - if let selected = selected { - // A tab can be re-selected during deletion - let changed = selected != previous - updateCellFor(tab: selected, selectedTabChanged: changed) - } + // A tab can be re-selected during deletion + let changedTab = selectedTab != previousTab + updateCellFor(tab: selectedTab, selectedTabChanged: changedTab) // Rather than using 'previous' Tab to deselect, just check if the selected tab // is different, and update the required cells. The refreshStore() cancels diff --git a/firefox-ios/Client/Frontend/Browser/TabScrollController.swift b/firefox-ios/Client/Frontend/Browser/TabScrollController.swift index fb05e6cc9fcd..b32aa73043b0 100644 --- a/firefox-ios/Client/Frontend/Browser/TabScrollController.swift +++ b/firefox-ios/Client/Frontend/Browser/TabScrollController.swift @@ -31,6 +31,8 @@ class TabScrollingController: NSObject, FeatureFlaggable, SearchBarLocationProvi } didSet { + // FXIOS-9781 This could result in scrolling not closing the toolbar + assert(scrollView != nil, "Can't set the scrollView delegate if the webView.scrollView is nil") self.scrollView?.addGestureRecognizer(panGesture) scrollView?.delegate = self scrollView?.keyboardDismissMode = .onDrag @@ -197,6 +199,21 @@ class TabScrollingController: NSObject, FeatureFlaggable, SearchBarLocationProvi completion: nil) } + func hideToolbars(animated: Bool, isFindInPageMode: Bool = false) { + guard toolbarState != .collapsed || isFindInPageMode else { return } + toolbarState = .collapsed + + let actualDuration = TimeInterval(ToolbarBaseAnimationDuration * hideDurationRation) + self.animateToolbarsWithOffsets( + animated, + duration: actualDuration, + headerOffset: -topScrollHeight, + bottomContainerOffset: bottomContainerScrollHeight, + overKeyboardOffset: overKeyboardScrollHeight, + alpha: 0, + completion: nil) + } + func beginObserving(scrollView: UIScrollView) { guard !observedScrollViews.contains(scrollView) else { logger.log("Duplicate observance of scroll view", level: .warning, category: .webview) @@ -252,21 +269,6 @@ class TabScrollingController: NSObject, FeatureFlaggable, SearchBarLocationProvi // MARK: - Private private extension TabScrollingController { - func hideToolbars(animated: Bool) { - guard toolbarState != .collapsed else { return } - toolbarState = .collapsed - - let actualDuration = TimeInterval(ToolbarBaseAnimationDuration * hideDurationRation) - self.animateToolbarsWithOffsets( - animated, - duration: actualDuration, - headerOffset: -topScrollHeight, - bottomContainerOffset: bottomContainerScrollHeight, - overKeyboardOffset: overKeyboardScrollHeight, - alpha: 0, - completion: nil) - } - func configureRefreshControl(isEnabled: Bool) { scrollView?.refreshControl = isEnabled ? UIRefreshControl() : nil scrollView?.refreshControl?.addTarget(self, action: #selector(reload), for: .valueChanged) @@ -459,15 +461,15 @@ extension TabScrollingController: UIScrollViewDelegate { } func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { - guard !tabIsLoading(), !isBouncingAtBottom(), isAbleToScroll else { return } + guard !tabIsLoading(), !isBouncingAtBottom(), isAbleToScroll, let tab else { return } - tab?.shouldScrollToTop = false + tab.shouldScrollToTop = false if decelerate || (toolbarState == .animating && !decelerate) { - if scrollDirection == .up { + if scrollDirection == .up, !tab.isFindInPageMode { showToolbars(animated: true) } else if scrollDirection == .down { - hideToolbars(animated: true) + hideToolbars(animated: true, isFindInPageMode: tab.isFindInPageMode) } } } diff --git a/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyGridTabViewController.swift b/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyGridTabViewController.swift index cd90cd05d691..7e468b921092 100644 --- a/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyGridTabViewController.swift +++ b/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyGridTabViewController.swift @@ -628,7 +628,7 @@ extension LegacyGridTabViewController: LegacyTabPeekDelegate { } func tabPeekDidCopyUrl() { - SimpleToast().showAlertWithText(.AppMenu.AppMenuCopyURLConfirmMessage, + SimpleToast().showAlertWithText(.LegacyAppMenu.AppMenuCopyURLConfirmMessage, bottomContainer: view, theme: currentTheme(), bottomConstraintPadding: -toolbarHeight) @@ -701,7 +701,7 @@ extension LegacyGridTabViewController { guard !tabDisplayManager.isDragging else { return } let controller = AlertController(title: nil, message: nil, preferredStyle: .actionSheet) - controller.addAction(UIAlertAction(title: .AppMenu.AppMenuCloseAllTabsTitleString, + controller.addAction(UIAlertAction(title: .LegacyAppMenu.AppMenuCloseAllTabsTitleString, style: .default, handler: { _ in self.closeTabsTrayBackground() }), accessibilityIdentifier: AccessibilityIdentifiers.TabTray.deleteCloseAllButton) diff --git a/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyTabPeekPreviewActionBuilder.swift b/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyTabPeekPreviewActionBuilder.swift index 18f4b4f54695..2cd2e8735e1d 100644 --- a/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyTabPeekPreviewActionBuilder.swift +++ b/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyTabPeekPreviewActionBuilder.swift @@ -19,7 +19,7 @@ class LegacyTabPeekPreviewActionBuilder { func addSendToDeviceTitle(handler: @escaping (UIPreviewAction, UIViewController) -> Void) { actions.append(UIPreviewAction( - title: .AppMenu.TouchActions.SendToDeviceTitle, + title: .LegacyAppMenu.TouchActions.SendToDeviceTitle, style: .default, handler: handler )) diff --git a/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyTabPeekViewController.swift b/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyTabPeekViewController.swift index 132a450ad237..2d2ce61fc886 100644 --- a/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyTabPeekViewController.swift +++ b/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyTabPeekViewController.swift @@ -87,8 +87,8 @@ class LegacyTabPeekViewController: UIViewController, WKNavigationDelegate { } if self.hasRemoteClients { actions.append(UIAction( - title: .AppMenu.TouchActions.SendToDeviceTitle, - image: UIImage.templateImageNamed(StandardImageIdentifiers.Large.shareApple), + title: .LegacyAppMenu.TouchActions.SendToDeviceTitle, + image: UIImage.templateImageNamed(StandardImageIdentifiers.Large.share), identifier: nil ) { [weak self] _ in guard let wself = self, diff --git a/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyTabTrayViewController.swift b/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyTabTrayViewController.swift index f94d61352d5c..a158c8b39b2d 100644 --- a/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyTabTrayViewController.swift +++ b/firefox-ios/Client/Frontend/Browser/Tabs/Legacy/LegacyTabTrayViewController.swift @@ -53,7 +53,7 @@ class LegacyTabTrayViewController: UIViewController, Themeable, TabTrayControlle return createButtonItem(imageName: StandardImageIdentifiers.Large.delete, action: #selector(didTapDeleteTabs(_:)), a11yId: AccessibilityIdentifiers.TabTray.closeAllTabsButton, - a11yLabel: .AppMenu.Toolbar.TabTrayDeleteMenuButtonAccessibilityLabel) + a11yLabel: .LegacyAppMenu.Toolbar.TabTrayDeleteMenuButtonAccessibilityLabel) }() private lazy var newTabButtonIpad: UIBarButtonItem = { @@ -67,7 +67,7 @@ class LegacyTabTrayViewController: UIViewController, Themeable, TabTrayControlle return createButtonItem(imageName: StandardImageIdentifiers.Large.delete, action: #selector(didTapDeleteTabs(_:)), a11yId: AccessibilityIdentifiers.TabTray.closeAllTabsButton, - a11yLabel: .AppMenu.Toolbar.TabTrayDeleteMenuButtonAccessibilityLabel) + a11yLabel: .LegacyAppMenu.Toolbar.TabTrayDeleteMenuButtonAccessibilityLabel) }() private lazy var newTabButtonIphone: UIBarButtonItem = { diff --git a/firefox-ios/Client/Frontend/Browser/Tabs/Middleware/TabManagerMiddleware.swift b/firefox-ios/Client/Frontend/Browser/Tabs/Middleware/TabManagerMiddleware.swift index d1471cc00fa0..cc25c054cd41 100644 --- a/firefox-ios/Client/Frontend/Browser/Tabs/Middleware/TabManagerMiddleware.swift +++ b/firefox-ios/Client/Frontend/Browser/Tabs/Middleware/TabManagerMiddleware.swift @@ -509,10 +509,8 @@ class TabManagerMiddleware { let windowManager: WindowManager = AppContainer.shared.resolve() guard uuid != .unavailable else { assertionFailure() - logger.log("Unexpected or unavailable UUID for TabManager. Returning active window tab manager by default.", - level: .warning, - category: .tabs) - return windowManager.tabManager(for: windowManager.activeWindow) + logger.log("Unexpected or unavailable window UUID for requested TabManager.", level: .fatal, category: .tabs) + return windowManager.allWindowTabManagers().first! } return windowManager.tabManager(for: uuid) diff --git a/firefox-ios/Client/Frontend/Browser/Tabs/State/TabTrayPanelType.swift b/firefox-ios/Client/Frontend/Browser/Tabs/State/TabTrayPanelType.swift index baa0dd1c92eb..cf9063d20d64 100644 --- a/firefox-ios/Client/Frontend/Browser/Tabs/State/TabTrayPanelType.swift +++ b/firefox-ios/Client/Frontend/Browser/Tabs/State/TabTrayPanelType.swift @@ -17,7 +17,7 @@ enum TabTrayPanelType: Int, CaseIterable { case .privateTabs: return .TabTrayPrivateBrowsingTitle case .syncedTabs: - return .AppMenu.AppMenuSyncedTabsTitleString + return .LegacyAppMenu.AppMenuSyncedTabsTitleString } } diff --git a/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabDisplayPanel.swift b/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabDisplayPanel.swift index baa0caeef274..f9dba00db908 100644 --- a/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabDisplayPanel.swift +++ b/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabDisplayPanel.swift @@ -206,6 +206,7 @@ class TabDisplayPanel: UIViewController, func didTapLearnMore(urlRequest: URLRequest) { let action = TabPanelViewAction(panelType: panelType, + urlRequest: urlRequest, windowUUID: windowUUID, actionType: TabPanelViewActionType.learnMorePrivateMode) store.dispatch(action) diff --git a/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabTrayViewController.swift b/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabTrayViewController.swift index 9c55d8d04511..9720eb712e6a 100644 --- a/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabTrayViewController.swift +++ b/firefox-ios/Client/Frontend/Browser/Tabs/Views/TabTrayViewController.swift @@ -108,7 +108,7 @@ class TabTrayViewController: UIViewController, return createButtonItem(imageName: StandardImageIdentifiers.Large.delete, action: #selector(deleteTabsButtonTapped), a11yId: AccessibilityIdentifiers.TabTray.closeAllTabsButton, - a11yLabel: .AppMenu.Toolbar.TabTrayDeleteMenuButtonAccessibilityLabel) + a11yLabel: .LegacyAppMenu.Toolbar.TabTrayDeleteMenuButtonAccessibilityLabel) }() private lazy var newTabButton: UIBarButtonItem = { @@ -481,7 +481,7 @@ class TabTrayViewController: UIViewController, private func showCloseAllConfirmation() { let controller = AlertController(title: nil, message: nil, preferredStyle: .actionSheet) - controller.addAction(UIAlertAction(title: .AppMenu.AppMenuCloseAllTabsTitleString, + controller.addAction(UIAlertAction(title: .LegacyAppMenu.AppMenuCloseAllTabsTitleString, style: .default, handler: { _ in self.confirmCloseAll() }), accessibilityIdentifier: AccessibilityIdentifiers.TabTray.deleteCloseAllButton) diff --git a/firefox-ios/Client/Frontend/Browser/ToastType.swift b/firefox-ios/Client/Frontend/Browser/ToastType.swift index 5dec291b0531..a00861939aff 100644 --- a/firefox-ios/Client/Frontend/Browser/ToastType.swift +++ b/firefox-ios/Client/Frontend/Browser/ToastType.swift @@ -23,9 +23,9 @@ enum ToastType: Equatable { .TabsTray.CloseTabsToast.Title, tabsCount) case .copyURL: - return .AppMenu.AppMenuCopyURLConfirmMessage + return .LegacyAppMenu.AppMenuCopyURLConfirmMessage case .addBookmark: - return .AppMenu.AddBookmarkConfirmMessage + return .LegacyAppMenu.AddBookmarkConfirmMessage } } diff --git a/firefox-ios/Client/Frontend/Browser/Toolbars/AddressToolbarContainer.swift b/firefox-ios/Client/Frontend/Browser/Toolbars/AddressToolbarContainer.swift index 1b10f11047a7..71db289bb6b5 100644 --- a/firefox-ios/Client/Frontend/Browser/Toolbars/AddressToolbarContainer.swift +++ b/firefox-ios/Client/Frontend/Browser/Toolbars/AddressToolbarContainer.swift @@ -11,7 +11,10 @@ protocol AddressToolbarContainerDelegate: AnyObject { func searchSuggestions(searchTerm: String) func openBrowser(searchTerm: String) func openSuggestions(searchTerm: String) + func configureContextualHint(for button: UIButton) func addressToolbarContainerAccessibilityActions() -> [UIAccessibilityCustomAction]? + func addressToolbarDidEnterOverlayMode(_ view: UIView) + func addressToolbar(_ view: UIView, didLeaveOverlayModeForReason: URLBarLeaveOverlayModeReason) } final class AddressToolbarContainer: UIView, @@ -20,7 +23,9 @@ final class AddressToolbarContainer: UIView, AlphaDimmable, StoreSubscriber, AddressToolbarDelegate, - MenuHelperURLBarInterface { + MenuHelperURLBarInterface, + Autocompletable, + URLBarViewProtocol { private enum UX { static let compactLeadingEdgeEditing: CGFloat = 8 static let compactLeadingEdgeDisplay: CGFloat = 16 @@ -39,8 +44,15 @@ final class AddressToolbarContainer: UIView, private(set) weak var delegate: AddressToolbarContainerDelegate? private var toolbar: BrowserAddressToolbar { - let isCompact = traitCollection.horizontalSizeClass == .compact - return isCompact ? compactToolbar : regularToolbar + return shouldDisplayCompact ? compactToolbar : regularToolbar + } + + private var shouldDisplayCompact: Bool { + guard let model else { + return traitCollection.horizontalSizeClass == .compact + } + + return model.shouldDisplayCompact } private var isTransitioning = false { @@ -69,7 +81,7 @@ final class AddressToolbarContainer: UIView, window: windowUUID) else { return nil } - let isCompact = traitCollection.horizontalSizeClass == .compact + let isCompact = shouldDisplayCompact let isHomepage = toolbarState.addressToolbar.url == nil let isEditing = toolbarState.addressToolbar.isEditing @@ -89,7 +101,7 @@ final class AddressToolbarContainer: UIView, window: windowUUID) else { return nil } - let isCompact = traitCollection.horizontalSizeClass == .compact + let isCompact = shouldDisplayCompact let isHomepage = toolbarState.addressToolbar.url == nil let isEditing = toolbarState.addressToolbar.isEditing @@ -102,6 +114,10 @@ final class AddressToolbarContainer: UIView, } } + /// Overlay mode is the state where the lock/reader icons are hidden, the home panels are shown, + /// and the Cancel button is visible (allowing the user to leave overlay mode). + var inOverlayMode = false + override init(frame: CGRect) { super.init(frame: .zero) setupLayout() @@ -141,12 +157,6 @@ final class AddressToolbarContainer: UIView, return toolbar.resignFirstResponder() } - // MARK: View Transitions - override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { - super.traitCollectionDidChange(previousTraitCollection) - adjustLayout() - } - // MARK: - Redux func subscribeToRedux() { @@ -205,7 +215,14 @@ final class AddressToolbarContainer: UIView, leadingSpace: leadingToolbarSpace, trailingSpace: trailingToolbarSpace) - _ = toolbarState.addressToolbar.isEditing ? becomeFirstResponder() : resignFirstResponder() + // the layout (compact/regular) that should be displayed is driven by the state + adjustLayout() + + // Dismiss overlay mode when not editing to fix overlay mode staying open + // on iPad when switching tabs using top tabs + if !toolbarState.addressToolbar.isEditing { + leaveOverlayMode(reason: .cancelled, shouldCancelLoading: false) + } } } @@ -249,10 +266,16 @@ final class AddressToolbarContainer: UIView, func applyTheme(theme: Theme) { compactToolbar.applyTheme(theme: theme) regularToolbar.applyTheme(theme: theme) + + let isPrivateMode = model?.isPrivateMode ?? false + let gradientStartColor = isPrivateMode ? theme.colors.borderAccentPrivate : theme.colors.borderAccent + let gradientMiddleColor = isPrivateMode ? nil : theme.colors.iconAccentPink + let gradientEndColor = isPrivateMode ? theme.colors.borderAccentPrivate : theme.colors.iconAccentYellow + progressBar.setGradientColors( - startColor: theme.colors.borderAccent, - middleColor: theme.colors.iconAccentPink, - endColor: theme.colors.iconAccentYellow + startColor: gradientStartColor, + middleColor: gradientMiddleColor, + endColor: gradientEndColor ) } @@ -279,6 +302,12 @@ final class AddressToolbarContainer: UIView, delegate?.addressToolbarContainerAccessibilityActions() } + func configureContextualHint(_ addressToolbar: BrowserAddressToolbar, for button: UIButton) { + if addressToolbar == toolbar { + delegate?.configureContextualHint(for: button) + } + } + // MARK: - MenuHelperURLBarInterface override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { if action == MenuHelperURLBarModel.selectorPasteAndGo { @@ -292,4 +321,31 @@ final class AddressToolbarContainer: UIView, guard let pasteboardContents = UIPasteboard.general.string else { return } delegate?.openBrowser(searchTerm: pasteboardContents) } + + // MARK: - Autocompletable + func setAutocompleteSuggestion(_ suggestion: String?) { + toolbar.setAutocompleteSuggestion(suggestion) + } + + // MARK: - Overlay Mode + func enterOverlayMode(_ locationText: String?, pasted: Bool, search: Bool) { + guard let windowUUID else { return } + inOverlayMode = true + delegate?.addressToolbarDidEnterOverlayMode(self) + + if pasted { + let action = ToolbarAction( + searchTerm: locationText, + windowUUID: windowUUID, + actionType: ToolbarActionType.didPasteSearchTerm + ) + store.dispatch(action) + } + } + + func leaveOverlayMode(reason: URLBarLeaveOverlayModeReason, shouldCancelLoading cancel: Bool) { + _ = toolbar.resignFirstResponder() + inOverlayMode = false + delegate?.addressToolbar(self, didLeaveOverlayModeForReason: reason) + } } diff --git a/firefox-ios/Client/Frontend/Browser/Toolbars/Autocompletable.swift b/firefox-ios/Client/Frontend/Browser/Toolbars/Autocompletable.swift new file mode 100644 index 000000000000..5a7721af3dd2 --- /dev/null +++ b/firefox-ios/Client/Frontend/Browser/Toolbars/Autocompletable.swift @@ -0,0 +1,9 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation + +protocol Autocompletable: UIView { + func setAutocompleteSuggestion(_ suggestion: String?) +} diff --git a/firefox-ios/Client/Frontend/Browser/Toolbars/Models/AddressToolbarContainerModel.swift b/firefox-ios/Client/Frontend/Browser/Toolbars/Models/AddressToolbarContainerModel.swift index 018cc1b05f07..c0d3b7cf6071 100644 --- a/firefox-ios/Client/Frontend/Browser/Toolbars/Models/AddressToolbarContainerModel.swift +++ b/firefox-ios/Client/Frontend/Browser/Toolbars/Models/AddressToolbarContainerModel.swift @@ -14,12 +14,19 @@ class AddressToolbarContainerModel: Equatable { let borderPosition: AddressToolbarBorderPosition? let searchEngineImage: UIImage? let searchEngines: SearchEngines - let lockIconImageName: String + let lockIconImageName: String? let url: URL? + let searchTerm: String? + let isEditing: Bool + let isPrivateMode: Bool + let shouldSelectSearchTerm: Bool + let shouldDisplayCompact: Bool let windowUUID: UUID var addressToolbarState: AddressToolbarState { + let term = searchTerm ?? searchTermFromURL(url, searchEngines: searchEngines) + let locationViewState = LocationViewState( searchEngineImageViewA11yId: AccessibilityIdentifiers.Browser.AddressToolbar.searchEngine, searchEngineImageViewA11yLabel: .AddressToolbar.PrivacyAndSecuritySettingsA11yLabel, @@ -31,7 +38,9 @@ class AddressToolbarContainerModel: Equatable { searchEngineImage: searchEngineImage, lockIconImageName: lockIconImageName, url: url, - searchTerm: searchTermFromURL(url, searchEngines: searchEngines), + searchTerm: term, + isEditing: isEditing, + shouldSelectSearchTerm: shouldSelectSearchTerm, onTapLockIcon: { let action = ToolbarMiddlewareAction(buttonType: .trackingProtection, gestureType: .tap, @@ -67,6 +76,11 @@ class AddressToolbarContainerModel: Equatable { self.searchEngines = profile.searchEngines self.lockIconImageName = state.addressToolbar.lockIconImageName self.url = state.addressToolbar.url + self.searchTerm = state.addressToolbar.searchTerm + self.isEditing = state.addressToolbar.isEditing + self.isPrivateMode = state.isPrivateMode + self.shouldSelectSearchTerm = state.addressToolbar.shouldSelectSearchTerm + self.shouldDisplayCompact = state.isShowingNavigationToolbar } func searchTermFromURL(_ url: URL?, searchEngines: SearchEngines) -> String? { @@ -85,11 +99,22 @@ class AddressToolbarContainerModel: Equatable { ToolbarElement( iconName: action.iconName, badgeImageName: action.badgeImageName, + maskImageName: action.maskImageName, numberOfTabs: action.numberOfTabs, isEnabled: action.isEnabled, + isFlippedForRTL: action.isFlippedForRTL, + shouldDisplayAsHighlighted: action.shouldDisplayAsHighlighted, + hasContextualHint: action.hasContextualHint, a11yLabel: action.a11yLabel, a11yHint: action.a11yHint, a11yId: action.a11yId, + a11yCustomActionName: action.a11yCustomActionName, + a11yCustomAction: action.a11yCustomActionName != nil ? { + let action = ToolbarMiddlewareAction(buttonType: action.actionType, + windowUUID: windowUUID, + actionType: ToolbarMiddlewareActionType.customA11yAction) + store.dispatch(action) + } : nil, onSelected: { button in let action = ToolbarMiddlewareAction(buttonType: action.actionType, buttonTapped: button, @@ -97,7 +122,7 @@ class AddressToolbarContainerModel: Equatable { windowUUID: windowUUID, actionType: ToolbarMiddlewareActionType.didTapButton) store.dispatch(action) - }, onLongPress: action.canPerformLongPressAction ? { button in + }, onLongPress: action.canPerformLongPressAction(isShowingTopTabs: true) ? { button in let action = ToolbarMiddlewareAction(buttonType: action.actionType, buttonTapped: button, gestureType: .longPress, @@ -113,10 +138,16 @@ class AddressToolbarContainerModel: Equatable { lhs.navigationActions == rhs.navigationActions && lhs.pageActions == rhs.pageActions && lhs.browserActions == rhs.browserActions && + lhs.borderPosition == rhs.borderPosition && lhs.searchEngineImage == rhs.searchEngineImage && - lhs.url == rhs.url && lhs.lockIconImageName == rhs.lockIconImageName && + lhs.url == rhs.url && + lhs.searchTerm == rhs.searchTerm && + lhs.isEditing == rhs.isEditing && + lhs.shouldSelectSearchTerm == rhs.shouldSelectSearchTerm && + lhs.shouldDisplayCompact == rhs.shouldDisplayCompact && + lhs.windowUUID == rhs.windowUUID } } diff --git a/firefox-ios/Client/Frontend/Browser/Toolbars/Models/NavigationToolbarContainerModel.swift b/firefox-ios/Client/Frontend/Browser/Toolbars/Models/NavigationToolbarContainerModel.swift index 8e3454b0be1c..20660a6fc04e 100644 --- a/firefox-ios/Client/Frontend/Browser/Toolbars/Models/NavigationToolbarContainerModel.swift +++ b/firefox-ios/Client/Frontend/Browser/Toolbars/Models/NavigationToolbarContainerModel.swift @@ -20,11 +20,22 @@ struct NavigationToolbarContainerModel: Equatable { ToolbarElement( iconName: action.iconName, badgeImageName: action.badgeImageName, + maskImageName: action.maskImageName, numberOfTabs: action.numberOfTabs, isEnabled: action.isEnabled, + isFlippedForRTL: action.isFlippedForRTL, + shouldDisplayAsHighlighted: action.shouldDisplayAsHighlighted, + hasContextualHint: action.hasContextualHint, a11yLabel: action.a11yLabel, a11yHint: action.a11yHint, a11yId: action.a11yId, + a11yCustomActionName: action.a11yCustomActionName, + a11yCustomAction: action.a11yCustomActionName != nil ? { + let action = ToolbarMiddlewareAction(buttonType: action.actionType, + windowUUID: windowUUID, + actionType: ToolbarMiddlewareActionType.customA11yAction) + store.dispatch(action) + } : nil, onSelected: { button in let action = ToolbarMiddlewareAction(buttonType: action.actionType, buttonTapped: button, @@ -32,7 +43,7 @@ struct NavigationToolbarContainerModel: Equatable { windowUUID: windowUUID, actionType: ToolbarMiddlewareActionType.didTapButton) store.dispatch(action) - }, onLongPress: action.canPerformLongPressAction ? { button in + }, onLongPress: action.canPerformLongPressAction(isShowingTopTabs: action.isShowingTopTabs) ? { button in let action = ToolbarMiddlewareAction(buttonType: action.actionType, buttonTapped: button, gestureType: .longPress, diff --git a/firefox-ios/Client/Frontend/Browser/Toolbars/NavigationToolbarContainer.swift b/firefox-ios/Client/Frontend/Browser/Toolbars/NavigationToolbarContainer.swift index 8320cb441693..9694f677fb00 100644 --- a/firefox-ios/Client/Frontend/Browser/Toolbars/NavigationToolbarContainer.swift +++ b/firefox-ios/Client/Frontend/Browser/Toolbars/NavigationToolbarContainer.swift @@ -7,6 +7,10 @@ import ToolbarKit import Redux import UIKit +protocol NavigationToolbarContainerDelegate: AnyObject { + func configureContextualHint(for: UIButton) +} + class NavigationToolbarContainer: UIView, ThemeApplicable, StoreSubscriber { typealias SubscriberStateType = ToolbarState @@ -19,6 +23,7 @@ class NavigationToolbarContainer: UIView, ThemeApplicable, StoreSubscriber { subscribeToRedux() } } + weak var toolbarDelegate: NavigationToolbarContainerDelegate? private var toolbarState: ToolbarState? private var model: NavigationToolbarContainerModel? @@ -69,7 +74,7 @@ class NavigationToolbarContainer: UIView, ThemeApplicable, StoreSubscriber { if self.model != model { self.model = model - toolbar.configure(state: model.navigationToolbarState) + toolbar.configure(state: model.navigationToolbarState, toolbarDelegate: self) } } @@ -93,3 +98,9 @@ class NavigationToolbarContainer: UIView, ThemeApplicable, StoreSubscriber { backgroundColor = theme.colors.layer1 } } + +extension NavigationToolbarContainer: BrowserNavigationToolbarDelegate { + func configureContextualHint(for button: UIButton) { + toolbarDelegate?.configureContextualHint(for: button) + } +} diff --git a/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/AddressBarState.swift b/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/AddressBarState.swift index 45be0c8c53f4..2a11dd205893 100644 --- a/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/AddressBarState.swift +++ b/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/AddressBarState.swift @@ -13,8 +13,10 @@ struct AddressBarState: StateType, Equatable { var browserActions: [ToolbarActionState] var borderPosition: AddressToolbarBorderPosition? var url: URL? - var lockIconImageName: String + var searchTerm: String? + var lockIconImageName: String? var isEditing: Bool + var shouldSelectSearchTerm: Bool var isLoading: Bool init(windowUUID: WindowUUID) { @@ -24,8 +26,10 @@ struct AddressBarState: StateType, Equatable { browserActions: [], borderPosition: nil, url: nil, + searchTerm: nil, lockIconImageName: "", isEditing: false, + shouldSelectSearchTerm: true, isLoading: false) } @@ -35,8 +39,10 @@ struct AddressBarState: StateType, Equatable { browserActions: [ToolbarActionState], borderPosition: AddressToolbarBorderPosition?, url: URL?, - lockIconImageName: String, + searchTerm: String? = nil, + lockIconImageName: String?, isEditing: Bool = false, + shouldSelectSearchTerm: Bool = true, isLoading: Bool = false) { self.windowUUID = windowUUID self.navigationActions = navigationActions @@ -44,8 +50,10 @@ struct AddressBarState: StateType, Equatable { self.browserActions = browserActions self.borderPosition = borderPosition self.url = url + self.searchTerm = searchTerm self.lockIconImageName = lockIconImageName self.isEditing = isEditing + self.shouldSelectSearchTerm = shouldSelectSearchTerm self.isLoading = isLoading } @@ -63,7 +71,9 @@ struct AddressBarState: StateType, Equatable { browserActions: model.browserActions ?? state.browserActions, borderPosition: model.borderPosition ?? state.borderPosition, url: model.url, - lockIconImageName: model.lockIconImageName ?? state.lockIconImageName + searchTerm: state.searchTerm, + lockIconImageName: model.lockIconImageName ?? state.lockIconImageName, + isEditing: state.isEditing ) case ToolbarActionType.numberOfTabsChanged: @@ -76,7 +86,22 @@ struct AddressBarState: StateType, Equatable { browserActions: addressToolbarModel.browserActions ?? state.browserActions, borderPosition: state.borderPosition, url: state.url, - lockIconImageName: state.lockIconImageName + searchTerm: state.searchTerm, + lockIconImageName: state.lockIconImageName, + isEditing: state.isEditing + ) + + case ToolbarActionType.readerModeStateChanged: + guard let addressToolbarModel = (action as? ToolbarAction)?.addressToolbarModel else { return state } + + return AddressBarState( + windowUUID: state.windowUUID, + navigationActions: state.navigationActions, + pageActions: addressToolbarModel.pageActions ?? state.pageActions, + browserActions: state.browserActions, + borderPosition: state.borderPosition, + url: addressToolbarModel.url, + lockIconImageName: addressToolbarModel.lockIconImageName ) case ToolbarActionType.addressToolbarActionsDidChange: @@ -89,6 +114,7 @@ struct AddressBarState: StateType, Equatable { browserActions: addressToolbarModel.browserActions ?? state.browserActions, borderPosition: state.borderPosition, url: state.url, + searchTerm: state.searchTerm, lockIconImageName: state.lockIconImageName, isEditing: addressToolbarModel.isEditing ?? state.isEditing ) @@ -103,6 +129,7 @@ struct AddressBarState: StateType, Equatable { browserActions: state.browserActions, borderPosition: state.borderPosition, url: addressToolbarModel.url, + searchTerm: nil, lockIconImageName: addressToolbarModel.lockIconImageName ?? state.lockIconImageName, isEditing: addressToolbarModel.isEditing ?? state.isEditing ) @@ -118,7 +145,9 @@ struct AddressBarState: StateType, Equatable { browserActions: state.browserActions, borderPosition: state.borderPosition, url: state.url, - lockIconImageName: state.lockIconImageName + searchTerm: nil, + lockIconImageName: state.lockIconImageName, + isEditing: state.isEditing ) case ToolbarActionType.showMenuWarningBadge: @@ -131,7 +160,9 @@ struct AddressBarState: StateType, Equatable { browserActions: browserActions ?? state.browserActions, borderPosition: state.borderPosition, url: state.url, - lockIconImageName: state.lockIconImageName + searchTerm: state.searchTerm, + lockIconImageName: state.lockIconImageName, + isEditing: state.isEditing ) case ToolbarActionType.scrollOffsetChanged, @@ -145,7 +176,56 @@ struct AddressBarState: StateType, Equatable { browserActions: state.browserActions, borderPosition: borderPosition, url: state.url, - lockIconImageName: state.lockIconImageName + searchTerm: state.searchTerm, + lockIconImageName: state.lockIconImageName, + isEditing: state.isEditing + ) + + case ToolbarActionType.didPasteSearchTerm: + guard let toolbarAction = action as? ToolbarAction else { return state } + + return AddressBarState( + windowUUID: state.windowUUID, + navigationActions: state.navigationActions, + pageActions: state.pageActions, + browserActions: state.browserActions, + borderPosition: state.borderPosition, + url: state.url, + searchTerm: toolbarAction.searchTerm, + lockIconImageName: state.lockIconImageName, + isEditing: true, + shouldSelectSearchTerm: false + ) + + case ToolbarActionType.didStartEditingUrl: + guard let addressToolbarModel = (action as? ToolbarAction)?.addressToolbarModel else { return state } + + return AddressBarState( + windowUUID: state.windowUUID, + navigationActions: addressToolbarModel.navigationActions ?? state.navigationActions, + pageActions: addressToolbarModel.pageActions ?? state.pageActions, + browserActions: addressToolbarModel.browserActions ?? state.browserActions, + borderPosition: state.borderPosition, + url: state.url, + searchTerm: state.searchTerm, + lockIconImageName: state.lockIconImageName, + isEditing: addressToolbarModel.isEditing ?? state.isEditing, + shouldSelectSearchTerm: state.shouldSelectSearchTerm + ) + + case ToolbarActionType.cancelEdit: + guard let addressToolbarModel = (action as? ToolbarAction)?.addressToolbarModel else { return state } + + return AddressBarState( + windowUUID: state.windowUUID, + navigationActions: addressToolbarModel.navigationActions ?? state.navigationActions, + pageActions: addressToolbarModel.pageActions ?? state.pageActions, + browserActions: addressToolbarModel.browserActions ?? state.browserActions, + borderPosition: state.borderPosition, + url: state.url, + searchTerm: nil, + lockIconImageName: state.lockIconImageName, + isEditing: addressToolbarModel.isEditing ?? state.isEditing ) default: @@ -156,7 +236,9 @@ struct AddressBarState: StateType, Equatable { browserActions: state.browserActions, borderPosition: state.borderPosition, url: state.url, - lockIconImageName: state.lockIconImageName + searchTerm: state.searchTerm, + lockIconImageName: state.lockIconImageName, + isEditing: state.isEditing ) } } diff --git a/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/ToolbarAction.swift b/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/ToolbarAction.swift index 155284516acd..f74182dd5ebc 100644 --- a/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/ToolbarAction.swift +++ b/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/ToolbarAction.swift @@ -6,30 +6,36 @@ import Common import Redux import ToolbarKit -class ToolbarAction: Action { +final class ToolbarAction: Action { let addressToolbarModel: AddressToolbarModel? let navigationToolbarModel: NavigationToolbarModel? let toolbarPosition: AddressToolbarPosition? let numberOfTabs: Int? let url: URL? + let searchTerm: String? let isPrivate: Bool? let badgeImageName: String? + let maskImageName: String? let isShowingNavigationToolbar: Bool? let isShowingTopTabs: Bool? let canGoBack: Bool? let canGoForward: Bool? + let readerModeState: ReaderModeState? init(addressToolbarModel: AddressToolbarModel? = nil, navigationToolbarModel: NavigationToolbarModel? = nil, toolbarPosition: AddressToolbarPosition? = nil, numberOfTabs: Int? = nil, url: URL? = nil, + searchTerm: String? = nil, isPrivate: Bool? = nil, badgeImageName: String? = nil, + maskImageName: String? = nil, isShowingNavigationToolbar: Bool? = nil, isShowingTopTabs: Bool? = nil, canGoBack: Bool? = nil, canGoForward: Bool? = nil, + readerModeState: ReaderModeState? = nil, windowUUID: WindowUUID, actionType: ActionType) { self.addressToolbarModel = addressToolbarModel @@ -37,12 +43,15 @@ class ToolbarAction: Action { self.toolbarPosition = toolbarPosition self.numberOfTabs = numberOfTabs self.url = url + self.searchTerm = searchTerm self.isPrivate = isPrivate self.badgeImageName = badgeImageName + self.maskImageName = maskImageName self.isShowingNavigationToolbar = isShowingNavigationToolbar self.isShowingTopTabs = isShowingTopTabs self.canGoBack = canGoBack self.canGoForward = canGoForward + self.readerModeState = readerModeState super.init(windowUUID: windowUUID, actionType: actionType) } } @@ -56,6 +65,10 @@ enum ToolbarActionType: ActionType { case scrollOffsetChanged case toolbarPositionChanged case showMenuWarningBadge + case didPasteSearchTerm + case didStartEditingUrl + case cancelEdit + case readerModeStateChanged } class ToolbarMiddlewareAction: Action { @@ -71,6 +84,8 @@ class ToolbarMiddlewareAction: Action { let canGoBack: Bool? let canGoForward: Bool? let badgeImageName: String? + let readerModeState: ReaderModeState? + let maskImageName: String? init(buttonType: ToolbarActionState.ActionType? = nil, buttonTapped: UIButton? = nil, @@ -84,6 +99,8 @@ class ToolbarMiddlewareAction: Action { canGoBack: Bool? = nil, canGoForward: Bool? = nil, badgeImageName: String? = nil, + readerModeState: ReaderModeState? = nil, + maskImageName: String? = nil, windowUUID: WindowUUID, actionType: ActionType) { self.buttonType = buttonType @@ -98,12 +115,15 @@ class ToolbarMiddlewareAction: Action { self.canGoBack = canGoBack self.canGoForward = canGoForward self.badgeImageName = badgeImageName + self.readerModeState = readerModeState + self.maskImageName = maskImageName super.init(windowUUID: windowUUID, actionType: actionType) } } enum ToolbarMiddlewareActionType: ActionType { case didTapButton + case customA11yAction case numberOfTabsChanged case urlDidChange case searchEngineDidChange @@ -114,4 +134,5 @@ enum ToolbarMiddlewareActionType: ActionType { case backButtonStateChanged case forwardButtonStateChanged case showMenuWarningBadge + case readerModeStateChanged } diff --git a/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/ToolbarActionState.swift b/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/ToolbarActionState.swift index 4d081b5e0c2a..de2a2c7c9c3b 100644 --- a/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/ToolbarActionState.swift +++ b/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/ToolbarActionState.swift @@ -4,7 +4,7 @@ import Foundation -struct ToolbarActionState: Equatable { +struct ToolbarActionState: Equatable, FeatureFlaggable { enum ActionType { case back case forward @@ -27,16 +27,24 @@ struct ToolbarActionState: Equatable { var actionType: ActionType var iconName: String var badgeImageName: String? + var maskImageName: String? var numberOfTabs: Int? + var isFlippedForRTL = false + var isShowingTopTabs: Bool? var isEnabled: Bool + var shouldDisplayAsHighlighted = false + var hasContextualHint = false var a11yLabel: String var a11yHint: String? var a11yId: String + var a11yCustomActionName: String? - var canPerformLongPressAction: Bool { + func canPerformLongPressAction(isShowingTopTabs: Bool?) -> Bool { return actionType == .back || actionType == .forward || - actionType == .tabs || - actionType == .reload + actionType == .reload || + actionType == .newTab || + actionType == .readerMode || + (actionType == .tabs && isShowingTopTabs == false) } } diff --git a/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/ToolbarMiddleware.swift b/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/ToolbarMiddleware.swift index faa3feb1dcd1..1b34df07334c 100644 --- a/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/ToolbarMiddleware.swift +++ b/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/ToolbarMiddleware.swift @@ -6,7 +6,7 @@ import Common import Redux import ToolbarKit -class ToolbarMiddleware: FeatureFlaggable { +final class ToolbarMiddleware: FeatureFlaggable { private let profile: Profile private let manager: ToolbarManager private let logger: Logger @@ -42,6 +42,15 @@ class ToolbarMiddleware: FeatureFlaggable { a11yHint: .TabLocationReloadAccessibilityHint, a11yId: AccessibilityIdentifiers.Toolbar.reloadButton) + private lazy var readerModeAction = ToolbarActionState( + actionType: .readerMode, + iconName: StandardImageIdentifiers.Large.readerView, + isEnabled: true, + a11yLabel: .TabLocationReaderModeAccessibilityLabel, + a11yHint: .TabLocationReloadAccessibilityHint, + a11yId: AccessibilityIdentifiers.Toolbar.readerModeButton, + a11yCustomActionName: .TabLocationReaderModeAddToReadingListAccessibilityLabel) + lazy var qrCodeScanAction = ToolbarActionState( actionType: .qrCode, iconName: StandardImageIdentifiers.Large.qrCode, @@ -52,6 +61,7 @@ class ToolbarMiddleware: FeatureFlaggable { lazy var cancelEditAction = ToolbarActionState( actionType: .cancelEdit, iconName: StandardImageIdentifiers.Large.chevronLeft, + isFlippedForRTL: true, isEnabled: true, a11yLabel: AccessibilityIdentifiers.GeneralizedIdentifiers.back, a11yId: AccessibilityIdentifiers.Browser.UrlBar.cancelButton) @@ -72,7 +82,7 @@ class ToolbarMiddleware: FeatureFlaggable { lazy var shareAction = ToolbarActionState( actionType: .share, - iconName: StandardImageIdentifiers.Large.shareApple, + iconName: StandardImageIdentifiers.Large.share, isEnabled: true, a11yLabel: .TabLocationShareAccessibilityLabel, a11yId: AccessibilityIdentifiers.Toolbar.shareButton) @@ -84,6 +94,14 @@ class ToolbarMiddleware: FeatureFlaggable { a11yLabel: .TabToolbarSearchAccessibilityLabel, a11yId: AccessibilityIdentifiers.Toolbar.searchButton) + lazy var dataClearanceAction = ToolbarActionState( + actionType: .dataClearance, + iconName: StandardImageIdentifiers.Large.dataClearance, + isEnabled: true, + hasContextualHint: true, + a11yLabel: .TabToolbarDataClearanceAccessibilityLabel, + a11yId: AccessibilityIdentifiers.Toolbar.fireButton) + private func resolveGeneralBrowserMiddlewareActions(action: GeneralBrowserMiddlewareAction, state: AppState) { let uuid = action.windowUUID @@ -115,20 +133,34 @@ class ToolbarMiddleware: FeatureFlaggable { private func resolveToolbarMiddlewareActions(action: ToolbarMiddlewareAction, state: AppState) { switch action.actionType { + case ToolbarMiddlewareActionType.customA11yAction: + resolveToolbarMiddlewareCustomA11yActions(action: action, state: state) + case ToolbarMiddlewareActionType.didTapButton: resolveToolbarMiddlewareButtonTapActions(action: action, state: state) case ToolbarMiddlewareActionType.urlDidChange: updateUrlAndActions(action: action, state: state) + case ToolbarMiddlewareActionType.readerModeStateChanged: + updateReaderModeState(action: action, state: state) + case ToolbarMiddlewareActionType.didStartEditingUrl: - updateAddressToolbarNavigationActions(action: action, state: state, isEditing: true) + updateAddressToolbarNavigationActions(action: action, + state: state, + isEditing: true, + dispatchActionType: ToolbarActionType.didStartEditingUrl) - case ToolbarMiddlewareActionType.cancelEdit, - ToolbarMiddlewareActionType.websiteLoadingStateDidChange, + case ToolbarMiddlewareActionType.websiteLoadingStateDidChange, ToolbarMiddlewareActionType.searchEngineDidChange: updateAddressToolbarNavigationActions(action: action, state: state, isEditing: false) + case ToolbarMiddlewareActionType.cancelEdit: + updateAddressToolbarNavigationActions(action: action, + state: state, + isEditing: false, + dispatchActionType: ToolbarActionType.cancelEdit) + case ToolbarMiddlewareActionType.traitCollectionDidChange: updateAddressToolbarNavigationActions(action: action, state: state) @@ -158,6 +190,16 @@ class ToolbarMiddleware: FeatureFlaggable { } } + func resolveToolbarMiddlewareCustomA11yActions(action: ToolbarMiddlewareAction, state: AppState) { + switch action.buttonType { + case .readerMode: + let action = GeneralBrowserAction(windowUUID: action.windowUUID, + actionType: GeneralBrowserActionType.addToReadingListLongPressAction) + store.dispatch(action) + default: break + } + } + private func loadInitialAddressToolbarState(toolbarPosition: AddressToolbarPosition) -> AddressToolbarModel { let borderPosition = getAddressBorderPosition(toolbarPosition: toolbarPosition) @@ -226,6 +268,11 @@ class ToolbarMiddleware: FeatureFlaggable { actionType: ToolbarMiddlewareActionType.cancelEdit) store.dispatch(action) + case .readerMode: + let action = GeneralBrowserAction(windowUUID: action.windowUUID, + actionType: GeneralBrowserActionType.showReaderMode) + store.dispatch(action) + case .reload: let action = GeneralBrowserAction(windowUUID: action.windowUUID, actionType: GeneralBrowserActionType.reloadWebsite) @@ -246,6 +293,10 @@ class ToolbarMiddleware: FeatureFlaggable { TelemetryWrapper.recordEvent(category: .action, method: .tap, object: .startSearchButton) updateAddressToolbarNavigationActions(action: action, state: state, isEditing: true) + case .dataClearance: + let action = GeneralBrowserAction(windowUUID: action.windowUUID, + actionType: GeneralBrowserActionType.clearData) + store.dispatch(action) default: break } @@ -270,6 +321,14 @@ class ToolbarMiddleware: FeatureFlaggable { windowUUID: action.windowUUID, actionType: GeneralBrowserActionType.showReloadLongPressAction) store.dispatch(action) + case .newTab: + let action = GeneralBrowserAction(windowUUID: action.windowUUID, + actionType: GeneralBrowserActionType.showNewTabLongPressActions) + store.dispatch(action) + case .readerMode: + let action = GeneralBrowserAction(windowUUID: action.windowUUID, + actionType: GeneralBrowserActionType.addToReadingListLongPressAction) + store.dispatch(action) default: break } @@ -350,11 +409,29 @@ class ToolbarMiddleware: FeatureFlaggable { isShowingNavigationToolbar: action.isShowingNavigationToolbar, canGoBack: action.canGoBack, canGoForward: action.canGoForward, + readerModeState: action.readerModeState, windowUUID: action.windowUUID, actionType: ToolbarActionType.urlDidChange) store.dispatch(toolbarAction) } + private func updateReaderModeState(action: ToolbarMiddlewareAction, + state: AppState) { + guard let readerModeState = action.readerModeState, + let addressToolbarModel = generateAddressToolbarActions( + action: action, + state: state, + lockIconImageName: action.lockIconImageName, + isEditing: false + ) else { return } + + let toolbarAction = ToolbarAction(addressToolbarModel: addressToolbarModel, + readerModeState: readerModeState, + windowUUID: action.windowUUID, + actionType: ToolbarActionType.readerModeStateChanged) + store.dispatch(toolbarAction) + } + private func updateNumberOfTabs(action: ToolbarMiddlewareAction, state: AppState) { guard let numberOfTabs = action.numberOfTabs, @@ -385,6 +462,7 @@ class ToolbarMiddleware: FeatureFlaggable { let toolbarAction = ToolbarAction(addressToolbarModel: addressToolbarModel, navigationToolbarModel: navToolbarModel, badgeImageName: action.badgeImageName, + maskImageName: action.maskImageName, windowUUID: action.windowUUID, actionType: ToolbarActionType.showMenuWarningBadge) store.dispatch(toolbarAction) @@ -432,10 +510,11 @@ class ToolbarMiddleware: FeatureFlaggable { let numberOfTabs = action.numberOfTabs ?? toolbarState.numberOfTabs let isShowMenuWarningAction = action.actionType as? ToolbarMiddlewareActionType == .showMenuWarningBadge - let menuBadgeImageName = isShowMenuWarningAction ? action.badgeImageName : toolbarState.menuWarningBadge + let menuBadgeImageName = isShowMenuWarningAction ? action.badgeImageName : toolbarState.badgeImageName + let maskImageName = isShowMenuWarningAction ? action.maskImageName : toolbarState.maskImageName - actions.append(contentsOf: [tabsAction(numberOfTabs: numberOfTabs), - menuAction(badgeImageName: menuBadgeImageName)]) + actions.append(contentsOf: [tabsAction(numberOfTabs: numberOfTabs, isPrivateMode: toolbarState.isPrivateMode), + menuAction(badgeImageName: menuBadgeImageName, maskImageName: maskImageName)]) return actions } @@ -470,6 +549,9 @@ class ToolbarMiddleware: FeatureFlaggable { let isForwardButtonEnabled = canGoForward actions.append(backAction(enabled: isBackButtonEnabled)) actions.append(forwardAction(enabled: isForwardButtonEnabled)) + if canShowDataClearanceAction(isPrivate: toolbarState.isPrivateMode) { + actions.append(dataClearanceAction) + } } return actions @@ -477,24 +559,28 @@ class ToolbarMiddleware: FeatureFlaggable { private func addressToolbarPageActions( action: ToolbarMiddlewareAction, - state: AppState, + toolbarState: borrowing ToolbarState, isEditing: Bool ) -> [ToolbarActionState] { var actions = [ToolbarActionState]() - guard let toolbarState = state.screenState(ToolbarState.self, - for: .toolbar, - window: action.windowUUID) - else { return actions } - let isUrlChangeAction = action.actionType as? ToolbarMiddlewareActionType == .urlDidChange - let url = isUrlChangeAction ? action.url : toolbarState.addressToolbar.url + let isReaderModeAction = action.actionType as? ToolbarMiddlewareActionType == .readerModeStateChanged + let readerModeState = isReaderModeAction ? action.readerModeState : toolbarState.readerModeState + let url = (isUrlChangeAction || isReaderModeAction) ? action.url : toolbarState.addressToolbar.url + readerModeAction.shouldDisplayAsHighlighted = readerModeState == .active guard url != nil, !isEditing else { // On homepage we only show the QR code button return [qrCodeScanAction] } + switch readerModeState { + case .active, .available: + actions.append(readerModeAction) + default: break + } + actions.append(shareAction) let isLoadingChangeAction = action.actionType as? ToolbarMiddlewareActionType == .websiteLoadingStateDidChange @@ -509,9 +595,11 @@ class ToolbarMiddleware: FeatureFlaggable { return actions } - private func updateAddressToolbarNavigationActions(action: ToolbarMiddlewareAction, - state: AppState, - isEditing: Bool? = nil) { + private func updateAddressToolbarNavigationActions( + action: ToolbarMiddlewareAction, + state: AppState, + isEditing: Bool? = nil, + dispatchActionType: ToolbarActionType = ToolbarActionType.addressToolbarActionsDidChange) { guard let toolbarState = state.screenState(ToolbarState.self, for: .toolbar, window: action.windowUUID), let addressToolbarModel = generateAddressToolbarActions(action: action, state: state, @@ -521,7 +609,7 @@ class ToolbarMiddleware: FeatureFlaggable { let toolbarAction = ToolbarAction(addressToolbarModel: addressToolbarModel, isShowingTopTabs: action.isShowingTopTabs ?? toolbarState.isShowingTopTabs, windowUUID: action.windowUUID, - actionType: ToolbarActionType.addressToolbarActionsDidChange) + actionType: dispatchActionType) store.dispatch(toolbarAction) } @@ -539,10 +627,11 @@ class ToolbarMiddleware: FeatureFlaggable { action: action, state: state, isEditing: editing) - let pageActions = addressToolbarPageActions(action: action, state: state, isEditing: editing) + let pageActions = addressToolbarPageActions(action: action, toolbarState: toolbarState, isEditing: editing) let browserActions = addressToolbarBrowserActions(action: action, state: state) let isUrlChangeAction = action.actionType as? ToolbarMiddlewareActionType == .urlDidChange - let url = isUrlChangeAction ? action.url : toolbarState.addressToolbar.url + let isReaderModeAction = action.actionType as? ToolbarMiddlewareActionType == .readerModeStateChanged + let url = (isUrlChangeAction || isReaderModeAction) ? action.url : toolbarState.addressToolbar.url let addressToolbarModel = AddressToolbarModel( navigationActions: navigationActions, @@ -568,24 +657,26 @@ class ToolbarMiddleware: FeatureFlaggable { let isUrlChangeAction = action.actionType as? ToolbarMiddlewareActionType == .urlDidChange let url = isUrlChangeAction ? action.url : toolbarState.addressToolbar.url - let isNewTabEnabled = featureFlags.isFeatureEnabled(.toolbarOneTapNewTab, checking: .buildOnly) - let middleActionDefault = isNewTabEnabled ? newTabAction : homeAction - let middleActionHome = searchAction - let middleAction = url == nil ? middleActionHome : middleActionDefault + let middleAction = getMiddleButtonAction(url: url, isPrivateMode: toolbarState.isPrivateMode) + + let isShowingTopTabs = action.isShowingTopTabs ?? false let canGoBack = action.canGoBack ?? toolbarState.canGoBack let canGoForward = action.canGoForward ?? toolbarState.canGoForward let numberOfTabs = action.numberOfTabs ?? toolbarState.numberOfTabs let isShowMenuWarningAction = action.actionType as? ToolbarMiddlewareActionType == .showMenuWarningBadge - let menuBadgeImageName = isShowMenuWarningAction ? action.badgeImageName : toolbarState.menuWarningBadge + let menuBadgeImageName = isShowMenuWarningAction ? action.badgeImageName : toolbarState.badgeImageName + let maskImageName = isShowMenuWarningAction ? action.maskImageName : toolbarState.maskImageName let actions = [ backAction(enabled: canGoBack), forwardAction(enabled: canGoForward), middleAction, - tabsAction(numberOfTabs: numberOfTabs), - menuAction(badgeImageName: menuBadgeImageName) + tabsAction(numberOfTabs: numberOfTabs, + isPrivateMode: toolbarState.isPrivateMode, + isShowingTopTabs: isShowingTopTabs), + menuAction(badgeImageName: menuBadgeImageName, maskImageName: maskImageName) ] let displayBorder = shouldDisplayNavigationToolbarBorder(toolbarPosition: toolbarState.toolbarPosition) @@ -596,12 +687,24 @@ class ToolbarMiddleware: FeatureFlaggable { return navToolbarModel } + private func getMiddleButtonAction(url: URL?, isPrivateMode: Bool) -> ToolbarActionState { + let canShowDataClearanceAction = canShowDataClearanceAction(isPrivate: isPrivateMode) + let isNewTabEnabled = featureFlags.isFeatureEnabled(.toolbarOneTapNewTab, checking: .buildOnly) + let middleActionForWebpage = canShowDataClearanceAction ? + dataClearanceAction : isNewTabEnabled ? newTabAction : homeAction + let middleActionForHomepage = searchAction + let middleAction = url == nil ? middleActionForHomepage : middleActionForWebpage + + return middleAction + } + // MARK: - Helper private func backAction(enabled: Bool) -> ToolbarActionState { return ToolbarActionState( actionType: .back, iconName: StandardImageIdentifiers.Large.back, + isFlippedForRTL: true, isEnabled: enabled, a11yLabel: .TabToolbarBackAccessibilityLabel, a11yId: AccessibilityIdentifiers.Toolbar.backButton) @@ -611,28 +714,35 @@ class ToolbarMiddleware: FeatureFlaggable { return ToolbarActionState( actionType: .forward, iconName: StandardImageIdentifiers.Large.forward, + isFlippedForRTL: true, isEnabled: enabled, a11yLabel: .TabToolbarForwardAccessibilityLabel, a11yId: AccessibilityIdentifiers.Toolbar.forwardButton) } - private func tabsAction(numberOfTabs: Int = 1) -> ToolbarActionState { + private func tabsAction(numberOfTabs: Int = 1, + isPrivateMode: Bool = false, + isShowingTopTabs: Bool = false) -> ToolbarActionState { return ToolbarActionState( actionType: .tabs, iconName: StandardImageIdentifiers.Large.tab, + badgeImageName: isPrivateMode ? StandardImageIdentifiers.Medium.privateModeCircleFillPurple : nil, + maskImageName: isPrivateMode ? ImageIdentifiers.badgeMask : nil, numberOfTabs: numberOfTabs, + isShowingTopTabs: isShowingTopTabs, isEnabled: true, a11yLabel: .TabsButtonShowTabsAccessibilityLabel, a11yId: AccessibilityIdentifiers.Toolbar.tabsButton) } - private func menuAction(badgeImageName: String? = nil) -> ToolbarActionState { + private func menuAction(badgeImageName: String? = nil, maskImageName: String? = nil) -> ToolbarActionState { return ToolbarActionState( actionType: .menu, iconName: StandardImageIdentifiers.Large.appMenu, badgeImageName: badgeImageName, + maskImageName: maskImageName, isEnabled: true, - a11yLabel: .AppMenu.Toolbar.MenuButtonAccessibilityLabel, + a11yLabel: .LegacyAppMenu.Toolbar.MenuButtonAccessibilityLabel, a11yId: AccessibilityIdentifiers.Toolbar.settingsMenuButton) } @@ -645,4 +755,11 @@ class ToolbarMiddleware: FeatureFlaggable { private func shouldDisplayNavigationToolbarBorder(toolbarPosition: AddressToolbarPosition) -> Bool { return manager.shouldDisplayNavigationBorder(toolbarPosition: toolbarPosition) } + + private func canShowDataClearanceAction(isPrivate: Bool) -> Bool { + let isFeltPrivacyUIEnabled = featureFlags.isFeatureEnabled(.feltPrivacySimplifiedUI, checking: .buildOnly) + let isFeltPrivacyDeletionEnabled = featureFlags.isFeatureEnabled(.feltPrivacyFeltDeletion, checking: .buildOnly) + + return isPrivate && isFeltPrivacyUIEnabled && isFeltPrivacyDeletionEnabled + } } diff --git a/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/ToolbarState.swift b/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/ToolbarState.swift index 76fa594d4696..38c5a1440b1a 100644 --- a/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/ToolbarState.swift +++ b/firefox-ios/Client/Frontend/Browser/Toolbars/Redux/ToolbarState.swift @@ -14,7 +14,9 @@ struct ToolbarState: ScreenState, Equatable { var navigationToolbar: NavigationBarState let isShowingNavigationToolbar: Bool let isShowingTopTabs: Bool - let menuWarningBadge: String? + let readerModeState: ReaderModeState? + let badgeImageName: String? + let maskImageName: String? let canGoBack: Bool let canGoForward: Bool var numberOfTabs: Int @@ -36,7 +38,9 @@ struct ToolbarState: ScreenState, Equatable { navigationToolbar: toolbarState.navigationToolbar, isShowingNavigationToolbar: toolbarState.isShowingNavigationToolbar, isShowingTopTabs: toolbarState.isShowingTopTabs, - menuWarningBadge: toolbarState.menuWarningBadge, + readerModeState: toolbarState.readerModeState, + badgeImageName: toolbarState.badgeImageName, + maskImageName: toolbarState.maskImageName, canGoBack: toolbarState.canGoBack, canGoForward: toolbarState.canGoForward, numberOfTabs: toolbarState.numberOfTabs) @@ -51,7 +55,9 @@ struct ToolbarState: ScreenState, Equatable { navigationToolbar: NavigationBarState(windowUUID: windowUUID), isShowingNavigationToolbar: true, isShowingTopTabs: false, - menuWarningBadge: nil, + readerModeState: nil, + badgeImageName: nil, + maskImageName: nil, canGoBack: false, canGoForward: false, numberOfTabs: 1 @@ -66,7 +72,9 @@ struct ToolbarState: ScreenState, Equatable { navigationToolbar: NavigationBarState, isShowingNavigationToolbar: Bool, isShowingTopTabs: Bool, - menuWarningBadge: String?, + readerModeState: ReaderModeState?, + badgeImageName: String?, + maskImageName: String?, canGoBack: Bool, canGoForward: Bool, numberOfTabs: Int @@ -78,7 +86,9 @@ struct ToolbarState: ScreenState, Equatable { self.navigationToolbar = navigationToolbar self.isShowingNavigationToolbar = isShowingNavigationToolbar self.isShowingTopTabs = isShowingTopTabs - self.menuWarningBadge = menuWarningBadge + self.readerModeState = readerModeState + self.badgeImageName = badgeImageName + self.maskImageName = maskImageName self.canGoBack = canGoBack self.canGoForward = canGoForward self.numberOfTabs = numberOfTabs @@ -93,7 +103,10 @@ struct ToolbarState: ScreenState, Equatable { ToolbarActionType.addressToolbarActionsDidChange, ToolbarActionType.backForwardButtonStatesChanged, ToolbarActionType.scrollOffsetChanged, - ToolbarActionType.urlDidChange: + ToolbarActionType.urlDidChange, + ToolbarActionType.didPasteSearchTerm, + ToolbarActionType.didStartEditingUrl, + ToolbarActionType.cancelEdit: guard let toolbarAction = action as? ToolbarAction else { return state } return ToolbarState( windowUUID: state.windowUUID, @@ -103,7 +116,9 @@ struct ToolbarState: ScreenState, Equatable { navigationToolbar: NavigationBarState.reducer(state.navigationToolbar, toolbarAction), isShowingNavigationToolbar: toolbarAction.isShowingNavigationToolbar ?? state.isShowingNavigationToolbar, isShowingTopTabs: toolbarAction.isShowingTopTabs ?? state.isShowingTopTabs, - menuWarningBadge: state.menuWarningBadge, + readerModeState: state.readerModeState, + badgeImageName: state.badgeImageName, + maskImageName: state.maskImageName, canGoBack: toolbarAction.canGoBack ?? state.canGoBack, canGoForward: toolbarAction.canGoForward ?? state.canGoForward, numberOfTabs: state.numberOfTabs) @@ -118,7 +133,9 @@ struct ToolbarState: ScreenState, Equatable { navigationToolbar: NavigationBarState.reducer(state.navigationToolbar, toolbarAction), isShowingNavigationToolbar: toolbarAction.isShowingNavigationToolbar ?? state.isShowingNavigationToolbar, isShowingTopTabs: toolbarAction.isShowingTopTabs ?? state.isShowingTopTabs, - menuWarningBadge: toolbarAction.badgeImageName, + readerModeState: state.readerModeState, + badgeImageName: toolbarAction.badgeImageName, + maskImageName: toolbarAction.maskImageName, canGoBack: toolbarAction.canGoBack ?? state.canGoBack, canGoForward: toolbarAction.canGoForward ?? state.canGoForward, numberOfTabs: state.numberOfTabs) @@ -133,7 +150,9 @@ struct ToolbarState: ScreenState, Equatable { navigationToolbar: NavigationBarState.reducer(state.navigationToolbar, toolbarAction), isShowingNavigationToolbar: state.isShowingNavigationToolbar, isShowingTopTabs: state.isShowingTopTabs, - menuWarningBadge: state.menuWarningBadge, + readerModeState: state.readerModeState, + badgeImageName: state.badgeImageName, + maskImageName: state.maskImageName, canGoBack: state.canGoBack, canGoForward: state.canGoForward, numberOfTabs: toolbarAction.numberOfTabs ?? state.numberOfTabs) @@ -148,7 +167,9 @@ struct ToolbarState: ScreenState, Equatable { navigationToolbar: NavigationBarState.reducer(state.navigationToolbar, action), isShowingNavigationToolbar: state.isShowingNavigationToolbar, isShowingTopTabs: state.isShowingTopTabs, - menuWarningBadge: state.menuWarningBadge, + readerModeState: state.readerModeState, + badgeImageName: state.badgeImageName, + maskImageName: state.maskImageName, canGoBack: state.canGoBack, canGoForward: state.canGoForward, numberOfTabs: state.numberOfTabs) @@ -163,11 +184,30 @@ struct ToolbarState: ScreenState, Equatable { navigationToolbar: NavigationBarState.reducer(state.navigationToolbar, action), isShowingNavigationToolbar: state.isShowingNavigationToolbar, isShowingTopTabs: state.isShowingTopTabs, - menuWarningBadge: state.menuWarningBadge, + readerModeState: state.readerModeState, + badgeImageName: state.badgeImageName, + maskImageName: state.maskImageName, canGoBack: state.canGoBack, canGoForward: state.canGoForward, numberOfTabs: state.numberOfTabs) + case ToolbarActionType.readerModeStateChanged: + guard let toolbarAction = action as? ToolbarAction else { return state } + return ToolbarState( + windowUUID: state.windowUUID, + toolbarPosition: state.toolbarPosition, + isPrivateMode: state.isPrivateMode, + addressToolbar: AddressBarState.reducer(state.addressToolbar, toolbarAction), + navigationToolbar: NavigationBarState.reducer(state.navigationToolbar, toolbarAction), + isShowingNavigationToolbar: state.isShowingNavigationToolbar, + isShowingTopTabs: state.isShowingTopTabs, + readerModeState: toolbarAction.readerModeState, + badgeImageName: state.badgeImageName, + maskImageName: state.maskImageName, + canGoBack: state.canGoBack, + canGoForward: state.canGoForward, + numberOfTabs: state.numberOfTabs) + default: return ToolbarState( windowUUID: state.windowUUID, @@ -177,7 +217,9 @@ struct ToolbarState: ScreenState, Equatable { navigationToolbar: state.navigationToolbar, isShowingNavigationToolbar: state.isShowingNavigationToolbar, isShowingTopTabs: state.isShowingTopTabs, - menuWarningBadge: state.menuWarningBadge, + readerModeState: state.readerModeState, + badgeImageName: state.badgeImageName, + maskImageName: state.maskImageName, canGoBack: state.canGoBack, canGoForward: state.canGoForward, numberOfTabs: state.numberOfTabs) diff --git a/firefox-ios/Client/Frontend/Browser/TopTabsViewController.swift b/firefox-ios/Client/Frontend/Browser/TopTabsViewController.swift index de33d57d18a6..1c515424d9ae 100644 --- a/firefox-ios/Client/Frontend/Browser/TopTabsViewController.swift +++ b/firefox-ios/Client/Frontend/Browser/TopTabsViewController.swift @@ -24,6 +24,7 @@ struct TopTabsUX { protocol TopTabsDelegate: AnyObject { func topTabsDidPressTabs() func topTabsDidPressNewTab(_ isPrivate: Bool) + func topTabsDidLongPressNewTab(button: UIButton) func topTabsDidChangeTab() func topTabsDidPressPrivateMode() } @@ -70,6 +71,14 @@ class TopTabsViewController: UIViewController, Themeable, Notifiable, FeatureFla button.setImage(UIImage.templateImageNamed(StandardImageIdentifiers.Large.plus), for: .normal) button.semanticContentAttribute = .forceLeftToRight button.addTarget(self, action: #selector(TopTabsViewController.newTabTapped), for: .touchUpInside) + if self.featureFlags.isFeatureEnabled(.toolbarOneTapNewTab, checking: .buildOnly) && + self.featureFlags.isFeatureEnabled(.toolbarRefactor, checking: .buildOnly) { + let longPressRecognizer = UILongPressGestureRecognizer( + target: self, + action: #selector(TopTabsViewController.newTabLongPressed) + ) + button.addGestureRecognizer(longPressRecognizer) + } button.accessibilityIdentifier = AccessibilityIdentifiers.Toolbar.addNewTabButton button.accessibilityLabel = .AddTabAccessibilityLabel button.showsLargeContentViewer = true @@ -215,6 +224,13 @@ class TopTabsViewController: UIViewController, Themeable, Notifiable, FeatureFla delegate?.topTabsDidPressNewTab(self.topTabDisplayManager.isPrivate) } + @objc + func newTabLongPressed(_ gestureRecognizer: UILongPressGestureRecognizer) { + if gestureRecognizer.state == .began { + delegate?.topTabsDidLongPressNewTab(button: newTab) + } + } + @objc func togglePrivateModeTapped() { delegate?.topTabsDidPressPrivateMode() diff --git a/firefox-ios/Client/Frontend/Browser/ZoomPageBar.swift b/firefox-ios/Client/Frontend/Browser/ZoomPageBar.swift index 416535b8483f..bdd4fe9ddee5 100644 --- a/firefox-ios/Client/Frontend/Browser/ZoomPageBar.swift +++ b/firefox-ios/Client/Frontend/Browser/ZoomPageBar.swift @@ -65,7 +65,7 @@ final class ZoomPageBar: UIView, ThemeApplicable, AlphaDimmable { private lazy var zoomOutButton: UIButton = .build { button in self.configureButton(button, image: UIImage.templateImageNamed(StandardImageIdentifiers.Large.subtract), - accessibilityLabel: .AppMenu.ZoomPageDecreaseZoomAccessibilityLabel, + accessibilityLabel: .LegacyAppMenu.ZoomPageDecreaseZoomAccessibilityLabel, accessibilityIdentifier: AccessibilityIdentifiers.ZoomPageBar.zoomPageZoomOutButton) button.setContentHuggingPriority(.required, for: .horizontal) button.configuration = .plain() @@ -83,7 +83,7 @@ final class ZoomPageBar: UIView, ThemeApplicable, AlphaDimmable { private lazy var zoomInButton: UIButton = .build { button in self.configureButton(button, image: UIImage.templateImageNamed(StandardImageIdentifiers.Large.plus), - accessibilityLabel: .AppMenu.ZoomPageIncreaseZoomAccessibilityLabel, + accessibilityLabel: .LegacyAppMenu.ZoomPageIncreaseZoomAccessibilityLabel, accessibilityIdentifier: AccessibilityIdentifiers.ZoomPageBar.zoomPageZoomInButton) button.setContentHuggingPriority(.required, for: .horizontal) button.configuration = .plain() @@ -93,7 +93,7 @@ final class ZoomPageBar: UIView, ThemeApplicable, AlphaDimmable { private lazy var closeButton: UIButton = .build { button in self.configureButton(button, image: UIImage.templateImageNamed(StandardImageIdentifiers.Large.cross), - accessibilityLabel: .AppMenu.ZoomPageCloseAccessibilityLabel, + accessibilityLabel: .LegacyAppMenu.ZoomPageCloseAccessibilityLabel, accessibilityIdentifier: AccessibilityIdentifiers.FindInPage.findInPageCloseButton) } @@ -219,7 +219,7 @@ final class ZoomPageBar: UIView, ThemeApplicable, AlphaDimmable { zoomLevel.text = NumberFormatter.localizedString(from: NSNumber(value: tab.pageZoom), number: .percent) zoomLevel.isEnabled = tab.pageZoom == 1.0 ? false : true gestureRecognizer.isEnabled = !(tab.pageZoom == 1.0) - zoomLevel.accessibilityLabel = String(format: .AppMenu.ZoomPageCurrentZoomLevelAccessibilityLabel, + zoomLevel.accessibilityLabel = String(format: .LegacyAppMenu.ZoomPageCurrentZoomLevelAccessibilityLabel, zoomLevel.text ?? "") } diff --git a/firefox-ios/Client/Frontend/Home/HomepageContextMenuHelper.swift b/firefox-ios/Client/Frontend/Home/HomepageContextMenuHelper.swift index 3b18a9a62bf6..69b8f17d1987 100644 --- a/firefox-ios/Client/Frontend/Home/HomepageContextMenuHelper.swift +++ b/firefox-ios/Client/Frontend/Home/HomepageContextMenuHelper.swift @@ -243,7 +243,7 @@ class HomepageContextMenuHelper: HomepageContextMenuProtocol { /// - Returns: Share action private func getShareAction(site: Site, sourceView: UIView?) -> PhotonRowActions { return SingleActionViewModel(title: .ShareContextMenuTitle, - iconString: StandardImageIdentifiers.Large.shareApple, + iconString: StandardImageIdentifiers.Large.share, allowIconScaling: true, tapHandler: { _ in guard let url = URL(string: site.url, invalidCharacters: false) else { return } diff --git a/firefox-ios/Client/Frontend/Home/HomepageViewController.swift b/firefox-ios/Client/Frontend/Home/HomepageViewController.swift index a9cd29f7ba7c..282fd9fb4e0f 100644 --- a/firefox-ios/Client/Frontend/Home/HomepageViewController.swift +++ b/firefox-ios/Client/Frontend/Home/HomepageViewController.swift @@ -365,6 +365,10 @@ class HomepageViewController: private func dismissKeyboard() { if currentTab?.lastKnownUrl?.absoluteString.hasPrefix("internal://") ?? false { overlayManager.cancelEditing(shouldCancelLoading: false) + + let action = ToolbarMiddlewareAction(windowUUID: windowUUID, + actionType: ToolbarMiddlewareActionType.cancelEdit) + store.dispatch(action) } } @@ -381,6 +385,15 @@ class HomepageViewController: theme: theme) } + // Only dispatch action when user is in edit mode to avoid having the toolbar re-displayed + if featureFlags.isFeatureEnabled(.toolbarRefactor, checking: .buildOnly), + let toolbarState = store.state.screenState(ToolbarState.self, for: .toolbar, window: windowUUID), + toolbarState.addressToolbar.isEditing { + let action = ToolbarMiddlewareAction(windowUUID: windowUUID, + actionType: ToolbarMiddlewareActionType.cancelEdit) + store.dispatch(action) + } + let scrolledToTop = lastContentOffsetY > 0 && scrollView.contentOffset.y <= 0 let scrolledDown = lastContentOffsetY == 0 && scrollView.contentOffset.y > 0 @@ -412,12 +425,13 @@ class HomepageViewController: let viewController = WallpaperSelectorViewController(viewModel: viewModel, windowUUID: windowUUID) var bottomSheetViewModel = BottomSheetViewModel( closeButtonA11yLabel: .CloseButtonTitle, - closeButtonA11yIdentifier: - AccessibilityIdentifiers.FirefoxHomepage.OtherButtons.closeButton) + closeButtonA11yIdentifier: AccessibilityIdentifiers.FirefoxHomepage.OtherButtons.closeButton + ) bottomSheetViewModel.shouldDismissForTapOutside = false let bottomSheetVC = BottomSheetViewController( viewModel: bottomSheetViewModel, - childViewController: viewController + childViewController: viewController, + windowUUID: windowUUID ) self.present(bottomSheetVC, animated: false, completion: nil) diff --git a/firefox-ios/Client/Frontend/Home/JumpBackIn/Data/JumpBackInDataAdaptor.swift b/firefox-ios/Client/Frontend/Home/JumpBackIn/Data/JumpBackInDataAdaptor.swift index cf9c7f281794..f03e824e2160 100644 --- a/firefox-ios/Client/Frontend/Home/JumpBackIn/Data/JumpBackInDataAdaptor.swift +++ b/firefox-ios/Client/Frontend/Home/JumpBackIn/Data/JumpBackInDataAdaptor.swift @@ -9,7 +9,6 @@ import Common protocol JumpBackInDataAdaptor: Actor { func hasSyncedTabFeatureEnabled() -> Bool func getRecentTabData() -> [Tab] - func getGroupsData() -> [ASGroup]? func getSyncedTabData() -> JumpBackInSyncedTab? } @@ -76,10 +75,6 @@ actor JumpBackInDataAdaptorImplementation: JumpBackInDataAdaptor, FeatureFlaggab return recentTabs } - func getGroupsData() -> [ASGroup]? { - return recentGroups - } - func getSyncedTabData() -> JumpBackInSyncedTab? { return mostRecentSyncedTab } @@ -98,30 +93,22 @@ actor JumpBackInDataAdaptorImplementation: JumpBackInDataAdaptor, FeatureFlaggab private func updateTabsData() async { await withTaskGroup(of: Void.self) { group in group.addTask { - await self.setRecentTabs(recentTabs: await self.updateRecentTabs()) - await self.delegate?.didLoadNewData() + let recentTabs = await self.updateRecentTabs() + await self.setRecentTabs(recentTabs: recentTabs) } group.addTask { if let remoteTabs = await self.updateRemoteTabs() { await self.createMostRecentSyncedTab(from: remoteTabs) - await self.delegate?.didLoadNewData() } } - group.addTask { - await self.setRecentGroups(recentGroups: await self.updateGroupsData()) - await self.delegate?.didLoadNewData() - } } + delegate?.didLoadNewData() } private func setRecentTabs(recentTabs: [Tab]) { self.recentTabs = recentTabs } - private func setRecentGroups(recentGroups: [ASGroup]?) { - self.recentGroups = recentGroups - } - private func updateRecentTabs() async -> [Tab] { // Recent tabs need to be accessed from .main otherwise value isn't proper return await withCheckedContinuation { continuation in @@ -131,10 +118,6 @@ actor JumpBackInDataAdaptorImplementation: JumpBackInDataAdaptor, FeatureFlaggab } } - private func updateGroupsData() async -> [ASGroup]? { - return nil - } - // MARK: Synced tab data private func getHasSyncAccount() async -> Bool { diff --git a/firefox-ios/Client/Frontend/Home/JumpBackIn/JumpBackInViewModel.swift b/firefox-ios/Client/Frontend/Home/JumpBackIn/JumpBackInViewModel.swift index aeb8a4074ede..20caa39ffec1 100644 --- a/firefox-ios/Client/Frontend/Home/JumpBackIn/JumpBackInViewModel.swift +++ b/firefox-ios/Client/Frontend/Home/JumpBackIn/JumpBackInViewModel.swift @@ -449,15 +449,22 @@ extension JumpBackInViewModel: HomepageSectionHandler { extension JumpBackInViewModel: JumpBackInDelegate { func didLoadNewData() { Task { @MainActor in - self.recentTabs = await self.jumpBackInDataAdaptor.getRecentTabData() - self.recentSyncedTab = await self.jumpBackInDataAdaptor.getSyncedTabData() - self.isSyncTabFeatureEnabled = await self.jumpBackInDataAdaptor.hasSyncedTabFeatureEnabled() + await self.updateJumpBackInData() logger.log("JumpBack didLoadNewData and section shouldShow \(self.shouldShow)", level: .debug, category: .homepage) - guard self.isEnabled else { return } - - self.delegate?.reloadView() + reloadView() } } + + private func updateJumpBackInData() async { + self.recentTabs = await self.jumpBackInDataAdaptor.getRecentTabData() + self.recentSyncedTab = await self.jumpBackInDataAdaptor.getSyncedTabData() + self.isSyncTabFeatureEnabled = await self.jumpBackInDataAdaptor.hasSyncedTabFeatureEnabled() + } + + private func reloadView() { + guard self.isEnabled else { return } + self.delegate?.reloadView() + } } diff --git a/firefox-ios/Client/Frontend/Library/Bookmarks/BookmarksPanel.swift b/firefox-ios/Client/Frontend/Library/Bookmarks/BookmarksPanel.swift index ef7ed70c60ea..19dc9807ebd8 100644 --- a/firefox-ios/Client/Frontend/Library/Bookmarks/BookmarksPanel.swift +++ b/firefox-ios/Client/Frontend/Library/Bookmarks/BookmarksPanel.swift @@ -526,7 +526,7 @@ extension BookmarksPanel: LibraryPanelContextMenu { tapHandler: { _ in self.profile.pinnedSites.addPinnedTopSite(site).uponQueue(.main) { result in if result.isSuccess { - SimpleToast().showAlertWithText(.AppMenu.AddPinToShortcutsConfirmMessage, + SimpleToast().showAlertWithText(.LegacyAppMenu.AddPinToShortcutsConfirmMessage, bottomContainer: self.view, theme: self.currentTheme()) } else { diff --git a/firefox-ios/Client/Frontend/Library/HistoryPanel/HistoryPanel.swift b/firefox-ios/Client/Frontend/Library/HistoryPanel/HistoryPanel.swift index 225fc360c533..3e7513f33f7d 100644 --- a/firefox-ios/Client/Frontend/Library/HistoryPanel/HistoryPanel.swift +++ b/firefox-ios/Client/Frontend/Library/HistoryPanel/HistoryPanel.swift @@ -884,7 +884,7 @@ extension HistoryPanel { func pinToTopSites(_ site: Site) { profile.pinnedSites.addPinnedTopSite(site).uponQueue(.main) { result in if result.isSuccess { - SimpleToast().showAlertWithText(.AppMenu.AddPinToShortcutsConfirmMessage, + SimpleToast().showAlertWithText(.LegacyAppMenu.AddPinToShortcutsConfirmMessage, bottomContainer: self.view, theme: self.currentTheme()) } diff --git a/firefox-ios/Client/Frontend/Library/LibraryPanelContextMenu.swift b/firefox-ios/Client/Frontend/Library/LibraryPanelContextMenu.swift index 0b740737a471..0466faefa729 100644 --- a/firefox-ios/Client/Frontend/Library/LibraryPanelContextMenu.swift +++ b/firefox-ios/Client/Frontend/Library/LibraryPanelContextMenu.swift @@ -111,7 +111,7 @@ extension LibraryPanelContextMenu { func getShareAction(site: Site, sourceView: UIView, delegate: LibraryPanelCoordinatorDelegate?) -> PhotonRowActions { return SingleActionViewModel( title: .ShareContextMenuTitle, - iconString: StandardImageIdentifiers.Large.shareApple) { _ in + iconString: StandardImageIdentifiers.Large.share) { _ in guard let siteURL = URL(string: site.url, invalidCharacters: false) else { return } delegate?.shareLibraryItem(url: siteURL, sourceView: sourceView) }.items diff --git a/firefox-ios/Client/Frontend/Library/LibraryPanelHelper.swift b/firefox-ios/Client/Frontend/Library/LibraryPanelHelper.swift index 2f268926ab5a..f1c4f120b15a 100644 --- a/firefox-ios/Client/Frontend/Library/LibraryPanelHelper.swift +++ b/firefox-ios/Client/Frontend/Library/LibraryPanelHelper.swift @@ -56,13 +56,13 @@ enum LibraryPanelType: Int, CaseIterable { var title: String { switch self { case .bookmarks: - return .AppMenu.AppMenuBookmarksTitleString + return .LegacyAppMenu.AppMenuBookmarksTitleString case .history: - return .AppMenu.AppMenuHistoryTitleString + return .LegacyAppMenu.AppMenuHistoryTitleString case .downloads: - return .AppMenu.AppMenuDownloadsTitleString + return .LegacyAppMenu.AppMenuDownloadsTitleString case .readingList: - return .AppMenu.AppMenuReadingListTitleString + return .LegacyAppMenu.AppMenuReadingListTitleString } } diff --git a/firefox-ios/Client/Frontend/Microsurvey/Survey/View/MicrosurveyViewController.swift b/firefox-ios/Client/Frontend/Microsurvey/Survey/View/MicrosurveyViewController.swift index 6f72f455f7d0..805d59e694af 100644 --- a/firefox-ios/Client/Frontend/Microsurvey/Survey/View/MicrosurveyViewController.swift +++ b/firefox-ios/Client/Frontend/Microsurvey/Survey/View/MicrosurveyViewController.swift @@ -223,6 +223,7 @@ final class MicrosurveyViewController: UIViewController, scrollContainer.addArrangedSubview(containerView) scrollContainer.addArrangedSubview(submitButton) scrollContainer.addArrangedSubview(privacyPolicyButton) + scrollContainer.accessibilityElements = [containerView, submitButton, privacyPolicyButton] scrollView.addSubview(scrollContainer) @@ -331,6 +332,7 @@ final class MicrosurveyViewController: UIViewController, tableView.removeFromSuperview() submitButton.removeFromSuperview() containerView.addSubview(confirmationView) + scrollContainer.accessibilityElements = [containerView, privacyPolicyButton] NSLayoutConstraint.activate( [ confirmationView.topAnchor.constraint(equalTo: containerView.topAnchor), diff --git a/firefox-ios/Client/Frontend/Onboarding/Protocols/OnboardingCardDelegate.swift b/firefox-ios/Client/Frontend/Onboarding/Protocols/OnboardingCardDelegate.swift index 4cad527d0a24..8e8ac1466fe9 100644 --- a/firefox-ios/Client/Frontend/Onboarding/Protocols/OnboardingCardDelegate.swift +++ b/firefox-ios/Client/Frontend/Onboarding/Protocols/OnboardingCardDelegate.swift @@ -110,12 +110,14 @@ extension OnboardingCardDelegate where Self: OnboardingViewControllerProtocol, var bottomSheetViewModel = BottomSheetViewModel( closeButtonA11yLabel: .CloseButtonTitle, closeButtonA11yIdentifier: - AccessibilityIdentifiers.Onboarding.bottomSheetCloseButton) + AccessibilityIdentifiers.Onboarding.bottomSheetCloseButton + ) bottomSheetViewModel.shouldDismissForTapOutside = true let bottomSheetVC = BottomSheetViewController( viewModel: bottomSheetViewModel, childViewController: instructionsVC, - usingDimmedBackground: true) + usingDimmedBackground: true, + windowUUID: windowUUID) instructionsVC.dismissDelegate = bottomSheetVC diff --git a/firefox-ios/Client/Frontend/PasswordManagement/LoginProvider.swift b/firefox-ios/Client/Frontend/PasswordManagement/LoginProvider.swift new file mode 100644 index 000000000000..f1c57d8b3c08 --- /dev/null +++ b/firefox-ios/Client/Frontend/PasswordManagement/LoginProvider.swift @@ -0,0 +1,16 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import MozillaAppServices +import Storage + +protocol LoginProvider: AnyObject { + func searchLoginsWithQuery( + _ query: String?, + completionHandler: @escaping (Result<[EncryptedLogin], Error>) -> Void) + func addLogin(login: LoginEntry, completionHandler: @escaping (Result) -> Void) +} + +extension RustLogins: LoginProvider {} diff --git a/firefox-ios/Client/Frontend/PasswordManagement/PasswordManagerListViewController.swift b/firefox-ios/Client/Frontend/PasswordManagement/PasswordManagerListViewController.swift index ce2b65d9ed96..f0cd216d0735 100644 --- a/firefox-ios/Client/Frontend/PasswordManagement/PasswordManagerListViewController.swift +++ b/firefox-ios/Client/Frontend/PasswordManagement/PasswordManagerListViewController.swift @@ -47,7 +47,8 @@ class PasswordManagerListViewController: SensitiveViewController, Themeable { self.viewModel = PasswordManagerViewModel( profile: profile, searchController: searchController, - theme: themeManager.getCurrentTheme(for: windowUUID) + theme: themeManager.getCurrentTheme(for: windowUUID), + loginProvider: profile.logins ) self.loginDataSource = LoginDataSource(viewModel: viewModel) self.themeManager = themeManager diff --git a/firefox-ios/Client/Frontend/PasswordManagement/PasswordManagerViewModel.swift b/firefox-ios/Client/Frontend/PasswordManagement/PasswordManagerViewModel.swift index 6cbd2fc1b45b..5d276a78a8a2 100644 --- a/firefox-ios/Client/Frontend/PasswordManagement/PasswordManagerViewModel.swift +++ b/firefox-ios/Client/Frontend/PasswordManagement/PasswordManagerViewModel.swift @@ -6,7 +6,6 @@ import Common import Foundation import Storage import Shared -import AuthenticationServices import struct MozillaAppServices.EncryptedLogin import struct MozillaAppServices.LoginEntry @@ -22,6 +21,7 @@ final class PasswordManagerViewModel { private(set) var isDuringSearchControllerDismiss = false private(set) var count = 0 private(set) var hasData = false + private let loginProvider: LoginProvider weak var searchController: UISearchController? weak var delegate: LoginViewModelDelegate? private(set) var titles = [Character]() @@ -46,10 +46,11 @@ final class PasswordManagerViewModel { var hasLoadedBreaches = false var theme: Theme - init(profile: Profile, searchController: UISearchController, theme: Theme) { + init(profile: Profile, searchController: UISearchController, theme: Theme, loginProvider: LoginProvider) { self.profile = profile self.searchController = searchController self.theme = theme + self.loginProvider = loginProvider } func loadLogins(_ query: String? = nil, loginDataSource: LoginDataSource) { @@ -78,7 +79,7 @@ final class PasswordManagerViewModel { /// Searches SQLite database for logins that match query. /// Wraps the SQLiteLogins method to allow us to cancel it from our end. func queryLogins(_ query: String, completion: @escaping ([EncryptedLogin]) -> Void) { - profile.logins.searchLoginsWithQuery(query) { result in + loginProvider.searchLoginsWithQuery(query) { result in ensureMainThread { switch result { case .success(let logins): @@ -155,7 +156,7 @@ final class PasswordManagerViewModel { } public func save(loginRecord: LoginEntry, completion: @escaping ((String?) -> Void)) { - profile.logins.addLogin(login: loginRecord, completionHandler: { result in + loginProvider.addLogin(login: loginRecord, completionHandler: { result in switch result { case .success(let encryptedLogin): self.sendLoginsSavedTelemetry() diff --git a/firefox-ios/Client/Frontend/Reader/ReadabilityService.swift b/firefox-ios/Client/Frontend/Reader/ReadabilityService.swift index 72c79906751e..6270ea50d73d 100644 --- a/firefox-ios/Client/Frontend/Reader/ReadabilityService.swift +++ b/firefox-ios/Client/Frontend/Reader/ReadabilityService.swift @@ -46,12 +46,11 @@ class ReadabilityOperation: Operation { DispatchQueue.main.async(execute: { () in let configuration = WKWebViewConfiguration() - // TODO: To resolve profile from DI container - let windowManager: WindowManager = AppContainer.shared.resolve() - let tab = Tab(profile: self.profile, configuration: configuration, windowUUID: windowManager.activeWindow) + let defaultUUID = windowManager.windows.first?.key ?? .unavailable + let tab = Tab(profile: self.profile, windowUUID: defaultUUID) self.tab = tab - tab.createWebview() + tab.createWebview(configuration: configuration) tab.navigationDelegate = self let readerMode = ReaderMode(tab: tab) diff --git a/firefox-ios/Client/Frontend/Settings/Main/AppSettingsTableViewController.swift b/firefox-ios/Client/Frontend/Settings/Main/AppSettingsTableViewController.swift index 5af35a7cbe1b..c8fe567791c8 100644 --- a/firefox-ios/Client/Frontend/Settings/Main/AppSettingsTableViewController.swift +++ b/firefox-ios/Client/Frontend/Settings/Main/AppSettingsTableViewController.swift @@ -284,7 +284,7 @@ class AppSettingsTableViewController: SettingsTableViewController, privacySettings.append(AutofillCreditCardSettings(settings: self, settingsDelegate: parentCoordinator)) } - let autofillAddressStatus = featureFlags.isFeatureEnabled(.addressAutofill, checking: .buildOnly) + let autofillAddressStatus = AddressLocaleFeatureValidator.isValidRegion() if autofillAddressStatus { privacySettings.append(AddressAutofillSetting(theme: themeManager.getCurrentTheme(for: windowUUID), profile: profile, @@ -367,7 +367,7 @@ class AppSettingsTableViewController: SettingsTableViewController, SentryIDSetting(settings: self, settingsDelegate: self), FasterInactiveTabs(settings: self, settingsDelegate: self), OpenFiftyTabsDebugOption(settings: self, settingsDelegate: self), - FirefoxSuggestSettings(settings: self, settingsDelegate: self) + FirefoxSuggestSettings(settings: self, settingsDelegate: self), ] #if MOZ_CHANNEL_BETA || MOZ_CHANNEL_FENNEC diff --git a/firefox-ios/Client/Frontend/Settings/Main/Debug/FeatureFlags/FeatureFlagsDebugViewController.swift b/firefox-ios/Client/Frontend/Settings/Main/Debug/FeatureFlags/FeatureFlagsDebugViewController.swift index b075398d0db8..28fdedd3a78d 100644 --- a/firefox-ios/Client/Frontend/Settings/Main/Debug/FeatureFlags/FeatureFlagsDebugViewController.swift +++ b/firefox-ios/Client/Frontend/Settings/Main/Debug/FeatureFlags/FeatureFlagsDebugViewController.swift @@ -34,7 +34,8 @@ final class FeatureFlagsDebugViewController: SettingsTableViewController, Featur UserDefaults.standard.set(nil, forKey: "\(GleanPlumbMessageStore.rootKey)\("homepage-microsurvey-message")") self?.reloadView() } - return SettingSection(title: nil, children: [microsurveySetting]) + let closeRemoteTabsSetting = getCloseRemoteTabSetting(theme) + return SettingSection(title: nil, children: [microsurveySetting, closeRemoteTabsSetting]) } private func generateFeatureFlagList() -> SettingSection { @@ -52,6 +53,20 @@ final class FeatureFlagsDebugViewController: SettingsTableViewController, Featur ) } + private func getCloseRemoteTabSetting(_ theme: Theme) -> FeatureFlagsBoolSetting { + FeatureFlagsBoolSetting( + with: .closeRemoteTabs, + titleText: NSAttributedString( + string: "Enable Close Remote Tabs", + attributes: [NSAttributedString.Key.foregroundColor: theme.colors.textPrimary]), + statusText: NSAttributedString( + string: "Toggle to enable closing tabs remotely feature", + attributes: [NSAttributedString.Key.foregroundColor: theme.colors.textPrimary]) + ) { [weak self] _ in + self?.reloadView() + } + } + private func reloadView() { self.settings = self.generateSettings() self.tableView.reloadData() diff --git a/firefox-ios/Client/Frontend/Settings/Main/Support/SendAnonymousUsageDataSetting.swift b/firefox-ios/Client/Frontend/Settings/Main/Support/SendAnonymousUsageDataSetting.swift index 0320de985f5e..d880d4ef39a7 100644 --- a/firefox-ios/Client/Frontend/Settings/Main/Support/SendAnonymousUsageDataSetting.swift +++ b/firefox-ios/Client/Frontend/Settings/Main/Support/SendAnonymousUsageDataSetting.swift @@ -40,7 +40,7 @@ class SendAnonymousUsageDataSetting: BoolSetting { attributedTitleText: NSAttributedString(string: .SendUsageSettingTitle), attributedStatusText: statusText, settingDidChange: { - AdjustHelper.setEnabled($0) +// AdjustHelper.setEnabled($0) DefaultGleanWrapper.shared.setUpload(isEnabled: $0) Experiments.setTelemetrySetting($0) } diff --git a/firefox-ios/Client/Frontend/Settings/SyncContentSettingsViewController.swift b/firefox-ios/Client/Frontend/Settings/SyncContentSettingsViewController.swift index 7ea9ed316950..953ea8eb0af3 100644 --- a/firefox-ios/Client/Frontend/Settings/SyncContentSettingsViewController.swift +++ b/firefox-ios/Client/Frontend/Settings/SyncContentSettingsViewController.swift @@ -265,9 +265,7 @@ class SyncContentSettingsViewController: SettingsTableViewController, FeatureFla engineSectionChildren.append(creditCards) } - if featureFlags.isFeatureEnabled( - .addressAutofill, - checking: .buildOnly) { + if AddressLocaleFeatureValidator.isValidRegion() { engineSectionChildren.append(addresses) } diff --git a/firefox-ios/Client/Frontend/Share/ShareExtensionHelper.swift b/firefox-ios/Client/Frontend/Share/ShareExtensionHelper.swift index c183c2370c36..15b9cc74be6e 100644 --- a/firefox-ios/Client/Frontend/Share/ShareExtensionHelper.swift +++ b/firefox-ios/Client/Frontend/Share/ShareExtensionHelper.swift @@ -18,8 +18,7 @@ class ShareExtensionHelper: NSObject, FeatureFlaggable { private let pocketActionExtension = "com.ideashower.ReadItLaterPro.Action-Extension" private var excludingActivities: [UIActivity.ActivityType] { - return [UIActivity.ActivityType.addToReadingList, - UIActivity.ActivityType.copyToPasteboard] + return [UIActivity.ActivityType.addToReadingList] } // Can be a file:// or http(s):// url @@ -104,8 +103,6 @@ class ShareExtensionHelper: NSObject, FeatureFlaggable { private func getApplicationActivities() -> [UIActivity] { var appActivities = [UIActivity]() - let copyLinkActivity = CopyLinkActivity(activityType: .copyLink, url: url) - appActivities.append(copyLinkActivity) let sendToDeviceActivity = SendToDeviceActivity(activityType: .sendToDevice, url: url) appActivities.append(sendToDeviceActivity) diff --git a/firefox-ios/Client/Frontend/Strings.swift b/firefox-ios/Client/Frontend/Strings.swift index 304b79d0176e..7b684805d4dc 100644 --- a/firefox-ios/Client/Frontend/Strings.swift +++ b/firefox-ios/Client/Frontend/Strings.swift @@ -132,6 +132,46 @@ extension String { tableName: nil, value: "Desktop Bookmarks", comment: "A label indicating all bookmarks grouped under the category 'Desktop Bookmarks'.") + public static let EditBookmark = MZLocalizedString( + key: "Bookmarks.Menu.EditBookmark.v131", + tableName: "Bookmarks", + value: "Edit Bookmark", + comment: "When a bookmark is longpressed in the bookmarks menu, an `Edit Bookmark` button is present.") + public static let EditFolder = MZLocalizedString( + key: "Bookmarks.Menu.EditFolder.v131", + tableName: "Bookmarks", + value: "Edit Folder", + comment: "When a folder is longpressed in the bookmarks menu, an `Edit Folder` button is present.") + public static let DeleteFolder = MZLocalizedString( + key: "Bookmarks.Menu.DeleteFolder.v131", + tableName: "Bookmarks", + value: "Delete Folder", + comment: "When a folder is longpressed in the bookmarks menu, a `Delete Folder` button is present.") + public static let AllBookmarks = MZLocalizedString( + key: "Bookmarks.Menu.AllBookmarks.v131", + tableName: "Bookmarks", + value: "All", + comment: "When navigating through the bookmarks menu and bookmark folders, a back button with an `All` (bookmarks) label is present to take the user to the top level bookmarks menu.") + public static let EditBookmarkSaveIn = MZLocalizedString( + key: "Bookmarks.Menu.EditBookmarkSaveIn.v131", + tableName: "Bookmarks", + value: "Save in", + comment: "When editing a bookmark, you can select the folder that the bookmark will be saved in. The label for this section of the view is `Save in`.") + public static let EditBookmarkTitle = MZLocalizedString( + key: "Bookmarks.Menu.EditBookmarkTitle.v131", + tableName: "Bookmarks", + value: "Edit Bookmark", + comment: "Label on the top of the `Edit Bookmarks` screen.") + public static let DeletedBookmark = MZLocalizedString( + key: "Bookmarks.Menu.DeletedBookmark.v131", + tableName: "Bookmarks", + value: "Deleted “%@”", + comment: "Label of toast displayed after a bookmark is deleted in the Bookmarks menu. %@ is the name of the bookmark.") + public static let BookmarksTopLevelTitle = MZLocalizedString( + key: "Bookmarks.Menu.BookmarksTopLevelTitle.v131", + tableName: "Bookmarks", + value: "Bookmarks", + comment: "Name of the top level bookmarks folder present in the folder selection menu of the`Edit Bookmark` screen") } } } @@ -267,17 +307,17 @@ extension String { value: "Use saved address", comment: "Displayed inside the keyboard hint when a user is entering their address and has at least one saved address. Indicates that there are stored addresses available for use in filling out a form.") public static let SaveAddressesToFirefox = MZLocalizedString( - key: "", // Addresses.Settings.SaveToFirefox.Title.v129 + key: "Addresses.Settings.SaveToFirefox.Title.v130", tableName: "Settings", value: "Save Addresses to %@", comment: "Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app.") public static let SecureSaveInfo = MZLocalizedString( - key: "", // Addresses.Settings.SecureSaveInfo.Description.v129 + key: "Addresses.Settings.SecureSaveInfo.Description.v130", tableName: "Settings", value: "Securely save your information to get quick access to it later.", comment: "Description text for the content unavailable view informing users they can create or add new addresses.") public static let ListItemA11y = MZLocalizedString( - key: "", // Addresses.Settings.ListItemA11y.v129 + key: "Addresses.Settings.ListItemA11y.v130", tableName: "Settings", value: "Address for %@", comment: "Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available.") @@ -572,6 +612,11 @@ extension String { tableName: "BottomSheet", value: "Use a saved address?", comment: "When a user is in the process of entering an address, a screen pops up prompting the user if they want to use a saved address. This string is used as the title label of the screen.") + public static let ManageAddressesButton = MZLocalizedString( + key: "Addresses.ManageAddressesButton.v130", + tableName: "Settings", + value: "Manage addresses", + comment: "This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses.") } } } @@ -1466,6 +1511,41 @@ extension String { } } +// MARK: - Native Error Page +extension String { + struct NativeErrorPage { + public static let ButtonLabel = MZLocalizedString( + key: "NativeErrorPage.ButtonLabel.v131", + tableName: "NativeErrorPage", + value: "Reload", + comment: "On error page, this is the text on a button that will try to load the page again.") + struct NoInternetConnection { + public static let TitleLabel = MZLocalizedString( + key: "NativeErrorPage.NoInternetConnection.TitleLabel.v131", + tableName: "NativeErrorPage", + value: "Looks like there’s a problem with your internet connection.", + comment: "On error page, this is the title for no internet connection") + public static let Description = MZLocalizedString( + key: "NativeErrorPage.NoInternetConnection.Description.v131", + tableName: "NativeErrorPage", + value: "Try connecting on a different device. Check your modem or router. Disconnect and reconnect to Wi-Fi.", + comment: "On error page, this is the description for no internet connection.") + } + struct GenericError { + public static let TitleLabel = MZLocalizedString( + key: "NativeErrorPage.GenericError.TitleLabel.v131", + tableName: "NativeErrorPage", + value: "Be careful. Something doesn’t look right.", + comment: "On error page, this is the title for generic error.") + public static let Description = MZLocalizedString( + key: "NativeErrorPage.GenericError.Description.v131", + tableName: "NativeErrorPage", + value: "An SSL error has occurred and a secure connection to the server cannot be made.", + comment: "On error page, this is the description for generic error.") + } + } +} + // MARK: - Onboarding screens extension String { public struct Onboarding { @@ -2566,6 +2646,11 @@ extension String { tableName: nil, value: "Remove", comment: "The title for the Remove context menu action for sites in Home Panels") + public static let EditContextMenuTitle = MZLocalizedString( + key: "HomePanel.ContextMenu.Edit.v131", + tableName: "Bookmarks", + value: "Edit", + comment: "The title for the Edit context menu action for sites in Home Panels") public static let PinTopsiteActionTitle2 = MZLocalizedString( key: "ActivityStream.ContextMenu.PinTopsite2", tableName: nil, @@ -3762,10 +3847,266 @@ extension String { } } -// MARK: - App menu +// MARK: - Main Menu extension String { - /// Identifiers of all new strings should begin with `Menu.` - public struct AppMenu { + public struct MainMenu { + public struct Account { + public static let SignedOutTitle = MZLocalizedString( + key: "MainMenu.Account.SignedOut.Title.v131", + tableName: "MainMenu", + value: "Sign In", + comment: "On the main menu, at the top, when the user is signed out. The title for the sign in action") + public static let SignedOutDescription = MZLocalizedString( + key: "MainMenu.Account.SignedOut.Description.v131", + tableName: "MainMenu", + value: "Sync passwords, tabs, and more", + comment: "On the main menu, at the top, when the user is signed out. The description for the sign in action") + public static let SyncErrorTitle = MZLocalizedString( + key: "MainMenu.Account.SyncError.Title.v131", + tableName: "MainMenu", + value: "Sign back in to sync", + comment: "On the main menu, at the top, when the user is signed in but there was an error syncing. The title for this state.") + public static let SyncErrorDescription = MZLocalizedString( + key: "MainMenu.Account.SyncError.Description.v131", + tableName: "MainMenu", + value: "Syncing paused", + comment: "On the main menu, at the top, when the user is signed in but there was an error syncing. The description subtitle for the sync error state.") + } + + public struct TabsSection { + public static let NewTab = MZLocalizedString( + key: "MainMenu.TabsSection.NewTab.Title.v131", + tableName: "MainMenu", + value: "New Tab", + comment: "On the main menu, the title for the action that will create a new, non-private, tab.") + public static let NewPrivateTab = MZLocalizedString( + key: "MainMenu.TabsSection.NewPrivateTab.Title.v131", + tableName: "MainMenu", + value: "New Private Tab", + comment: "On the main menu, the title for the action that will create a new private tab.") + } + + public struct ToolsSection { + public static let SwitchToDesktopSite = MZLocalizedString( + key: "MainMenu.ToolsSection.SwitchToDesktopSite.Title.v131", + tableName: "MainMenu", + value: "Switch to Desktop Site", + comment: "On the main menu, the title for the action that will switch a site from mobile version to the desktop version, if available.") + public static let SwitchToMobileSite = MZLocalizedString( + key: "MainMenu.ToolsSection.SwitchToMobileSite.Title.v131", + tableName: "MainMenu", + value: "Switch to Mobile Site", + comment: "On the main menu, the title for the action that will switch a site from the desktop version to the mobile version.") + public static let FindInPage = MZLocalizedString( + key: "MainMenu.ToolsSection.FindInPage.Title.v131", + tableName: "MainMenu", + value: "Find in Page…", + comment: "On the main menu, the title for the action that will bring up the Search menu, so the user can search for a word or a pharse on the current page.") + public static let Tools = MZLocalizedString( + key: "MainMenu.ToolsSection.ToolsSubmenu.Title.v131", + tableName: "MainMenu", + value: "Tools", + comment: "On the main menu, the title for the action that will take the user to the Tools submenu in the menu.") + public static let Save = MZLocalizedString( + key: "MainMenu.ToolsSection.SaveSubmenu.Title.v131", + tableName: "MainMenu", + value: "Save", + comment: "On the main menu, the title for the action that will take the user to the Save submenu in the menu.") + } + + public struct PanelLinkSection { + public static let Bookmarks = MZLocalizedString( + key: "MainMenu.PanelLinkSection.Bookmarks.Title.v131", + tableName: "MainMenu", + value: "Bookmarks", + comment: "On the main menu, the title for the action that will take the user to the Bookmarks panel. Please keep as short as possible, <15 chars of space available.") + public static let History = MZLocalizedString( + key: "MainMenu.PanelLinkSection.History.Title.v131", + tableName: "MainMenu", + value: "History", + comment: "On the main menu, the title for the action that will take the user to the History panel. Please keep as short as possible, <15 chars of space available.") + public static let Downloads = MZLocalizedString( + key: "MainMenu.PanelLinkSection.Downloads.Title.v131", + tableName: "MainMenu", + value: "Downloads", + comment: "On the main menu, the title for the action that will take the user to the Downloads panel. Please keep as short as possible, <15 chars of space available.") + public static let Passwords = MZLocalizedString( + key: "MainMenu.PanelLinkSection.Passwords.Title.v131", + tableName: "MainMenu", + value: "Passwords", + comment: "On the main menu, the title for the action that will take the user to the Passwords panel in the settings screen. Please keep as short as possible, <15 chars of space available.") + } + + public struct SettingsSection { + public static let CustomizeHomepage = MZLocalizedString( + key: "MainMenu.SettingsSection.CustomizeHomepage.Title.v131", + tableName: "MainMenu", + value: "Customize Homepage", + comment: "On the main menu, the title for the action that will take the user to the Customize Hopegape section in the settings screen.") + public static let WhatsNew = MZLocalizedString( + key: "MainMenu.SettingsSection.WhatsNew.Title.v131", + tableName: "MainMenu", + value: "New in %@", + comment: "On the main menu, the title for the action that will take the user to a What's New in Firefox popup. Placeholder is for the app name.") + public static let Settings = MZLocalizedString( + key: "MainMenu.SettingsSection.Settings.Title.v131", + tableName: "MainMenu", + value: "Settings", + comment: "On the main menu, the title for the action that will take the user to the Settings menu.") + public static let GetHelp = MZLocalizedString( + key: "MainMenu.SettingsSection.GetHelp.Title.v131", + tableName: "MainMenu", + value: "Get Help", + comment: "On the main menu, the title for the action that will take the user to a website to get help from Mozilla.") + } + + public struct Submenus { + public struct Tools { + public static let Zoom = MZLocalizedString( + key: "MainMenu.Submenus.Tools.Zoom.Title.v131", + tableName: "MainMenu", + value: "Zoom (%@)", + comment: "On the main menu, in the tools submenu, the title for the menu component that indicates the current zoom level. Placeholder is for the current zoom level percentage. ") + public static let ZoomSubtitle = MZLocalizedString( + key: "MainMenu.Submenus.Tools.Zoom.Subtitle.v131", + tableName: "MainMenu", + value: "Zoom", + comment: "On the main menu, a string below the Tool submenu title, indicating what kind of tools are available in that menu. This string is for the Zoom tool.") + public static let ReaderViewOn = MZLocalizedString( + key: "MainMenu.Submenus.Tools.ReaderView.On.Title.v131", + tableName: "MainMenu", + value: "Turn on Reader View", + comment: "On the main menu, the title for the action that will turn the reader view on for the current website.") + public static let ReaderViewOff = MZLocalizedString( + key: "MainMenu.Submenus.Tools.ReaderView.Off.Title.v131", + tableName: "MainMenu", + value: "Turn off Reader View", + comment: "On the main menu, the title for the action that will turn the reader view on for the current website.") + public static let ReaderViewSubtitle = MZLocalizedString( + key: "MainMenu.Submenus.Tools.ReaderView.Subtitle.v131", + tableName: "MainMenu", + value: "Reader View", + comment: "On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Reader View tool.") + public static let NightModeOn = MZLocalizedString( + key: "MainMenu.Submenus.Tools.NightMode.On.Title.v131", + tableName: "MainMenu", + value: "Turn on Night Mode", + comment: "On the main menu, the title for the action that will turn Night Mode on in the application.") + public static let NightModeOff = MZLocalizedString( + key: "MainMenu.Submenus.Tools.NightMode.Off.Title.v131", + tableName: "MainMenu", + value: "Turn off Night Mode", + comment: "On the main menu, the title for the action that will turn Night Mode off in the application.") + public static let NightModeSubtitle = MZLocalizedString( + key: "MainMenu.Submenus.Tools.NightMode.Subtitle.v131", + tableName: "MainMenu", + value: "Night Mode", + comment: "On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Night Mode tool.") + public static let Print = MZLocalizedString( + key: "MainMenu.Submenus.Tools.Print.Title.v131", + tableName: "MainMenu", + value: "Print", + comment: "On the main menu, the title for the action that will take the user to the Print module in the application.") + public static let PrintSubtitle = MZLocalizedString( + key: "MainMenu.Submenus.Tools.Print.Subtitle.v131", + tableName: "MainMenu", + value: "Print", + comment: "On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Report Print tool.") + public static let Share = MZLocalizedString( + key: "MainMenu.Submenus.Tools.Share.Title.v131", + tableName: "MainMenu", + value: "Share", + comment: "On the main menu, the title for the action that will take the user to the Share module in the application.") + public static let ShareSubtitle = MZLocalizedString( + key: "MainMenu.Submenus.Tools.Share.Subtitle.v131", + tableName: "MainMenu", + value: "Share", + comment: "On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Report Share tool.") + public static let ReportBrokenSite = MZLocalizedString( + key: "MainMenu.Submenus.Tools.ReportBrokenSite.Title.v131", + tableName: "MainMenu", + value: "Report Broken Site", + comment: "On the main menu, the title for the action that will take the user to the site where they can report a broken website to our web compatibility team.") + public static let ReportBrokenSiteSubtitle = MZLocalizedString( + key: "MainMenu.Submenus.Tools.ReportBrokenSite.Subtitle.v131", + tableName: "MainMenu", + value: "Report", + comment: "On the main menu, a string below the Tool submenu tiitle, indicating what kind of tools are available in that menu. This string is for the Report Broken Site tool.") + } + + public struct Save { + public static let BookmarkThisPage = MZLocalizedString( + key: "MainMenu.Submenus.Save.BookmarkThisPage.Title.v131", + tableName: "MainMenu", + value: "Bookmark This Page", + comment: "On the main menu, in the Save submenu, the title for the menu component that allows a user to save a bookmark for this particular page..") + public static let BookmarkThisPageSubtitle = MZLocalizedString( + key: "MainMenu.Submenus.Save.BookmarkThisPage.Subtitle.v131", + tableName: "MainMenu", + value: "Add Bookmark", + comment: "On the main menu, a string below the Save submenu title, indicating what kind of tools are available in that menu. This string is for the Bookmarks tool.") + public static let EditBookmark = MZLocalizedString( + key: "MainMenu.Submenus.Save.EditBookmark.Title.v131", + tableName: "MainMenu", + value: "Edit Bookmark", + comment: "On the main menu, in the Save submenu, the title for the menu component that allows a user to edit the bookmark for this particular page.") + public static let AddToShortcuts = MZLocalizedString( + key: "MainMenu.Submenus.Save.AddToShortcuts.Title.v131", + tableName: "MainMenu", + value: "Add to Shortcuts", + comment: "On the main menu, in the Save submenu, the title for the menu component that allows a user to add the current website to the shortcuts on the homepage.") + public static let RemoveFromShortcuts = MZLocalizedString( + key: "MainMenu.Submenus.Save.RemoveFromShortcuts.Title.v131", + tableName: "MainMenu", + value: "Remove from Shortcuts", + comment: "On the main menu, in the Save submenu, the title for the menu component that allows a user to remove the current website from the shortcuts on the homepage.") + public static let AddToShortcutsSubtitle = MZLocalizedString( + key: "MainMenu.Submenus.Save.AddToShortcuts.Subtitle.v131", + tableName: "MainMenu", + value: "Shortcut", + comment: "On the main menu, a string below the Save submenu title, indicating what kind of tools are available in that menu. This string is for the Shortcuts tool.") + public static let AddToHomeScreen = MZLocalizedString( + key: "MainMenu.Submenus.Save.AddToHomeScreen.Title.v131", + tableName: "MainMenu", + value: "Add to Home Screen", + comment: "On the main menu, in the Save submenu, the title for the menu component that allows a user to add a website to the home screen.") + public static let AddToHomeScreenSubtitle = MZLocalizedString( + key: "MainMenu.Submenus.Save.AddToHomeScreen.Subtitle.v131", + tableName: "MainMenu", + value: "Home", + comment: "On the main menu, a string below the Save submenu title, indicating what kind of tools are available in that menu. This string is for the Add to Homescreen tool.") + public static let SaveToReadingList = MZLocalizedString( + key: "MainMenu.Submenus.Save.SaveToReadingList.Title.v131", + tableName: "MainMenu", + value: "Save to Reading List", + comment: "On the main menu, in the Save submenu, the title for the menu component that allows the user to add this site to the reading list.") + public static let RemoveFromReadingList = MZLocalizedString( + key: "MainMenu.Submenus.Save.RemoveFromReadingList.Title.v131", + tableName: "MainMenu", + value: "Remove from Reading List", + comment: "On the main menu, in the Save submenu, the title for the menu component that allows the user to remove this site from the reading list.") + public static let SaveToReadingListSubtitle = MZLocalizedString( + key: "MainMenu.Submenus.Save.SaveToReadingList.Subtitle.v131", + tableName: "MainMenu", + value: "Reading List", + comment: "On the main menu, a string below the Save submenu title, indicating what kind of tools are available in that menu. This string is for the Reading List tool.") + public static let SaveAsPDF = MZLocalizedString( + key: "MainMenu.Submenus.Save.SaveAsPDF.Title.v131", + tableName: "MainMenu", + value: "Save as PDF", + comment: "On the main menu, in the Save submenu, the title for the menu component that allows the user to use the Save to PDF tool.") + public static let SaveAsPDFSubtitle = MZLocalizedString( + key: "MainMenu.Submenus.Save.SaveAsPDF.Subtitle.v131", + tableName: "MainMenu", + value: "PDF", + comment: "On the main menu, a string below the Save submenu title, indicating what kind of tools are available in that menu. This string is for the Save as PDF tool.") + } + } + } + + // MARK: - LegacyAppMenu + // These strings may still be in use, thus have not been moved to the `OldStrings` struct + public struct LegacyAppMenu { public static let AppMenuReportSiteIssueTitleString = MZLocalizedString( key: "Menu.ReportSiteIssueAction.Title", tableName: "Menu", @@ -3796,16 +4137,17 @@ extension String { tableName: "Menu", value: "Request Mobile Site", comment: "Label for the button, displayed in the menu, used to request the mobile version of the current website.") - public static let AppMenuSettingsTitleString = MZLocalizedString( - key: "Menu.OpenSettingsAction.Title", - tableName: "Menu", - value: "Settings", - comment: "Label for the button, displayed in the menu, used to open the Settings menu.") public static let AppMenuCloseAllTabsTitleString = MZLocalizedString( key: "Menu.CloseAllTabsAction.Title", tableName: "Menu", value: "Close All Tabs", comment: "Label for the button, displayed in the menu, used to close all tabs currently open.") + + public static let AppMenuSettingsTitleString = MZLocalizedString( + key: "Menu.OpenSettingsAction.Title", + tableName: "Menu", + value: "Settings", + comment: "Label for the button, displayed in the menu, used to open the Settings menu.") public static let AppMenuOpenHomePageTitleString = MZLocalizedString( key: "SettingsMenu.OpenHomePageAction.Title", tableName: "Menu", @@ -4109,7 +4451,7 @@ extension String { comment: "Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure.") public static let onNotSecureHeader = MZLocalizedString( - key: "Menu.EnhancedTrackingProtection.On.Header.v128", + key: "Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128", tableName: "EnhancedTrackingProtection", value: "Your connection is not secure.", comment: "Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure.") @@ -4127,7 +4469,7 @@ extension String { comment: "Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked") public static let noTrackersLabel = MZLocalizedString( - key: "Menu.EnhancedTrackingProtection.Details.Trackers.v128", + key: "Menu.EnhancedTrackingProtection.Details.NoTrackers.v131", tableName: "EnhancedTrackingProtection", value: "No trackers found", comment: "Text to let users know that no trackers were found on the current website.") @@ -4221,6 +4563,72 @@ extension String { tableName: "EnhancedTrackingProtection", value: "Privacy settings", comment: "The title for the privacy settings button inside the enhanced tracking protection screen.") + + public static let certificatesTitle = MZLocalizedString( + key: "Menu.EnhancedTrackingProtection.Certificates.Title.v131", + tableName: "EnhancedTrackingProtection", + value: "Certificate", + comment: "The title for the certificates screen inside the certificates screen.") + + public static let certificateSubjectName = MZLocalizedString( + key: "Menu.EnhancedTrackingProtection.Certificates.SubjectName.v131", + tableName: "EnhancedTrackingProtection", + value: "Subject Name", + comment: "The title for the certificate subject name section inside the certificate screen.") + + public static let certificateCommonName = MZLocalizedString( + key: "Menu.EnhancedTrackingProtection.Certificates.CommonName.v131", + tableName: "EnhancedTrackingProtection", + value: "Common Name", + comment: "The title for the certificate common name inside the certificate screen.") + + public static let certificateIssuerName = MZLocalizedString( + key: "Menu.EnhancedTrackingProtection.Certificates.IssuerName.v131", + tableName: "EnhancedTrackingProtection", + value: "Issuer Name", + comment: "The title for the certificate issuer name section inside the certificate screen.") + + public static let certificateIssuerCountry = MZLocalizedString( + key: "Menu.EnhancedTrackingProtection.Certificates.IssuerCountry.v131", + tableName: "EnhancedTrackingProtection", + value: "Country", + comment: "The title for the certificate issuer country inside the certificate screen.") + + public static let certificateIssuerOrganization = MZLocalizedString( + key: "Menu.EnhancedTrackingProtection.Certificates.IssuerOrganization.v131", + tableName: "EnhancedTrackingProtection", + value: "Organization", + comment: "The title for the certificate issuer organization inside the certificate screen.") + + public static let certificateValidity = MZLocalizedString( + key: "Menu.EnhancedTrackingProtection.Certificates.Validity.v131", + tableName: "EnhancedTrackingProtection", + value: "Validity", + comment: "The title for the certificate validity section inside the certificate screen.") + + public static let certificateValidityNotBefore = MZLocalizedString( + key: "Menu.EnhancedTrackingProtection.Certificates.ValidityNotBefore.v131", + tableName: "EnhancedTrackingProtection", + value: "Not Before", + comment: "The title for the certificate validity not before date inside the certificate screen.") + + public static let certificateValidityNotAfter = MZLocalizedString( + key: "Menu.EnhancedTrackingProtection.Certificates.ValidityNotAfter.v131", + tableName: "EnhancedTrackingProtection", + value: "Not After", + comment: "The title for the certificate validity not after date inside the certificate screen.") + + public static let certificateSubjectAltNames = MZLocalizedString( + key: "Menu.EnhancedTrackingProtection.Certificates.SubjectAltNames.v131", + tableName: "EnhancedTrackingProtection", + value: "Subject Alt Names", + comment: "The title for the certificate subject alt names section inside the certificate screen.") + + public static let certificateSubjectAltNamesDNSName = MZLocalizedString( + key: "Menu.EnhancedTrackingProtection.Certificates.SubjectAltNamesDNSName.v131", + tableName: "EnhancedTrackingProtection", + value: "DNS Name", + comment: "The title for the certificate subject alt names DNS name inside the certificate screen.") } } } @@ -4514,7 +4922,7 @@ extension String { tableName: nil, value: "Available devices:", comment: "Header for the list of devices table") - public static let ShareSendToDevice = String.AppMenu.TouchActions.SendToDeviceTitle + public static let ShareSendToDevice = String.LegacyAppMenu.TouchActions.SendToDeviceTitle // The above items are re-used strings from the old extension. New strings below. @@ -5766,6 +6174,14 @@ extension String { tableName: "Toolbar", value: "New Tab", comment: "Accessibility label for the new tab button that can be displayed in the navigation or address toolbar.") + public struct TabToolbarLongPressActionsMenu { + public static let CloseThisTabButton = MZLocalizedString( + key: "Toolbar.Tab.CloseThisTab.Button.v130", + tableName: "Toolbar", + value: "Close This Tab", + comment: "Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed" + ) + } } public struct AddressToolbar { @@ -6375,6 +6791,11 @@ extension String { tableName: nil, value: nil, comment: "Tile title for Twitter") + public static let DefaultSuggestedX = MZLocalizedString( + key: "SuggestedSites.X.Title.v131", + tableName: "SuggestedSites", + value: "X", + comment: "Title for X (formerly Twitter) tile in the suggested sites section of the homepage.") } // MARK: - Credential Provider diff --git a/firefox-ios/Client/Frontend/TabContentsScripts/DownloadContentScript.swift b/firefox-ios/Client/Frontend/TabContentsScripts/DownloadContentScript.swift index 05ab1c79208c..501cf4626b95 100644 --- a/firefox-ios/Client/Frontend/TabContentsScripts/DownloadContentScript.swift +++ b/firefox-ios/Client/Frontend/TabContentsScripts/DownloadContentScript.swift @@ -64,7 +64,8 @@ class DownloadContentScript: TabContentScript { let data = Bytes.decodeBase64(base64String) else { return } - let windowUUID = tab?.windowUUID ?? (AppContainer.shared.resolve() as WindowManager).activeWindow + let windowManager: WindowManager = AppContainer.shared.resolve() + let windowUUID = tab?.windowUUID ?? windowManager.windows.first?.key ?? .unavailable defer { notificationCenter.post(name: .PendingBlobDownloadAddedToQueue, withObject: nil) DownloadContentScript.blobUrlForDownload = nil diff --git a/firefox-ios/Client/Frontend/Toolbar+URLBar/ShareButton.swift b/firefox-ios/Client/Frontend/Toolbar+URLBar/ShareButton.swift index 4e4c1f6c7b1b..bb1d590a8666 100644 --- a/firefox-ios/Client/Frontend/Toolbar+URLBar/ShareButton.swift +++ b/firefox-ios/Client/Frontend/Toolbar+URLBar/ShareButton.swift @@ -37,7 +37,7 @@ class ShareButton: UIButton { super.init(frame: frame) clipsToBounds = false - setImage(UIImage.templateImageNamed(StandardImageIdentifiers.Large.shareApple), for: .normal) + setImage(UIImage.templateImageNamed(StandardImageIdentifiers.Large.share), for: .normal) imageView?.contentMode = .scaleAspectFit configuration = .plain() } diff --git a/firefox-ios/Client/Frontend/Toolbar+URLBar/TabToolbarHelper.swift b/firefox-ios/Client/Frontend/Toolbar+URLBar/TabToolbarHelper.swift index 77bfd5b5cea0..9924796fd05d 100644 --- a/firefox-ios/Client/Frontend/Toolbar+URLBar/TabToolbarHelper.swift +++ b/firefox-ios/Client/Frontend/Toolbar+URLBar/TabToolbarHelper.swift @@ -29,13 +29,13 @@ protocol TabToolbarProtocol: AnyObject { } protocol TabToolbarDelegate: AnyObject { - func configureDataClearanceContextualHint() + func configureDataClearanceContextualHint(_ view: UIView) func tabToolbarDidPressBack(_ tabToolbar: TabToolbarProtocol, button: UIButton) func tabToolbarDidPressForward(_ tabToolbar: TabToolbarProtocol, button: UIButton) func tabToolbarDidLongPressBack(_ tabToolbar: TabToolbarProtocol, button: UIButton) func tabToolbarDidLongPressForward(_ tabToolbar: TabToolbarProtocol, button: UIButton) func tabToolbarDidPressHome(_ tabToolbar: TabToolbarProtocol, button: UIButton) - func tabToolbarDidPressFire(_ tabToolbar: TabToolbarProtocol, button: UIButton) + func tabToolbarDidPressDataClearance(_ tabToolbar: TabToolbarProtocol, button: UIButton) func tabToolbarDidPressMenu(_ tabToolbar: TabToolbarProtocol, button: UIButton) func tabToolbarDidPressBookmarks(_ tabToolbar: TabToolbarProtocol, button: UIButton) func tabToolbarDidPressTabs(_ tabToolbar: TabToolbarProtocol, button: UIButton) @@ -153,19 +153,19 @@ open class TabToolbarHelper: NSObject { let appMenuImage = UIImage.templateImageNamed(StandardImageIdentifiers.Large.appMenu) toolbar.appMenuButton.contentMode = .center toolbar.appMenuButton.showsLargeContentViewer = true - toolbar.appMenuButton.largeContentTitle = .AppMenu.Toolbar.MenuButtonAccessibilityLabel + toolbar.appMenuButton.largeContentTitle = .LegacyAppMenu.Toolbar.MenuButtonAccessibilityLabel toolbar.appMenuButton.largeContentImage = appMenuImage toolbar.appMenuButton.setImage(appMenuImage, for: .normal) - toolbar.appMenuButton.accessibilityLabel = .AppMenu.Toolbar.MenuButtonAccessibilityLabel + toolbar.appMenuButton.accessibilityLabel = .LegacyAppMenu.Toolbar.MenuButtonAccessibilityLabel toolbar.appMenuButton.addTarget(self, action: #selector(didClickMenu), for: .touchUpInside) toolbar.appMenuButton.accessibilityIdentifier = AccessibilityIdentifiers.Toolbar.settingsMenuButton toolbar.bookmarksButton.contentMode = .center toolbar.bookmarksButton.showsLargeContentViewer = true toolbar.bookmarksButton.largeContentImage = ImageBookmark - toolbar.bookmarksButton.largeContentTitle = .AppMenu.Toolbar.BookmarksButtonAccessibilityLabel + toolbar.bookmarksButton.largeContentTitle = .LegacyAppMenu.Toolbar.BookmarksButtonAccessibilityLabel toolbar.bookmarksButton.setImage(ImageBookmark, for: .normal) - toolbar.bookmarksButton.accessibilityLabel = .AppMenu.Toolbar.BookmarksButtonAccessibilityLabel + toolbar.bookmarksButton.accessibilityLabel = .LegacyAppMenu.Toolbar.BookmarksButtonAccessibilityLabel toolbar.bookmarksButton.addTarget(self, action: #selector(didClickLibrary), for: .touchUpInside) toolbar.bookmarksButton.accessibilityIdentifier = AccessibilityIdentifiers.Toolbar.bookmarksButton @@ -246,7 +246,7 @@ open class TabToolbarHelper: NSObject { TelemetryWrapper.recordEvent(category: .action, method: .tap, object: .startSearchButton) toolbar.tabToolbarDelegate?.tabToolbarDidPressSearch(toolbar, button: toolbar.multiStateButton) case .fire: - toolbar.tabToolbarDelegate?.tabToolbarDidPressFire(toolbar, button: toolbar.multiStateButton) + toolbar.tabToolbarDelegate?.tabToolbarDidPressDataClearance(toolbar, button: toolbar.multiStateButton) } } } diff --git a/firefox-ios/Client/Frontend/Toolbar+URLBar/URLBarView.swift b/firefox-ios/Client/Frontend/Toolbar+URLBar/URLBarView.swift index 9575237c7c4b..f5db4e4875b1 100644 --- a/firefox-ios/Client/Frontend/Toolbar+URLBar/URLBarView.swift +++ b/firefox-ios/Client/Frontend/Toolbar+URLBar/URLBarView.swift @@ -65,8 +65,13 @@ protocol URLBarViewProtocol { func leaveOverlayMode(reason: URLBarLeaveOverlayModeReason, shouldCancelLoading cancel: Bool) } -class URLBarView: UIView, URLBarViewProtocol, AlphaDimmable, TopBottomInterchangeable, - SearchEngineDelegate, SearchBarLocationProvider { +class URLBarView: UIView, + URLBarViewProtocol, + AlphaDimmable, + TopBottomInterchangeable, + SearchEngineDelegate, + SearchBarLocationProvider, + Autocompletable { // Additional UIAppearance-configurable properties @objc dynamic lazy var locationBorderColor: UIColor = .clear { didSet { diff --git a/firefox-ios/Client/Frontend/TrackingProtection/CertificatesCell.swift b/firefox-ios/Client/Frontend/TrackingProtection/CertificatesCell.swift new file mode 100644 index 000000000000..d9b7b3c840b5 --- /dev/null +++ b/firefox-ios/Client/Frontend/TrackingProtection/CertificatesCell.swift @@ -0,0 +1,100 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Common + +final class CertificatesCell: UITableViewCell, ReusableCell, ThemeApplicable { + struct UX { + static let sectionLabelWidth = 150.0 + static let sectionLabelMargin = 20.0 + static let sectionItemsSpacing = 40.0 + static let allSectionItemsSpacing = 10.0 + static let allSectionItemsTopMargin = 20.0 + } + + var sectionLabel: UILabel = .build { label in + label.font = FXFontStyles.Bold.headline.scaledFont() + label.textAlignment = .right + label.numberOfLines = 0 + label.lineBreakMode = .byWordWrapping + } + + var allSectionItemsStackView: UIStackView = .build { stack in + stack.axis = .vertical + stack.distribution = .equalSpacing + stack.spacing = UX.allSectionItemsSpacing + } + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + + contentView.addSubview(sectionLabel) + contentView.addSubview(allSectionItemsStackView) + + NSLayoutConstraint.activate([ + sectionLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, + constant: UX.sectionLabelMargin), + sectionLabel.topAnchor.constraint(equalTo: contentView.topAnchor, + constant: UX.sectionLabelMargin), + sectionLabel.widthAnchor.constraint(equalToConstant: UX.sectionLabelWidth), + + allSectionItemsStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, + constant: UX.sectionLabelMargin), + allSectionItemsStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, + constant: -UX.sectionLabelMargin), + allSectionItemsStackView.topAnchor.constraint(equalTo: sectionLabel.bottomAnchor, + constant: UX.allSectionItemsTopMargin), + allSectionItemsStackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, + constant: -UX.sectionLabelMargin) + ]) + } + + override func prepareForReuse() { + super.prepareForReuse() + allSectionItemsStackView.removeAllArrangedViews() + sectionLabel.text = nil + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func configure(theme: Theme, sectionTitle: String, items: CertificateItems) { + applyTheme(theme: theme) + sectionLabel.text = sectionTitle + for (key, value) in items { + let stackView = getSectionItemStackView() + let titleLabel = getItemLabel(theme: theme, with: key, isTitle: true) + titleLabel.widthAnchor.constraint(equalToConstant: UX.sectionLabelWidth).isActive = true + stackView.addArrangedSubview(titleLabel) + stackView.addArrangedSubview(getItemLabel(theme: theme, with: value, isTitle: false)) + allSectionItemsStackView.addArrangedSubview(stackView) + } + } + + func applyTheme(theme: Theme) { + sectionLabel.textColor = theme.colors.textPrimary + } + + private func getItemLabel(theme: Theme, with title: String, isTitle: Bool) -> UILabel { + let itemLabel: UILabel = .build { label in + label.font = FXFontStyles.Bold.headline.scaledFont() + label.textColor = isTitle ? theme.colors.textSecondary : theme.colors.textPrimary + label.text = title + label.textAlignment = isTitle ? .right : .left + label.numberOfLines = 0 + label.lineBreakMode = .byWordWrapping + } + return itemLabel + } + + private func getSectionItemStackView() -> UIStackView { + let sectionItemsStackView: UIStackView = .build { stack in + stack.axis = .horizontal + stack.spacing = UX.sectionItemsSpacing + } + return sectionItemsStackView + } +} diff --git a/firefox-ios/Client/Frontend/TrackingProtection/CertificatesHandler.swift b/firefox-ios/Client/Frontend/TrackingProtection/CertificatesHandler.swift index ab7fa050e61f..7e96e81330fd 100644 --- a/firefox-ios/Client/Frontend/TrackingProtection/CertificatesHandler.swift +++ b/firefox-ios/Client/Frontend/TrackingProtection/CertificatesHandler.swift @@ -7,10 +7,10 @@ import Security import CryptoKit import X509 import SwiftASN1 +import Common class CertificatesHandler { private let serverTrust: SecTrust - var certificates = [Certificate]() /// Initializes a new `CertificatesHandler` with the given server trust. /// - Parameters: @@ -20,17 +20,68 @@ class CertificatesHandler { } /// Extracts and handles the certificate chain. - func handleCertificates() { + /// - Parameters: + /// - completion: A completion block that provides the extracted certificates. + func handleCertificates() -> [Certificate] { + var certificates = [Certificate]() guard let certificateChain = SecTrustCopyCertificateChain(serverTrust) as? [SecCertificate] else { - return + return certificates } - for (_, certificate) in certificateChain.enumerated() { + for certificate in certificateChain { let certificateData = SecCertificateCopyData(certificate) as Data do { let certificate = try Certificate(derEncoded: Array(certificateData)) certificates.append(certificate) } catch { + DefaultLogger.shared.log("\(error)", + level: .warning, + category: .homepage) } } + return certificates + } +} + +// Define a function to get the certificates for a given URL +func getCertificates(for url: URL, completion: @escaping ([Certificate]?) -> Void) { + // Create a URL session with a custom delegate + let session = URLSession(configuration: .default, + delegate: CertificateDelegate(completion: completion), + delegateQueue: nil) + + // Start a data task to trigger the certificate retrieval + let task = session.dataTask(with: url) { _, _, error in + if let error = error { + DefaultLogger.shared.log("\(error)", + level: .warning, + category: .homepage) + completion(nil) + } + } + + task.resume() +} + +// Custom delegate to handle the authentication challenge +class CertificateDelegate: NSObject, URLSessionDelegate { + private let completion: ([Certificate]?) -> Void + + init(completion: @escaping ([Certificate]?) -> Void) { + self.completion = completion + } + + func urlSession(_ session: URLSession, didReceive + challenge: URLAuthenticationChallenge, + completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { + if let serverTrust = challenge.protectionSpace.serverTrust { + let certificatesHandler = CertificatesHandler(serverTrust: serverTrust) + self.completion(certificatesHandler.handleCertificates()) + // Use the server trust + completionHandler(.useCredential, URLCredential(trust: serverTrust)) + } else { + // Call the completion handler with nil if serverTrust is not available + self.completion(nil) + completionHandler(.cancelAuthenticationChallenge, nil) + } } } diff --git a/firefox-ios/Client/Frontend/TrackingProtection/CertificatesViewController.swift b/firefox-ios/Client/Frontend/TrackingProtection/CertificatesViewController.swift new file mode 100644 index 000000000000..2133efb857eb --- /dev/null +++ b/firefox-ios/Client/Frontend/TrackingProtection/CertificatesViewController.swift @@ -0,0 +1,278 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Common +import Shared +import SiteImageView + +import Security +import CryptoKit +import X509 +import SwiftASN1 + +struct CertificateKeys { + static let commonName = "CN" + static let country = "C" + static let organization = "O" +} + +typealias CertificateItems = [(key: String, value: String)] + +class CertificatesViewController: UIViewController, Themeable, UITableViewDelegate, UITableViewDataSource { + private enum CertificatesItemType: Int { + case subjectName + case issuerName + case validity + case subjectAltName + } + + // MARK: - UI + struct UX { + static let headerStackViewSpacing = 16.0 + static let titleLabelMargin = 8.0 + static let titleLabelTopMargin = 20.0 + static let headerStackViewMargin = 8.0 + static let headerStackViewTopMargin = 20.0 + static let tableViewSpacerTopMargin = 20.0 + static let tableViewSpacerHeight = 1.0 + static let tableViewTopMargin = 20.0 + } + + private let titleLabel: UILabel = .build { label in + label.font = FXFontStyles.Bold.title1.scaledFont() + label.text = .Menu.EnhancedTrackingProtection.certificatesTitle + } + + private let headerStackView: UIStackView = .build { stack in + stack.axis = .horizontal + stack.distribution = .fillEqually + stack.spacing = UX.headerStackViewSpacing + } + + private let tableViewTopSpacer: UIView = .build() + + let certificatesTableView: UITableView = .build { tableView in + tableView.allowsSelection = false + tableView.register(CertificatesCell.self, forCellReuseIdentifier: CertificatesCell.cellIdentifier) + } + + // MARK: - Variables + private var constraints = [NSLayoutConstraint]() + var viewModel: CertificatesViewModel + var notificationCenter: NotificationProtocol + var themeManager: ThemeManager + var themeObserver: NSObjectProtocol? + let windowUUID: WindowUUID + var currentWindowUUID: UUID? { return windowUUID } + + // MARK: - View Lifecycle + + init(with viewModel: CertificatesViewModel, + windowUUID: WindowUUID, + and notificationCenter: NotificationProtocol = NotificationCenter.default, + themeManager: ThemeManager = AppContainer.shared.resolve()) { + self.viewModel = viewModel + self.windowUUID = windowUUID + self.notificationCenter = notificationCenter + self.themeManager = themeManager + super.init(nibName: nil, bundle: nil) + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + notificationCenter.removeObserver(self) + } + + override func viewDidLoad() { + super.viewDidLoad() + setupView() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + updateViewDetails() + listenForThemeChange(view) + applyTheme() + } + + private func setupView() { + constraints.removeAll() + setupTitleConstraints() + setupCertificatesHeaderView() + setupCertificatesTableView() + setupAccessibilityIdentifiers() + NSLayoutConstraint.activate(constraints) + } + + private func setupTitleConstraints() { + view.addSubview(titleLabel) + NSLayoutConstraint.activate([ + titleLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: UX.titleLabelMargin), + titleLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -UX.titleLabelMargin), + titleLabel.topAnchor.constraint(equalTo: view.topAnchor, constant: UX.titleLabelTopMargin) + ]) + } + + private func setupCertificatesHeaderView() { + // TODO: FXIOS-9834 Add tab indicator for table view tabs and hide/unhide it on selection + for (index, certificate) in viewModel.certificates.enumerated() { + let certificateValues = viewModel.getCertificateValues(from: "\(certificate.subject)") + if !certificateValues.isEmpty, let commonName = certificateValues[CertificateKeys.commonName] { + let button: UIButton = .build { [weak self] button in + button.setTitle(commonName, for: .normal) + button.setTitleColor(self?.currentTheme().colors.textPrimary, for: .normal) + button.configuration?.titleLineBreakMode = .byWordWrapping + button.titleLabel?.numberOfLines = 2 + button.titleLabel?.textAlignment = .center + button.tag = index + button.addTarget(self, action: #selector(self?.certificateButtonTapped(_:)), for: .touchUpInside) + } + headerStackView.addArrangedSubview(button) + } + } + + view.addSubview(headerStackView) + + NSLayoutConstraint.activate([ + headerStackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, + constant: UX.headerStackViewMargin), + headerStackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, + constant: -UX.headerStackViewMargin), + headerStackView.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, + constant: UX.headerStackViewTopMargin) + ]) + } + + private func setupCertificatesTableView() { + certificatesTableView.delegate = self + certificatesTableView.dataSource = self + view.addSubview(tableViewTopSpacer) + view.addSubview(certificatesTableView) + NSLayoutConstraint.activate([ + tableViewTopSpacer.topAnchor.constraint(equalTo: headerStackView.bottomAnchor, + constant: UX.tableViewSpacerTopMargin), + tableViewTopSpacer.leadingAnchor.constraint(equalTo: view.leadingAnchor), + tableViewTopSpacer.trailingAnchor.constraint(equalTo: view.trailingAnchor), + tableViewTopSpacer.heightAnchor.constraint(equalToConstant: UX.tableViewSpacerHeight), + + certificatesTableView.topAnchor.constraint(equalTo: tableViewTopSpacer.bottomAnchor, + constant: UX.tableViewTopMargin), + certificatesTableView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + certificatesTableView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + certificatesTableView.bottomAnchor.constraint(equalTo: view.bottomAnchor) + ]) + } + + @objc + private func certificateButtonTapped(_ sender: UIButton) { + viewModel.selectedCertificateIndex = sender.tag + certificatesTableView.reloadData() + } + + // MARK: - UITableViewDataSource + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + guard !viewModel.certificates.isEmpty else { return 0 } + return 4 + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + guard let cell = tableView.dequeueReusableCell(withIdentifier: CertificatesCell.cellIdentifier, for: indexPath) + as? CertificatesCell else { + return UITableViewCell() + } + + guard !viewModel.certificates.isEmpty else { + return cell + } + + let certificate = viewModel.certificates[viewModel.selectedCertificateIndex] + + switch CertificatesItemType(rawValue: indexPath.row) { + case .subjectName: + if let commonName = viewModel.getCertificateValues(from: "\(certificate.subject)")[CertificateKeys.commonName] { + cell.configure(theme: currentTheme(), + sectionTitle: .Menu.EnhancedTrackingProtection.certificateSubjectName, + items: [(.Menu.EnhancedTrackingProtection.certificateCommonName, commonName)]) + } + + case .issuerName: + let issuerData = viewModel.getCertificateValues(from: "\(certificate.issuer)") + if let country = issuerData[CertificateKeys.country], + let organization = issuerData[CertificateKeys.organization], + let commonName = issuerData[CertificateKeys.commonName] { + cell.configure(theme: currentTheme(), + sectionTitle: .Menu.EnhancedTrackingProtection.certificateIssuerName, + items: [(.Menu.EnhancedTrackingProtection.certificateIssuerCountry, country), + (.Menu.EnhancedTrackingProtection.certificateIssuerOrganization, organization), + (.Menu.EnhancedTrackingProtection.certificateCommonName, commonName)]) + } + + case .validity: + cell.configure(theme: currentTheme(), + sectionTitle: .Menu.EnhancedTrackingProtection.certificateValidity, + items: [ + (.Menu.EnhancedTrackingProtection.certificateValidityNotBefore, + certificate.notValidBefore.toRFC822String()), + (.Menu.EnhancedTrackingProtection.certificateValidityNotAfter, + certificate.notValidAfter.toRFC822String()) + ]) + + case .subjectAltName: + cell.configure(theme: currentTheme(), + sectionTitle: .Menu.EnhancedTrackingProtection.certificateSubjectAltNames, + items: viewModel.getDNSNames(for: certificate)) + case .none: break + } + return cell + } + + // MARK: Accessibility + private func setupAccessibilityIdentifiers() { + // TODO: FXIOS-9829 Enhanced Tracking Protection certificates details screen accessibility identifiers + } + + // MARK: View Transitions + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + adjustLayout() + } + + override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { + super.viewWillTransition(to: size, with: coordinator) + coordinator.animate(alongsideTransition: { _ in + self.adjustLayout() + }, completion: nil) + } + + private func adjustLayout() { + view.setNeedsLayout() + view.layoutIfNeeded() + } + + private func updateViewDetails() { + self.certificatesTableView.reloadData() + } + + // MARK: - Actions + private func currentTheme() -> Theme { + return themeManager.getCurrentTheme(for: windowUUID) + } +} + +// MARK: - Themable +extension CertificatesViewController { + func applyTheme() { + let theme = currentTheme() + overrideUserInterfaceStyle = theme.type.getInterfaceStyle() + view.backgroundColor = theme.colors.layer5 + titleLabel.textColor = theme.colors.textPrimary + tableViewTopSpacer.backgroundColor = theme.colors.layer1 + setNeedsStatusBarAppearanceUpdate() + } +} diff --git a/firefox-ios/Client/Frontend/TrackingProtection/CertificatesViewModel.swift b/firefox-ios/Client/Frontend/TrackingProtection/CertificatesViewModel.swift new file mode 100644 index 000000000000..e8c9fb56e3ef --- /dev/null +++ b/firefox-ios/Client/Frontend/TrackingProtection/CertificatesViewModel.swift @@ -0,0 +1,65 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import X509 + +// TODO FXIOS-9853 ⁃ Enhanced Tracking Protection Redux +final class CertificatesViewModel { + let topLevelDomain: String + let title: String + let URL: String + var certificates = [Certificate]() + var selectedCertificateIndex: Int = 0 + + init(topLevelDomain: String, title: String, URL: String, certificates: [Certificate]) { + self.topLevelDomain = topLevelDomain + self.title = title + self.URL = URL + self.certificates = certificates + } + + func getCertificateValues(from input: String) -> [String: String] { + var result = [String: String]() + let components = input.split(separator: ",") + for component in components { + let parts = component.split(separator: "=", maxSplits: 1, omittingEmptySubsequences: false) + if parts.count == 2 { + let key = parts[0].trimmingCharacters(in: .whitespacesAndNewlines) + let value = parts[1].trimmingCharacters(in: .whitespacesAndNewlines) + result[key] = value + } + } + return result + } + + func getDNSNamesList(from input: String) -> [String] { + let pattern = #"DNSName\("([^"]+)"\)"# + var dnsNames: [String] = [] + do { + let regex = try NSRegularExpression(pattern: pattern, options: []) + let matches = regex.matches(in: input, options: [], range: NSRange(location: 0, length: input.utf16.count)) + for match in matches { + if let range = Range(match.range(at: 1), in: input) { + let dnsNameString = String(input[range]) + dnsNames.append(dnsNameString) + } + } + return dnsNames + } catch { + return [] + } + } + + func getDNSNames(for certificate: Certificate) -> CertificateItems { + var dnsNames: CertificateItems = [] + if let certificateExtension = + certificate.extensions.first(where: { $0.description.contains("SubjectAlternativeNames") }) { + for dnsName in getDNSNamesList(from: certificateExtension.description) { + dnsNames.append((.Menu.EnhancedTrackingProtection.certificateSubjectAltNamesDNSName, dnsName)) + } + } + return dnsNames + } +} diff --git a/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionModel.swift b/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionModel.swift index d5212ba61c85..e52ba3abbe1c 100644 --- a/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionModel.swift +++ b/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionModel.swift @@ -20,6 +20,7 @@ class TrackingProtectionModel { let displayTitle: String let connectionSecure: Bool let globalETPIsEnabled: Bool + private var selectedTab: Tab? let clearCookiesButtonTitle: String = .Menu.EnhancedTrackingProtection.clearDataButtonTitle let clearCookiesButtonA11yId: String = AccessibilityIdentifiers.EnhancedTrackingProtection.MainScreen.clearCookiesButton @@ -59,21 +60,35 @@ class TrackingProtectionModel { return connectionSecure ? secureStatusString : unsecureStatusString } - var connectionDetailsTitle: String { - let titleOn = String(format: String.Menu.EnhancedTrackingProtection.onTitle, AppName.shortName.rawValue) - return connectionSecure ? titleOn : .Menu.EnhancedTrackingProtection.offTitle + var connectionDetailsTitle: String { + if !isProtectionEnabled { + return .Menu.EnhancedTrackingProtection.offTitle + } + if !connectionSecure { + return .Menu.EnhancedTrackingProtection.onNotSecureTitle + } + return String(format: String.Menu.EnhancedTrackingProtection.onTitle, AppName.shortName.rawValue) } - let protectionOnHeaderString = String.Menu.EnhancedTrackingProtection.onHeader - let protectionOffHeaderString = String.Menu.EnhancedTrackingProtection.offHeader - var connectionDetailsHeader: String { - return connectionSecure ? protectionOnHeaderString : protectionOffHeaderString + var connectionDetailsHeader: String { + if !isProtectionEnabled { + return String(format: .Menu.EnhancedTrackingProtection.offHeader, AppName.shortName.rawValue) + } + if !connectionSecure { + return .Menu.EnhancedTrackingProtection.onNotSecureHeader + } + return .Menu.EnhancedTrackingProtection.onHeader } - let protectionOnImage = UIImage(named: ImageIdentifiers.TrackingProtection.protectionOn) - let protectionOffImage = UIImage(named: ImageIdentifiers.TrackingProtection.protectionOff) - var connectionDetailsImage: UIImage? { - return connectionSecure ? protectionOnImage : protectionOffImage + var isProtectionEnabled = false + var connectionDetailsImage: UIImage? { + if !isProtectionEnabled { + return UIImage(named: ImageIdentifiers.TrackingProtection.protectionOff) + } + if !connectionSecure { + return UIImage(named: ImageIdentifiers.TrackingProtection.protectionAlert) + } + return UIImage(named: ImageIdentifiers.TrackingProtection.protectionOn) } var isSiteETPEnabled: Bool { @@ -90,16 +105,27 @@ class TrackingProtectionModel { connectionSecure: Bool, globalETPIsEnabled: Bool, contentBlockerStatus: BlockerStatus, - contentBlockerStats: TPPageStats?) { + contentBlockerStats: TPPageStats?, + selectedTab: Tab?) { self.url = url self.displayTitle = displayTitle self.connectionSecure = connectionSecure self.globalETPIsEnabled = globalETPIsEnabled self.contentBlockerStatus = contentBlockerStatus self.contentBlockerStats = contentBlockerStats + self.selectedTab = selectedTab } // MARK: - Helpers + func getConnectionDetailsBackgroundColor(theme: Theme) -> UIColor { + if !isProtectionEnabled { + return theme.colors.layerAccentPrivateNonOpaque + } + if !connectionSecure { + return theme.colors.layerRatingFSubdued + } + return theme.colors.layer3 + } func getConnectionStatusImage(themeType: ThemeType) -> UIImage { if connectionSecure { @@ -110,6 +136,13 @@ class TrackingProtectionModel { } } + func getCertificatesViewModel() -> CertificatesViewModel { + return CertificatesViewModel(topLevelDomain: websiteTitle, + title: displayTitle, + URL: url.absoluteDisplayString, + certificates: certificates) + } + func toggleSiteSafelistStatus() { TelemetryWrapper.recordEvent(category: .action, method: .add, object: .trackingProtectionSafelist) ContentBlocker.shared.safelist(enable: contentBlockerStatus != .safelisted, url: url) { @@ -129,12 +162,15 @@ class TrackingProtectionModel { let confirmAction = UIAlertAction(title: clearCookiesAlertButton, style: .destructive) { [weak self] _ in - self?.clearCookiesAndSiteData() + self?.clearCookiesAndSiteData(cookiesClearable: CookiesClearable(), siteDataClearable: SiteDataClearable()) + self?.selectedTab?.webView?.reload() } alert.addAction(confirmAction) controller.present(alert, animated: true, completion: nil) } - func clearCookiesAndSiteData() { + func clearCookiesAndSiteData(cookiesClearable: Clearable, siteDataClearable: Clearable) { + _ = cookiesClearable.clear() + _ = siteDataClearable.clear() } } diff --git a/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionViewController.swift b/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionViewController.swift new file mode 100644 index 000000000000..ac0fa9147973 --- /dev/null +++ b/firefox-ios/Client/Frontend/TrackingProtection/TrackingProtectionViewController.swift @@ -0,0 +1,978 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Shared +import UIKit +import Common +import ComponentLibrary +import SiteImageView + +struct TPMenuUX { + struct Fonts { + static let websiteTitle: TextStyling = FXFontStyles.Regular.headline + static let viewTitleLabels: TextStyling = FXFontStyles.Regular.body + static let detailsLabel: TextStyling = FXFontStyles.Regular.caption1 + static let minorInfoLabel: TextStyling = FXFontStyles.Regular.subheadline + } + + struct UX { + static let baseCellHeight: CGFloat = 44 + static let popoverTopDistance: CGFloat = 16 + static let horizontalMargin: CGFloat = 16 + static let viewCornerRadius: CGFloat = 8 + static let headerLabelDistance: CGFloat = 2.0 + static let headerLinesLimit: Int = 2 + static let foxImageSize: CGFloat = 100 + static let iconSize: CGFloat = 24 + static let protectionViewBottomSpacing: CGFloat = 70 + static let siteDomainLabelsVerticalSpacing: CGFloat = 12 + static let connectionDetailsLabelBottomSpacing: CGFloat = 28 + static let connectionDetailsHeaderMargins: CGFloat = 8 + static let faviconImageSize: CGFloat = 40 + static let closeButtonSize: CGFloat = 30 + static let faviconCornerRadius: CGFloat = 5 + static let scrollContentHorizontalPadding: CGFloat = 16 + + static let trackersLabelConstraintConstant = 16.0 + static let connectionStatusLabelConstraintConstant = 16.0 + static let toggleLabelsContainerConstraintConstant = 16.0 + + static let clearDataButtonCornerRadius: CGFloat = 12 + static let clearDataButtonBorderWidth: CGFloat = 1 + static let settingsLinkButtonBottomSpacing: CGFloat = 32 + static let connectionDetailsStackSpacing = 15.0 + + static let modalMenuCornerRadius: CGFloat = 12 + struct Line { + static let height: CGFloat = 1 + } + struct TrackingDetails { + static let baseDistance: CGFloat = 20 + static let imageMargins: CGFloat = 10 + static let bottomDistance: CGFloat = 350 + static let viewCertButtonTopDistance: CGFloat = 8.0 + } + struct BlockedTrackers { + static let headerDistance: CGFloat = 8 + } + } +} + +protocol TrackingProtectionMenuDelegate: AnyObject { + func settingsOpenPage(settings: Route.SettingsSection) + func didFinish() +} + +// TODO: FXIOS-9726 #21369 - Refactor/Split TrackingProtectionViewController UI into more custom views +class TrackingProtectionViewController: UIViewController, Themeable, Notifiable, UIScrollViewDelegate, BottomSheetChild { + var themeManager: ThemeManager + var themeObserver: NSObjectProtocol? + var notificationCenter: NotificationProtocol + let windowUUID: WindowUUID + var currentWindowUUID: UUID? { windowUUID } + + weak var enhancedTrackingProtectionMenuDelegate: TrackingProtectionMenuDelegate? + + private var faviconHeightConstraint: NSLayoutConstraint? + private var faviconWidthConstraint: NSLayoutConstraint? + private var foxImageHeightConstraint: NSLayoutConstraint? + private var shieldImageHeightConstraint: NSLayoutConstraint? + private var lockImageHeightConstraint: NSLayoutConstraint? + private var trackersLabelTopConstraint: NSLayoutConstraint? + private var trackersLabelBottomConstraint: NSLayoutConstraint? + + private var trackersArrowHeightConstraint: NSLayoutConstraint? + private var connectionArrowHeightConstraint: NSLayoutConstraint? + + private lazy var scrollView: UIScrollView = .build { scrollView in + scrollView.translatesAutoresizingMaskIntoConstraints = false + scrollView.showsVerticalScrollIndicator = false + } + + private let baseView: UIView = .build { view in + view.translatesAutoresizingMaskIntoConstraints = false + } + + // MARK: UI components Header View + private let headerContainer: UIView = .build { view in + view.backgroundColor = .clear + } + + private lazy var headerLabelsContainer: UIStackView = .build { stack in + stack.backgroundColor = .clear + stack.alignment = .leading + stack.axis = .vertical + stack.spacing = TPMenuUX.UX.headerLabelDistance + } + + private var favicon: FaviconImageView = .build { favicon in + favicon.manuallySetImage( + UIImage(named: StandardImageIdentifiers.Large.globe)?.withRenderingMode(.alwaysTemplate) ?? UIImage()) + } + + private let siteDisplayTitleLabel: UILabel = .build { label in + label.font = FXFontStyles.Regular.headline.scaledFont() + label.numberOfLines = TPMenuUX.UX.headerLinesLimit + label.adjustsFontForContentSizeCategory = true + label.translatesAutoresizingMaskIntoConstraints = false + } + + private let siteDomainLabel: UILabel = .build { label in + label.font = FXFontStyles.Regular.caption1.scaledFont() + label.numberOfLines = 0 + label.adjustsFontForContentSizeCategory = true + label.translatesAutoresizingMaskIntoConstraints = false + } + + private var closeButton: CloseButton = .build { button in + button.layer.cornerRadius = 0.5 * TPMenuUX.UX.closeButtonSize + } + + // MARK: Connection Details View + private let connectionDetailsHeaderView: UIView = .build { view in + view.layer.cornerRadius = TPMenuUX.UX.viewCornerRadius + view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] + view.layer.masksToBounds = true + } + private let connectionDetailsContentView: UIView = .build { view in + view.layer.cornerRadius = TPMenuUX.UX.viewCornerRadius + view.layer.masksToBounds = true + } + + private let foxStatusImage: UIImageView = .build { image in + image.contentMode = .scaleAspectFit + image.clipsToBounds = true + } + + private var connectionDetailsLabelsContainer: UIStackView = .build { stack in + stack.backgroundColor = .clear + stack.distribution = .fillProportionally + stack.alignment = .leading + stack.axis = .vertical + stack.spacing = TPMenuUX.UX.connectionDetailsStackSpacing + } + + private var connectionDetailsTitleLabel: UILabel = .build { label in + label.font = FXFontStyles.Bold.subheadline.scaledFont() + label.numberOfLines = 0 + label.adjustsFontForContentSizeCategory = true + } + + private let connectionDetailsStatusLabel: UILabel = .build { label in + label.font = FXFontStyles.Regular.subheadline.scaledFont() + label.numberOfLines = 0 + label.adjustsFontForContentSizeCategory = true + } + + // MARK: Blocked Trackers View + private var trackersConnectionContainer: UIStackView = .build { stack in + stack.backgroundColor = .clear + stack.distribution = .fillProportionally + stack.alignment = .leading + stack.axis = .vertical + } + + private let trackersView: UIView = .build { view in + view.backgroundColor = .clear + } + + private let shieldImage: UIImageView = .build { image in + image.contentMode = .scaleAspectFit + image.clipsToBounds = true + image.layer.masksToBounds = true + image.image = UIImage(imageLiteralResourceName: StandardImageIdentifiers.Large.shield) + .withRenderingMode(.alwaysTemplate) + } + + private let trackersLabel: UILabel = .build { label in + label.font = FXFontStyles.Regular.body.scaledFont() + label.numberOfLines = 0 + label.adjustsFontForContentSizeCategory = true + } + + private let trackersDetailArrow: UIImageView = .build { image in + image.image = UIImage(imageLiteralResourceName: StandardImageIdentifiers.Large.chevronLeft) + .withRenderingMode(.alwaysTemplate) + .imageFlippedForRightToLeftLayoutDirection() + image.transform = CGAffineTransform(rotationAngle: .pi) + } + + private let trackersHorizontalLine: UIView = .build { _ in } + private let trackersButton: UIButton = .build { button in } + + // MARK: Connection Status View + private let connectionView: UIView = .build { view in + view.backgroundColor = .clear + } + + private let connectionStatusImage: UIImageView = .build { image in + image.contentMode = .scaleAspectFit + image.clipsToBounds = true + image.layer.masksToBounds = true + } + + private let connectionStatusLabel: UILabel = .build { label in + label.font = FXFontStyles.Regular.body.scaledFont() + label.numberOfLines = 0 + label.adjustsFontForContentSizeCategory = true + } + + private let connectionDetailArrow: UIImageView = .build { image in + image.image = UIImage(imageLiteralResourceName: StandardImageIdentifiers.Large.chevronLeft) + .withRenderingMode(.alwaysTemplate) + .imageFlippedForRightToLeftLayoutDirection() + image.transform = CGAffineTransform(rotationAngle: .pi) + } + + private let connectionButton: UIButton = .build { button in } + private let connectionHorizontalLine: UIView = .build { _ in } + + // MARK: Toggle View + private let toggleView: UIView = .build { view in + view.layer.cornerRadius = TPMenuUX.UX.viewCornerRadius + view.layer.maskedCorners = [.layerMinXMaxYCorner, .layerMaxXMaxYCorner] + view.backgroundColor = .clear + view.translatesAutoresizingMaskIntoConstraints = false + } + private let toggleLabelsContainer: UIStackView = .build { stack in + stack.backgroundColor = .clear + stack.distribution = .fill + stack.alignment = .leading + stack.axis = .vertical + stack.spacing = TPMenuUX.UX.headerLabelDistance + } + + private let toggleLabel: UILabel = .build { label in + label.font = FXFontStyles.Regular.body.scaledFont() + label.numberOfLines = 0 + label.adjustsFontForContentSizeCategory = true + } + + private let toggleSwitch: UISwitch = .build { toggleSwitch in + toggleSwitch.isEnabled = true + } + + private let toggleStatusLabel: UILabel = .build { label in + label.font = FXFontStyles.Regular.caption1.scaledFont() + label.numberOfLines = 0 + label.adjustsFontForContentSizeCategory = true + } + + // MARK: Clear Cookies View + private lazy var clearCookiesButton: TrackingProtectionButton = .build { button in + button.titleLabel?.textAlignment = .left + button.titleLabel?.numberOfLines = 0 + button.layer.cornerRadius = TPMenuUX.UX.clearDataButtonCornerRadius + button.layer.borderWidth = TPMenuUX.UX.clearDataButtonBorderWidth + button.addTarget(self, action: #selector(self.didTapClearCookiesAndSiteData), for: .touchUpInside) + } + + // MARK: Protection setting view + private lazy var settingsLinkButton: LinkButton = .build { button in + button.titleLabel?.textAlignment = .left + button.titleLabel?.numberOfLines = 0 + button.addTarget(self, action: #selector(self.protectionSettingsTapped), for: .touchUpInside) + } + + private var constraints = [NSLayoutConstraint]() + + // MARK: - Variables + + private var viewModel: TrackingProtectionModel + private var hasSetPointOrigin = false + private var pointOrigin: CGPoint? + var asPopover = false + + private var toggleContainerShouldBeHidden: Bool { + return !viewModel.globalETPIsEnabled + } + + private var protectionViewTopConstraint: NSLayoutConstraint? + + // MARK: - View lifecycle + + init(viewModel: TrackingProtectionModel, + windowUUID: WindowUUID, + themeManager: ThemeManager = AppContainer.shared.resolve(), + notificationCenter: NotificationProtocol = NotificationCenter.default) { + self.viewModel = viewModel + self.windowUUID = windowUUID + self.themeManager = themeManager + self.notificationCenter = notificationCenter + super.init(nibName: nil, bundle: nil) + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + if !asPopover { + addGestureRecognizer() + } + setupView() + listenForThemeChange(view) + setupNotifications(forObserver: self, + observing: [.DynamicFontChanged]) + scrollView.delegate = self + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + self.view.invalidateIntrinsicContentSize() // Adjusts size based on content. + if !hasSetPointOrigin { + hasSetPointOrigin = true + pointOrigin = self.view.frame.origin + } + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + updateViewDetails() + updateProtectionViewStatus() + applyTheme() + getCertificates(for: viewModel.url) { [weak self] certificates in + if let certs = certificates { + self?.viewModel.certificates = certs + } + } + } + + private func setupView() { + constraints.removeAll() + + setupHeaderView() + setupContentView() + setupConnectionHeaderView() + setupBlockedTrackersView() + setupConnectionStatusView() + setupToggleView() + setupClearCookiesButton() + setupProtectionSettingsView() + setupViewActions() + + NSLayoutConstraint.activate(constraints) + setupAccessibilityIdentifiers() + } + + // MARK: Content View + private func setupContentView() { + view.addSubview(scrollView) + + let scrollViewConstraints = [ + scrollView.topAnchor.constraint(equalTo: headerContainer.bottomAnchor), + scrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), + scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor) + ] + + constraints.append(contentsOf: scrollViewConstraints) + scrollView.addSubview(baseView) + + let baseViewConstraints = [ + baseView.topAnchor.constraint(equalTo: scrollView.topAnchor), + baseView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor), + baseView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor), + baseView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor), + baseView.widthAnchor.constraint(equalTo: scrollView.widthAnchor) + ] + + constraints.append(contentsOf: baseViewConstraints) + } + + // MARK: Header Setup + private func setupHeaderView() { + headerLabelsContainer.addArrangedSubview(siteDisplayTitleLabel) + headerLabelsContainer.addArrangedSubview(siteDomainLabel) + + headerContainer.addSubviews(favicon, headerLabelsContainer, closeButton) + view.addSubview(headerContainer) + + faviconHeightConstraint = favicon.heightAnchor.constraint(equalToConstant: TPMenuUX.UX.faviconImageSize) + faviconWidthConstraint = favicon.widthAnchor.constraint(equalToConstant: TPMenuUX.UX.faviconImageSize) + let topDistance = asPopover ? TPMenuUX.UX.popoverTopDistance : 0 + + let headerConstraints = [ + headerContainer.leadingAnchor.constraint(equalTo: view.leadingAnchor), + headerContainer.trailingAnchor.constraint(equalTo: view.trailingAnchor), + headerContainer.topAnchor.constraint( + equalTo: view.safeAreaLayoutGuide.topAnchor, + constant: topDistance + ), + + favicon.leadingAnchor.constraint( + equalTo: headerContainer.leadingAnchor, + constant: TPMenuUX.UX.horizontalMargin + ), + favicon.centerYAnchor.constraint(equalTo: headerContainer.centerYAnchor), + faviconHeightConstraint!, + faviconWidthConstraint!, + + headerLabelsContainer.topAnchor.constraint( + equalTo: headerContainer.topAnchor, + constant: TPMenuUX.UX.siteDomainLabelsVerticalSpacing + ), + headerLabelsContainer.bottomAnchor.constraint( + equalTo: headerContainer.bottomAnchor, + constant: -TPMenuUX.UX.siteDomainLabelsVerticalSpacing + ), + headerLabelsContainer.leadingAnchor.constraint( + equalTo: favicon.trailingAnchor, + constant: TPMenuUX.UX.siteDomainLabelsVerticalSpacing + ), + headerLabelsContainer.trailingAnchor.constraint( + equalTo: closeButton.leadingAnchor, + constant: -TPMenuUX.UX.horizontalMargin + ), + + closeButton.trailingAnchor.constraint( + equalTo: headerContainer.trailingAnchor, + constant: -TPMenuUX.UX.horizontalMargin + ), + closeButton.topAnchor.constraint( + equalTo: headerContainer.topAnchor, + constant: TPMenuUX.UX.horizontalMargin + ), + closeButton.heightAnchor.constraint(greaterThanOrEqualToConstant: TPMenuUX.UX.closeButtonSize), + closeButton.widthAnchor.constraint(greaterThanOrEqualToConstant: TPMenuUX.UX.closeButtonSize), + ] + + constraints.append(contentsOf: headerConstraints) + } + + // MARK: Connection Status Header Setup + private func setupConnectionHeaderView() { + connectionDetailsLabelsContainer.addArrangedSubview(connectionDetailsTitleLabel) + connectionDetailsLabelsContainer.addArrangedSubview(connectionDetailsStatusLabel) + connectionDetailsContentView.addSubviews(foxStatusImage, connectionDetailsLabelsContainer) + connectionDetailsHeaderView.addSubview(connectionDetailsContentView) + + baseView.addSubviews(connectionDetailsHeaderView) + let connectionHeaderConstraints = [ + // Section + connectionDetailsHeaderView.leadingAnchor.constraint( + equalTo: view.leadingAnchor, + constant: TPMenuUX.UX.horizontalMargin + ), + connectionDetailsHeaderView.trailingAnchor.constraint( + equalTo: view.trailingAnchor, + constant: -TPMenuUX.UX.horizontalMargin + ), + connectionDetailsHeaderView.topAnchor.constraint(equalTo: baseView.topAnchor, + constant: TPMenuUX.UX.connectionDetailsHeaderMargins), + // Content + connectionDetailsContentView.leadingAnchor.constraint( + equalTo: connectionDetailsHeaderView.leadingAnchor, + constant: TPMenuUX.UX.connectionDetailsHeaderMargins + ), + connectionDetailsContentView.trailingAnchor.constraint( + equalTo: connectionDetailsHeaderView.trailingAnchor, + constant: -TPMenuUX.UX.connectionDetailsHeaderMargins + ), + connectionDetailsContentView.topAnchor.constraint(equalTo: connectionDetailsHeaderView.topAnchor, + constant: TPMenuUX.UX.connectionDetailsHeaderMargins), + connectionDetailsContentView.bottomAnchor.constraint(equalTo: connectionDetailsHeaderView.bottomAnchor, + constant: -TPMenuUX.UX.connectionDetailsHeaderMargins / 2), + // Image + foxStatusImage.leadingAnchor.constraint( + equalTo: connectionDetailsContentView.leadingAnchor, + constant: TPMenuUX.UX.horizontalMargin + ), + foxStatusImage.topAnchor.constraint( + equalTo: connectionDetailsContentView.topAnchor, + constant: TPMenuUX.UX.horizontalMargin + ), + foxStatusImage.heightAnchor.constraint(equalToConstant: TPMenuUX.UX.foxImageSize), + foxStatusImage.widthAnchor.constraint(equalToConstant: TPMenuUX.UX.foxImageSize), + + // Labels + connectionDetailsLabelsContainer.topAnchor.constraint( + equalTo: connectionDetailsContentView.topAnchor, + constant: TPMenuUX.UX.horizontalMargin + ), + connectionDetailsLabelsContainer.bottomAnchor.constraint( + equalTo: connectionDetailsContentView.bottomAnchor, + constant: -TPMenuUX.UX.connectionDetailsLabelBottomSpacing / 2 + ), + connectionDetailsLabelsContainer.leadingAnchor.constraint(equalTo: foxStatusImage.trailingAnchor, + constant: TPMenuUX.UX.siteDomainLabelsVerticalSpacing), + connectionDetailsLabelsContainer.trailingAnchor.constraint(equalTo: + connectionDetailsContentView.trailingAnchor, + constant: -TPMenuUX.UX.horizontalMargin), + connectionDetailsLabelsContainer.heightAnchor.constraint(greaterThanOrEqualToConstant: TPMenuUX.UX.foxImageSize), + ] + + constraints.append(contentsOf: connectionHeaderConstraints) + } + + // MARK: Blocked Trackers Setup + private func setupBlockedTrackersView() { + trackersView.addSubviews(shieldImage, trackersLabel, trackersDetailArrow, trackersButton, trackersHorizontalLine) + baseView.addSubview(trackersConnectionContainer) + trackersConnectionContainer.addArrangedSubview(trackersView) + + shieldImageHeightConstraint = shieldImage.heightAnchor.constraint(equalToConstant: TPMenuUX.UX.iconSize) + trackersArrowHeightConstraint = trackersDetailArrow.heightAnchor.constraint( + equalToConstant: TPMenuUX.UX.iconSize + ) + + trackersLabelTopConstraint = trackersLabel.topAnchor.constraint( + equalTo: trackersView.topAnchor, + constant: TPMenuUX.UX.trackersLabelConstraintConstant) + trackersLabelBottomConstraint = trackersLabel.bottomAnchor.constraint( + equalTo: trackersView.bottomAnchor, + constant: -TPMenuUX.UX.trackersLabelConstraintConstant) + + let blockedTrackersConstraints = [ + trackersView.leadingAnchor.constraint( + equalTo: view.leadingAnchor, + constant: TPMenuUX.UX.horizontalMargin + ), + trackersView.trailingAnchor.constraint( + equalTo: view.trailingAnchor, + constant: -TPMenuUX.UX.horizontalMargin + ), + trackersView.topAnchor.constraint(equalTo: connectionDetailsHeaderView.bottomAnchor, constant: 0), + + shieldImage.leadingAnchor.constraint( + equalTo: trackersView.leadingAnchor, + constant: TPMenuUX.UX.horizontalMargin + ), + shieldImage.centerYAnchor.constraint(equalTo: trackersView.centerYAnchor), + shieldImage.heightAnchor.constraint(equalTo: shieldImage.widthAnchor), + shieldImageHeightConstraint!, + trackersLabel.leadingAnchor.constraint( + equalTo: shieldImage.trailingAnchor, + constant: TPMenuUX.UX.horizontalMargin + ), + trackersLabel.trailingAnchor.constraint( + equalTo: trackersDetailArrow.leadingAnchor, + constant: TPMenuUX.UX.horizontalMargin + ), + + trackersDetailArrow.trailingAnchor.constraint( + equalTo: connectionView.trailingAnchor, + constant: -TPMenuUX.UX.horizontalMargin + ), + trackersDetailArrow.centerYAnchor.constraint(equalTo: trackersView.centerYAnchor), + trackersArrowHeightConstraint!, + trackersDetailArrow.widthAnchor.constraint(equalTo: trackersDetailArrow.heightAnchor), + + trackersButton.leadingAnchor.constraint(equalTo: trackersView.leadingAnchor), + trackersButton.topAnchor.constraint(equalTo: trackersView.topAnchor), + trackersButton.trailingAnchor.constraint(equalTo: trackersView.trailingAnchor), + trackersButton.bottomAnchor.constraint(equalTo: trackersView.bottomAnchor), + + trackersHorizontalLine.leadingAnchor.constraint(equalTo: trackersLabel.leadingAnchor), + trackersHorizontalLine.trailingAnchor.constraint(equalTo: trackersView.trailingAnchor, + constant: -TPMenuUX.UX.connectionDetailsHeaderMargins), + trackersHorizontalLine.heightAnchor.constraint(equalToConstant: TPMenuUX.UX.Line.height), + trackersView.bottomAnchor.constraint(equalTo: trackersHorizontalLine.bottomAnchor), + ] + + if let trackersLabelTopConstraint, let trackersLabelBottomConstraint { + constraints.append(trackersLabelTopConstraint) + constraints.append(trackersLabelBottomConstraint) + } + + constraints.append(contentsOf: blockedTrackersConstraints) + } + + // MARK: Connection Status Setup + private func setupConnectionStatusView() { + connectionView.addSubviews(connectionStatusImage, connectionStatusLabel, connectionDetailArrow) + connectionView.addSubviews(connectionButton, connectionHorizontalLine) + trackersConnectionContainer.addArrangedSubview(connectionView) + + lockImageHeightConstraint = connectionStatusImage.widthAnchor.constraint( + equalToConstant: TPMenuUX.UX.iconSize + ) + connectionArrowHeightConstraint = connectionDetailArrow.heightAnchor.constraint( + equalToConstant: TPMenuUX.UX.iconSize + ) + let connectionConstraints = [ + connectionView.leadingAnchor.constraint( + equalTo: view.leadingAnchor, + constant: TPMenuUX.UX.horizontalMargin + ), + connectionView.trailingAnchor.constraint( + equalTo: view.trailingAnchor, + constant: -TPMenuUX.UX.horizontalMargin + ), + connectionView.topAnchor.constraint(equalTo: trackersView.bottomAnchor, constant: 0), + + connectionStatusImage.leadingAnchor.constraint( + equalTo: connectionView.leadingAnchor, + constant: TPMenuUX.UX.horizontalMargin + ), + connectionStatusImage.centerYAnchor.constraint(equalTo: connectionView.centerYAnchor), + lockImageHeightConstraint!, + connectionStatusImage.heightAnchor.constraint(equalTo: connectionView.widthAnchor), + + connectionStatusLabel.leadingAnchor.constraint( + equalTo: connectionStatusImage.trailingAnchor, + constant: TPMenuUX.UX.horizontalMargin + ), + connectionStatusLabel.topAnchor.constraint(equalTo: connectionView.topAnchor, + constant: TPMenuUX.UX.connectionStatusLabelConstraintConstant), + connectionStatusLabel.bottomAnchor.constraint(equalTo: connectionView.bottomAnchor, + constant: -TPMenuUX.UX.connectionStatusLabelConstraintConstant), + connectionStatusLabel.trailingAnchor.constraint( + equalTo: connectionDetailArrow.leadingAnchor, + constant: TPMenuUX.UX.horizontalMargin + ), + + connectionDetailArrow.trailingAnchor.constraint( + equalTo: connectionView.trailingAnchor, + constant: -TPMenuUX.UX.horizontalMargin + ), + connectionDetailArrow.centerYAnchor.constraint(equalTo: connectionView.centerYAnchor), + connectionArrowHeightConstraint!, + connectionDetailArrow.widthAnchor.constraint(equalTo: connectionDetailArrow.heightAnchor), + + connectionButton.leadingAnchor.constraint(equalTo: connectionView.leadingAnchor), + connectionButton.topAnchor.constraint(equalTo: connectionView.topAnchor), + connectionButton.trailingAnchor.constraint(equalTo: connectionView.trailingAnchor), + connectionButton.bottomAnchor.constraint(equalTo: connectionView.bottomAnchor), + + connectionHorizontalLine.leadingAnchor.constraint(equalTo: connectionView.leadingAnchor), + connectionHorizontalLine.trailingAnchor.constraint(equalTo: connectionView.trailingAnchor), + connectionHorizontalLine.heightAnchor.constraint(equalToConstant: TPMenuUX.UX.Line.height), + connectionView.bottomAnchor.constraint(equalTo: connectionHorizontalLine.bottomAnchor), + ] + + constraints.append(contentsOf: connectionConstraints) + } + + // MARK: Toggle View Setup + private func setupToggleView() { + toggleLabelsContainer.addArrangedSubview(toggleLabel) + toggleLabelsContainer.addArrangedSubview(toggleStatusLabel) + toggleView.addSubviews(toggleLabelsContainer, toggleSwitch) + baseView.addSubview(toggleView) + + let toggleConstraints = [ + toggleView.leadingAnchor.constraint(equalTo: baseView.leadingAnchor, + constant: TPMenuUX.UX.horizontalMargin), + toggleView.trailingAnchor.constraint(equalTo: baseView.trailingAnchor, + constant: -TPMenuUX.UX.horizontalMargin), + toggleView.topAnchor.constraint( + equalTo: connectionView.bottomAnchor, + constant: 0 + ), + + toggleLabelsContainer.leadingAnchor.constraint( + equalTo: toggleView.leadingAnchor, + constant: TPMenuUX.UX.horizontalMargin + ), + toggleLabelsContainer.trailingAnchor.constraint( + equalTo: toggleSwitch.leadingAnchor, + constant: -TPMenuUX.UX.horizontalMargin + ), + toggleLabelsContainer.topAnchor.constraint( + equalTo: toggleView.topAnchor, + constant: TPMenuUX.UX.toggleLabelsContainerConstraintConstant + ), + toggleLabelsContainer.bottomAnchor.constraint( + equalTo: toggleView.bottomAnchor, + constant: -TPMenuUX.UX.toggleLabelsContainerConstraintConstant + ), + + toggleSwitch.centerYAnchor.constraint(equalTo: toggleView.centerYAnchor), + toggleSwitch.trailingAnchor.constraint( + equalTo: toggleView.trailingAnchor, + constant: -TPMenuUX.UX.horizontalMargin + ) + ] + + constraints.append(contentsOf: toggleConstraints) + toggleSwitch.actions(forTarget: self, forControlEvent: .valueChanged) + view.bringSubviewToFront(toggleSwitch) + } + + // MARK: Clear Cookies Button Setup + private func setupClearCookiesButton() { + let clearCookiesViewModel = TrackingProtectionButtonModel(title: viewModel.clearCookiesButtonTitle, + a11yIdentifier: viewModel.clearCookiesButtonA11yId) + clearCookiesButton.configure(viewModel: clearCookiesViewModel) + baseView.addSubview(clearCookiesButton) + + let clearCookiesButtonConstraints = [ + clearCookiesButton.leadingAnchor.constraint( + equalTo: baseView.leadingAnchor, + constant: TPMenuUX.UX.horizontalMargin + ), + clearCookiesButton.trailingAnchor.constraint( + equalTo: baseView.trailingAnchor, + constant: -TPMenuUX.UX.horizontalMargin + ), + clearCookiesButton.topAnchor.constraint( + equalTo: toggleView.bottomAnchor, + constant: TPMenuUX.UX.horizontalMargin + ), + clearCookiesButton.bottomAnchor.constraint( + equalTo: settingsLinkButton.topAnchor, + constant: -TPMenuUX.UX.horizontalMargin + ), + ] + constraints.append(contentsOf: clearCookiesButtonConstraints) + } + + // MARK: Settings View Setup + private func setupProtectionSettingsView() { + let settingsButtonViewModel = LinkButtonViewModel(title: viewModel.settingsButtonTitle, + a11yIdentifier: viewModel.settingsA11yId) + settingsLinkButton.configure(viewModel: settingsButtonViewModel) + baseView.addSubviews(settingsLinkButton) + + let protectionConstraints = [ + settingsLinkButton.leadingAnchor.constraint(equalTo: baseView.leadingAnchor), + settingsLinkButton.trailingAnchor.constraint(equalTo: baseView.trailingAnchor), + settingsLinkButton.bottomAnchor.constraint( + equalTo: baseView.bottomAnchor, + constant: -TPMenuUX.UX.settingsLinkButtonBottomSpacing + ), + ] + + constraints.append(contentsOf: protectionConstraints) + } + + private func updateViewDetails() { + favicon.setFavicon(FaviconImageViewModel(siteURLString: viewModel.url.absoluteString, + faviconCornerRadius: TPMenuUX.UX.faviconCornerRadius)) + + siteDomainLabel.text = viewModel.websiteTitle + siteDisplayTitleLabel.text = viewModel.displayTitle + trackersLabel.text = getTrackerString() + shieldImage.image = UIImage(imageLiteralResourceName: StandardImageIdentifiers.Large.shield) + .withRenderingMode(.alwaysTemplate) + connectionStatusImage.image = viewModel.getConnectionStatusImage(themeType: currentTheme().type) + connectionStatusLabel.text = viewModel.connectionStatusString + toggleSwitch.isOn = viewModel.isSiteETPEnabled + viewModel.isProtectionEnabled = toggleSwitch.isOn + toggleLabel.text = .Menu.EnhancedTrackingProtection.switchTitle + toggleStatusLabel.text = toggleSwitch.isOn ? + .Menu.EnhancedTrackingProtection.switchOnText : .Menu.EnhancedTrackingProtection.switchOffText + connectionDetailsTitleLabel.text = viewModel.connectionDetailsTitle + connectionDetailsStatusLabel.text = viewModel.connectionDetailsHeader + foxStatusImage.image = viewModel.connectionDetailsImage + } + + private func getTrackerString() -> String { + if let trackersNumber = viewModel.contentBlockerStats?.total, trackersNumber > 0 { + return String(format: .Menu.EnhancedTrackingProtection.trackersBlockedLabel, + String(trackersNumber)) + } else { + return .Menu.EnhancedTrackingProtection.noTrackersLabel + } + } + + private func setupViewActions() { + closeButton.addTarget(self, action: #selector(closeButtonTapped), for: .touchUpInside) + connectionButton.addTarget(self, action: #selector(connectionDetailsTapped), for: .touchUpInside) + trackersButton.addTarget(self, action: #selector(blockedTrackersTapped), for: .touchUpInside) + toggleSwitch.addTarget(self, action: #selector(trackingProtectionToggleTapped), for: .valueChanged) + } + + // MARK: Notifications + func handleNotifications(_ notification: Notification) { + switch notification.name { + case .DynamicFontChanged: + adjustLayout() + default: break + } + } + + // MARK: View Transitions + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + adjustLayout() + } + + override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { + super.viewWillTransition(to: size, with: coordinator) + coordinator.animate(alongsideTransition: { _ in + self.adjustLayout() + }, completion: nil) + } + + // MARK: Accessibility + private func setupAccessibilityIdentifiers() { + foxStatusImage.accessibilityIdentifier = viewModel.foxImageA11yId + trackersDetailArrow.accessibilityIdentifier = viewModel.arrowImageA11yId + trackersLabel.accessibilityIdentifier = viewModel.trackersBlockedLabelA11yId + connectionDetailArrow.accessibilityIdentifier = viewModel.arrowImageA11yId + shieldImage.accessibilityIdentifier = viewModel.shieldImageA11yId + connectionStatusLabel.accessibilityIdentifier = viewModel.securityStatusLabelA11yId + toggleLabel.accessibilityIdentifier = viewModel.toggleViewTitleLabelA11yId + toggleStatusLabel.accessibilityIdentifier = viewModel.toggleViewBodyLabelA11yId + clearCookiesButton.accessibilityIdentifier = viewModel.clearCookiesButtonA11yId + settingsLinkButton.accessibilityIdentifier = viewModel.settingsA11yId + } + + private func adjustLayout() { + let faviconSize = TPMenuUX.UX.faviconImageSize // to avoid line length warnings + let iconSize = TPMenuUX.UX.iconSize + faviconHeightConstraint?.constant = min(UIFontMetrics.default.scaledValue(for: faviconSize), 2 * faviconSize) + faviconWidthConstraint?.constant = min(UIFontMetrics.default.scaledValue(for: faviconSize), 2 * faviconSize) + + shieldImageHeightConstraint?.constant = min(UIFontMetrics.default.scaledValue(for: iconSize), 2 * iconSize) + trackersArrowHeightConstraint?.constant = min(UIFontMetrics.default.scaledValue(for: iconSize), 2 * iconSize) + + lockImageHeightConstraint?.constant = min(UIFontMetrics.default.scaledValue(for: iconSize), 2 * iconSize) + connectionArrowHeightConstraint?.constant = min(UIFontMetrics.default.scaledValue(for: iconSize), 2 * iconSize) + + if #available(iOS 16.0, *), UIDevice.current.userInterfaceIdiom == .phone { + headerContainer.layoutIfNeeded() + scrollView.layoutIfNeeded() + let contentHeight = headerContainer.frame.height + scrollView.contentSize.height + let customDetent = UISheetPresentationController.Detent.custom { context in + return contentHeight + } + self.sheetPresentationController?.detents = [customDetent] + } + + view.setNeedsLayout() + view.layoutIfNeeded() + } + + // MARK: - Button actions + @objc + func closeButtonTapped() { + enhancedTrackingProtectionMenuDelegate?.didFinish() + } + + @objc + func connectionDetailsTapped() { + // TODO: FXIOS-9198 #20366 Enhanced Tracking Protection Connection details screen + // let detailsVC = TrackingProtectionDetailsViewController(with: viewModel.getDetailsViewModel(), + // windowUUID: windowUUID) + // detailsVC.modalPresentationStyle = .pageSheet + // self.present(detailsVC, animated: true) + } + + @objc + func blockedTrackersTapped() {} + + @objc + func trackingProtectionToggleTapped() { + // site is safelisted if site ETP is disabled + viewModel.toggleSiteSafelistStatus() + updateProtectionViewStatus() + } + + @objc + private func didTapClearCookiesAndSiteData() { + viewModel.onTapClearCookiesAndSiteData(controller: self) + } + + @objc + func protectionSettingsTapped() { + enhancedTrackingProtectionMenuDelegate?.settingsOpenPage(settings: .contentBlocker) + } + + // MARK: - Gesture Recognizer + private func addGestureRecognizer() { + let panGesture = UIPanGestureRecognizer(target: self, action: #selector(panGestureRecognizerAction)) + view.addGestureRecognizer(panGesture) + } + + @objc + func panGestureRecognizerAction(sender: UIPanGestureRecognizer) { + let translation = sender.translation(in: view) + let originalYPosition = self.view.frame.origin.y + let originalXPosition = self.view.frame.origin.x + + // Not allowing the user to drag the view upward + guard translation.y >= 0 else { return } + + // Setting x based on window calculation because we don't want + // users to move the frame side ways, only straight up or down + view.frame.origin = CGPoint(x: originalXPosition, + y: self.pointOrigin!.y + translation.y) + + if sender.state == .ended { + let dragVelocity = sender.velocity(in: view) + if dragVelocity.y >= 1300 { + enhancedTrackingProtectionMenuDelegate?.didFinish() + } else { + // Set back to original position of the view controller + UIView.animate(withDuration: 0.3) { + self.view.frame.origin = self.pointOrigin ?? CGPoint(x: originalXPosition, y: originalYPosition) + } + } + } + } + + private func currentTheme() -> Theme { + return themeManager.getCurrentTheme(for: windowUUID) + } + + func willDismiss() { + } + + // MARK: - Update Views + private func updateProtectionViewStatus() { + if toggleSwitch.isOn { + toggleStatusLabel.text = .Menu.EnhancedTrackingProtection.switchOnText + trackersView.isHidden = false + trackersLabelTopConstraint?.constant = TPMenuUX.UX.trackersLabelConstraintConstant + trackersLabelBottomConstraint?.constant = -TPMenuUX.UX.trackersLabelConstraintConstant + viewModel.isProtectionEnabled = true + } else { + toggleStatusLabel.text = .Menu.EnhancedTrackingProtection.switchOffText + trackersView.isHidden = true + trackersLabelTopConstraint?.constant = 0 + trackersLabelBottomConstraint?.constant = 0 + viewModel.isProtectionEnabled = false + } + + connectionDetailsContentView.backgroundColor = viewModel.getConnectionDetailsBackgroundColor(theme: currentTheme()) + foxStatusImage.image = viewModel.connectionDetailsImage + connectionDetailsTitleLabel.text = viewModel.connectionDetailsTitle + connectionDetailsStatusLabel.text = viewModel.connectionDetailsHeader + } +} + +// MARK: - Themable +extension TrackingProtectionViewController { + func applyTheme() { + let theme = currentTheme() + overrideUserInterfaceStyle = theme.type.getInterfaceStyle() + view.backgroundColor = theme.colors.layer1 + closeButton.backgroundColor = theme.colors.layer2 + let buttonImage = UIImage(named: StandardImageIdentifiers.Medium.cross)? + .tinted(withColor: theme.colors.iconSecondary) + closeButton.setImage(buttonImage, for: .normal) + connectionDetailsHeaderView.backgroundColor = theme.colors.layer2 + trackersView.backgroundColor = theme.colors.layer2 + trackersDetailArrow.tintColor = theme.colors.iconSecondary + shieldImage.tintColor = theme.colors.iconPrimary + connectionView.backgroundColor = theme.colors.layer2 + connectionDetailArrow.tintColor = theme.colors.iconSecondary + connectionStatusImage.image = viewModel.getConnectionStatusImage(themeType: theme.type) + connectionDetailsContentView.backgroundColor = toggleSwitch.isOn ? + currentTheme().colors.layerAccentPrivateNonOpaque : theme.colors.layer3 + headerContainer.tintColor = theme.colors.layer2 + siteDomainLabel.textColor = theme.colors.textSecondary + siteDisplayTitleLabel.textColor = theme.colors.textPrimary + if viewModel.connectionSecure { + connectionStatusImage.tintColor = theme.colors.iconPrimary + } + toggleView.backgroundColor = theme.colors.layer2 + toggleSwitch.tintColor = theme.colors.actionPrimary + toggleSwitch.onTintColor = theme.colors.actionPrimary + toggleStatusLabel.textColor = theme.colors.textSecondary + + trackersHorizontalLine.backgroundColor = theme.colors.borderPrimary + connectionHorizontalLine.backgroundColor = theme.colors.borderPrimary + + clearCookiesButton.applyTheme(theme: theme) + clearCookiesButton.layer.borderColor = theme.colors.borderPrimary.cgColor + settingsLinkButton.applyTheme(theme: theme) + setNeedsStatusBarAppearanceUpdate() + } +} diff --git a/firefox-ios/Client/Frontend/UIConstants.swift b/firefox-ios/Client/Frontend/UIConstants.swift index 8715cffeae82..3f2998378686 100644 --- a/firefox-ios/Client/Frontend/UIConstants.swift +++ b/firefox-ios/Client/Frontend/UIConstants.swift @@ -14,10 +14,6 @@ public struct UIConstants { static var ToolbarPadding: CGFloat = 17 static let ZoomPageBarHeight: CGFloat = 54 - // Static fonts - static let DefaultChromeSmallSize: CGFloat = 11 - static let DefaultChromeSmallFontBold = UIFont.boldSystemFont(ofSize: DefaultChromeSmallSize) - /// JPEG compression quality for persisted screenshots. Must be between 0-1. static let ScreenshotQuality: Float = 1 static let ActiveScreenshotQuality: CGFloat = 0.5 diff --git a/firefox-ios/Client/Frontend/Widgets/PhotonActionSheet/PhotonActionSheetProtocol.swift b/firefox-ios/Client/Frontend/Widgets/PhotonActionSheet/PhotonActionSheetProtocol.swift index 25d152fb0283..77321068211f 100644 --- a/firefox-ios/Client/Frontend/Widgets/PhotonActionSheet/PhotonActionSheetProtocol.swift +++ b/firefox-ios/Client/Frontend/Widgets/PhotonActionSheet/PhotonActionSheetProtocol.swift @@ -64,15 +64,8 @@ extension PhotonActionSheetProtocol { if let pasteboardContents = UIPasteboard.general.string { if let urlBar = view as? URLBarView { urlBar.enterOverlayMode(pasteboardContents, pasted: true, search: true) - } else if view is AddressToolbarContainer { - guard let uuid = view.currentWindowUUID else { return } - - let action = ToolbarAction( - url: URL(string: pasteboardContents), - windowUUID: uuid, - actionType: ToolbarActionType.urlDidChange - ) - store.dispatch(action) + } else if let toolbar = view as? AddressToolbarContainer { + toolbar.enterOverlayMode(pasteboardContents, pasted: true, search: true) } } } @@ -83,7 +76,7 @@ extension PhotonActionSheetProtocol { let currentURL = tabManager.selectedTab?.currentURL() if let url = tabManager.selectedTab?.canonicalURL?.displayURL ?? currentURL { UIPasteboard.general.url = url - SimpleToast().showAlertWithText(.AppMenu.AppMenuCopyURLConfirmMessage, + SimpleToast().showAlertWithText(.LegacyAppMenu.AppMenuCopyURLConfirmMessage, bottomContainer: alertContainer, theme: themeManager.getCurrentTheme(for: tabManager.windowUUID)) } @@ -105,9 +98,9 @@ extension PhotonActionSheetProtocol { let toggleActionTitle: String // swiftlint:disable line_length if defaultUAisDesktop { - toggleActionTitle = tab.changedUserAgent ? .AppMenu.AppMenuViewDesktopSiteTitleString : .AppMenu.AppMenuViewMobileSiteTitleString + toggleActionTitle = tab.changedUserAgent ? .LegacyAppMenu.AppMenuViewDesktopSiteTitleString : .LegacyAppMenu.AppMenuViewMobileSiteTitleString } else { - toggleActionTitle = tab.changedUserAgent ? .AppMenu.AppMenuViewMobileSiteTitleString : .AppMenu.AppMenuViewDesktopSiteTitleString + toggleActionTitle = tab.changedUserAgent ? .LegacyAppMenu.AppMenuViewMobileSiteTitleString : .LegacyAppMenu.AppMenuViewDesktopSiteTitleString } // swiftlint:enable line_length let toggleDesktopSite = SingleActionViewModel(title: toggleActionTitle, diff --git a/firefox-ios/Client/Frontend/Widgets/PhotonActionSheet/PhotonActionSheetView.swift b/firefox-ios/Client/Frontend/Widgets/PhotonActionSheet/PhotonActionSheetView.swift index 938ad685010c..a305455fc8ee 100644 --- a/firefox-ios/Client/Frontend/Widgets/PhotonActionSheet/PhotonActionSheetView.swift +++ b/firefox-ios/Client/Frontend/Widgets/PhotonActionSheet/PhotonActionSheetView.swift @@ -110,7 +110,7 @@ class PhotonActionSheetView: UIView, UIGestureRecognizerDelegate, ThemeApplicabl } private lazy var tabsLabel: UILabel = .build { label in - label.font = UIFont.boldSystemFont(ofSize: UIConstants.DefaultChromeSmallSize) + label.font = FXFontStyles.Bold.caption2.systemFont() } lazy var bottomBorder: UIView = .build { _ in } diff --git a/firefox-ios/Client/Frontend/Widgets/TabsButton.swift b/firefox-ios/Client/Frontend/Widgets/TabsButton.swift index 4c9b38a2d84d..4be0e0063ef1 100644 --- a/firefox-ios/Client/Frontend/Widgets/TabsButton.swift +++ b/firefox-ios/Client/Frontend/Widgets/TabsButton.swift @@ -9,7 +9,7 @@ import Common class TabsButton: UIButton, ThemeApplicable { struct UX { static let cornerRadius: CGFloat = 2 - static let titleFont: UIFont = UIConstants.DefaultChromeSmallFontBold + static let titleFont: UIFont = FXFontStyles.Bold.caption2.systemFont() static let insideButtonSize: CGFloat = 24 // Animation constants diff --git a/firefox-ios/Client/Info.plist b/firefox-ios/Client/Info.plist index dd7a19d43852..7a19c1cf64ee 100644 --- a/firefox-ios/Client/Info.plist +++ b/firefox-ios/Client/Info.plist @@ -28,7 +28,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 130.0 + 131.0 CFBundleSignature ???? CFBundleURLTypes diff --git a/firefox-ios/Client/Nimbus/NimbusFeatureFlagLayer.swift b/firefox-ios/Client/Nimbus/NimbusFeatureFlagLayer.swift index fa71c86b758e..192588067a45 100644 --- a/firefox-ios/Client/Nimbus/NimbusFeatureFlagLayer.swift +++ b/firefox-ios/Client/Nimbus/NimbusFeatureFlagLayer.swift @@ -13,9 +13,6 @@ final class NimbusFeatureFlagLayer { case .accountSettingsRedux: return checkAccountSettingsRedux(from: nimbus) - case .addressAutofill: - return checkAddressAutofill(from: nimbus) - case .addressAutofillEdit: return checkAddressAutofillEditing(from: nimbus) @@ -61,15 +58,24 @@ final class NimbusFeatureFlagLayer { case .microsurvey: return checkMicrosurveyFeature(from: nimbus) + case .nativeErrorPage: + return checkNativeErrorPageFeature(from: nimbus) + case .nightMode: return checkNightModeFeature(from: nimbus) + case .passwordGenerator: + return checkPasswordGeneratorFeature(from: nimbus) + case .preferSwitchToOpenTabOverDuplicate: return checkPreferSwitchToOpenTabOverDuplicate(from: nimbus) case .reduxSearchSettings: return checkReduxSearchSettingsFeature(from: nimbus) + case .closeRemoteTabs: + return checkCloseRemoteTabsFeature(from: nimbus) + case .reportSiteIssue: return checkGeneralFeature(for: featureID, from: nimbus) @@ -244,6 +250,10 @@ final class NimbusFeatureFlagLayer { return config.productAds } + private func checkPasswordGeneratorFeature(from nimbus: FxNimbus) -> Bool { + return nimbus.features.passwordGeneratorFeature.value().enabled + } + private func checkProductBackInStockFakespotFeature(from nimbus: FxNimbus) -> Bool { let config = nimbus.features.shopping2023.value() @@ -254,16 +264,10 @@ final class NimbusFeatureFlagLayer { return nimbus.features.homescreenFeature.value().preferSwitchToOpenTab } - private func checkAddressAutofill(from nimbus: FxNimbus) -> Bool { - let config = nimbus.features.addressAutofillFeature.value() - - return config.status - } - private func checkAddressAutofillEditing(from nimbus: FxNimbus) -> Bool { - let config = nimbus.features.addressAutofillFeature.value() + let config = nimbus.features.addressAutofillEdit.value() - return config.addressAutofillEdit + return config.status } private func checkZoomFeature(from nimbus: FxNimbus) -> Bool { @@ -298,4 +302,13 @@ final class NimbusFeatureFlagLayer { return config.enabled } + + private func checkCloseRemoteTabsFeature(from nimbus: FxNimbus) -> Bool { + let config = nimbus.features.remoteTabManagement.value() + return config.closeTabsEnabled + } + + private func checkNativeErrorPageFeature(from nimbus: FxNimbus) -> Bool { + return nimbus.features.nativeErrorPageFeature.value().enabled + } } diff --git a/firefox-ios/Client/TabManagement/Legacy/LegacyTabManager.swift b/firefox-ios/Client/TabManagement/Legacy/LegacyTabManager.swift index 0b177e689447..f3f14e6866ea 100644 --- a/firefox-ios/Client/TabManagement/Legacy/LegacyTabManager.swift +++ b/firefox-ios/Client/TabManagement/Legacy/LegacyTabManager.swift @@ -10,7 +10,8 @@ import Shared // MARK: - TabManagerDelegate protocol TabManagerDelegate: AnyObject { - func tabManager(_ tabManager: TabManager, didSelectedTabChange selected: Tab?, previous: Tab?, isRestoring: Bool) + // Must be called on the main thread + func tabManager(_ tabManager: TabManager, didSelectedTabChange selectedTab: Tab, previousTab: Tab?, isRestoring: Bool) func tabManager(_ tabManager: TabManager, didAddTab tab: Tab, placeNextToParentTab: Bool, isRestoring: Bool) func tabManager(_ tabManager: TabManager, didRemoveTab tab: Tab, isRestoring: Bool) @@ -21,7 +22,7 @@ protocol TabManagerDelegate: AnyObject { } extension TabManagerDelegate { - func tabManager(_ tabManager: TabManager, didSelectedTabChange selected: Tab?, previous: Tab?, isRestoring: Bool) {} + func tabManager(_ tabManager: TabManager, didSelectedTabChange selectedTab: Tab, previousTab: Tab?, isRestoring: Bool) {} func tabManager(_ tabManager: TabManager, didAddTab tab: Tab, placeNextToParentTab: Bool, isRestoring: Bool) {} func tabManager(_ tabManager: TabManager, didRemoveTab tab: Tab, isRestoring: Bool) {} @@ -205,7 +206,7 @@ class LegacyTabManager: NSObject, FeatureFlaggable, TabManager, TabEventHandler // MARK: - Webview configuration // A WKWebViewConfiguration used for normal tabs - private lazy var configuration: WKWebViewConfiguration = { + lazy var configuration: WKWebViewConfiguration = { return LegacyTabManager.makeWebViewConfig(isPrivate: false, prefs: profile.prefs) }() @@ -343,7 +344,6 @@ class LegacyTabManager: NSObject, FeatureFlaggable, TabManager, TabEventHandler // MARK: - Add tabs func addTab(_ request: URLRequest?, afterTab: Tab?, isPrivate: Bool) -> Tab { return addTab(request, - configuration: nil, afterTab: afterTab, flushToDisk: true, zombie: false, @@ -352,13 +352,11 @@ class LegacyTabManager: NSObject, FeatureFlaggable, TabManager, TabEventHandler @discardableResult func addTab(_ request: URLRequest! = nil, - configuration: WKWebViewConfiguration! = nil, afterTab: Tab? = nil, zombie: Bool = false, isPrivate: Bool = false ) -> Tab { return addTab(request, - configuration: configuration, afterTab: afterTab, flushToDisk: true, zombie: zombie, @@ -388,23 +386,18 @@ class LegacyTabManager: NSObject, FeatureFlaggable, TabManager, TabEventHandler } func addTab(_ request: URLRequest? = nil, - configuration: WKWebViewConfiguration? = nil, afterTab: Tab? = nil, flushToDisk: Bool, zombie: Bool, isPrivate: Bool = false ) -> Tab { - // Take the given configuration. Or if it was nil, take our default configuration for the current browsing mode. - let configuration: WKWebViewConfiguration = configuration ?? (isPrivate ? privateConfiguration : self.configuration) - - let tab = Tab(profile: profile, configuration: configuration, isPrivate: isPrivate, windowUUID: windowUUID) + let tab = Tab(profile: profile, isPrivate: isPrivate, windowUUID: windowUUID) configureTab(tab, request: request, afterTab: afterTab, flushToDisk: flushToDisk, zombie: zombie) return tab } func addPopupForParentTab(profile: Profile, parentTab: Tab, configuration: WKWebViewConfiguration) -> Tab { let popup = Tab(profile: profile, - configuration: configuration, isPrivate: parentTab.isPrivate, windowUUID: windowUUID) configureTab(popup, request: nil, afterTab: parentTab, flushToDisk: true, zombie: false, isPopup: true) @@ -454,7 +447,8 @@ class LegacyTabManager: NSObject, FeatureFlaggable, TabManager, TabEventHandler } if !zombie { - tab.createWebview() + let configuration = tab.isPrivate ? self.privateConfiguration : self.configuration + tab.createWebview(configuration: configuration) } tab.navigationDelegate = self.navDelegate @@ -639,6 +633,20 @@ class LegacyTabManager: NSObject, FeatureFlaggable, TabManager, TabEventHandler storeChanges() } + @MainActor + func removeTabs(by urls: [URL]) async { + let urls = Set(urls) + let tabsToRemove = normalTabs.filter { tab in + guard let url = tab.url else { return false } + return urls.contains(url) + } + for tab in tabsToRemove { + await withCheckedContinuation { continuation in + removeTab(tab) { continuation.resume() } + } + } + } + @MainActor func removeAllTabs(isPrivateMode: Bool) async { let currentModeTabs = tabs.filter { $0.isPrivate == isPrivateMode } diff --git a/firefox-ios/Client/TabManagement/Tab.swift b/firefox-ios/Client/TabManagement/Tab.swift index 7e8225d32636..a399902c9a44 100644 --- a/firefox-ios/Client/TabManagement/Tab.swift +++ b/firefox-ios/Client/TabManagement/Tab.swift @@ -127,6 +127,7 @@ class Tab: NSObject, ThemeApplicable { var adsProviderName: String = "" var hasHomeScreenshot = false var shouldScrollToTop = false + var isFindInPageMode = false private var logger: Logger @@ -194,7 +195,7 @@ class Tab: NSObject, ThemeApplicable { /// or not, it will resort to other displayable titles. var displayTitle: String { if self.isFxHomeTab { - return .AppMenu.AppMenuOpenHomePageTitleString + return .LegacyAppMenu.AppMenuOpenHomePageTitleString } if let lastTitle = lastTitle, !lastTitle.isEmpty { @@ -226,7 +227,7 @@ class Tab: NSObject, ThemeApplicable { var backUpName: String = "" // In case display title is empty if let baseDomain = baseDomain { - backUpName = baseDomain.contains("local") ? .AppMenu.AppMenuOpenHomePageTitleString : baseDomain + backUpName = baseDomain.contains("local") ? .LegacyAppMenu.AppMenuOpenHomePageTitleString : baseDomain } else if let url = url, let about = InternalURL(url)?.aboutComponent { backUpName = about } @@ -235,10 +236,16 @@ class Tab: NSObject, ThemeApplicable { } var canGoBack: Bool { + // FXIOS-9785 This could result in the back button never being enabled for restored tabs + assert(webView != nil, "We should not be trying to enable or disable the back button before the webView is set") + return webView?.canGoBack ?? false } var canGoForward: Bool { + // FXIOS-9785 This could result in the forward button never being enabled for restored tabs + assert(webView != nil, "We should not be trying to enable or disable the forward button before the webView is set") + return webView?.canGoForward ?? false } @@ -388,23 +395,21 @@ class Tab: NSObject, ThemeApplicable { // If this tab has been opened from another, its parent will point to the tab from which it was opened weak var parent: Tab? - fileprivate var contentScriptManager = TabContentScriptManager() + private var contentScriptManager = TabContentScriptManager() - fileprivate let configuration: WKWebViewConfiguration + private var configuration: WKWebViewConfiguration? /// Any time a tab tries to make requests to display a Javascript Alert and we are not the active /// tab instance, queue it for later until we become foregrounded. - fileprivate var alertQueue = [JSAlertInfo]() + private var alertQueue = [JSAlertInfo]() var profile: Profile init(profile: Profile, - configuration: WKWebViewConfiguration, isPrivate: Bool = false, windowUUID: WindowUUID, faviconHelper: SiteImageHandler = DefaultSiteImageHandler.factory(), logger: Logger = DefaultLogger.shared) { - self.configuration = configuration self.nightMode = false self.windowUUID = windowUUID self.noImageMode = false @@ -457,7 +462,8 @@ class Tab: NSObject, ThemeApplicable { } } - func createWebview(with restoreSessionData: Data? = nil) { + func createWebview(with restoreSessionData: Data? = nil, configuration: WKWebViewConfiguration) { + self.configuration = configuration if webView == nil { configuration.userContentController = WKUserContentController() configuration.allowsInlineMediaPlayback = true @@ -775,6 +781,16 @@ class Tab: NSObject, ThemeApplicable { bars.reversed().filter({ $0.snackbarClassIdentifier == snackbarClass }).forEach({ removeSnackbar($0) }) } + func setFindInPage(isBottomSearchBar: Bool, doesFindInPageBarExist: Bool) { + if #available(iOS 16, *) { + guard let webView = self.webView, + let findInteraction = webView.findInteraction else { return } + isFindInPageMode = findInteraction.isFindNavigatorVisible && isBottomSearchBar + } else { + isFindInPageMode = doesFindInPageBarExist && isBottomSearchBar + } + } + func setScreenshot(_ screenshot: UIImage?) { self.screenshot = screenshot } diff --git a/firefox-ios/Client/TabManagement/TabManager.swift b/firefox-ios/Client/TabManagement/TabManager.swift index 66b9cddce91a..c5dcef4f3335 100644 --- a/firefox-ios/Client/TabManagement/TabManager.swift +++ b/firefox-ios/Client/TabManagement/TabManager.swift @@ -62,7 +62,6 @@ protocol TabManager: AnyObject { @discardableResult func addTab(_ request: URLRequest!, - configuration: WKWebViewConfiguration!, afterTab: Tab?, zombie: Bool, isPrivate: Bool) -> Tab @@ -83,6 +82,9 @@ protocol TabManager: AnyObject { /// Undo close all tabs, it will restore the tabs that were backed up when the close action was called. func undoCloseAllTabs() + /// Removes all tabs matching the urls, used when other clients request to close tabs on this device. + func removeTabs(by urls: [URL]) async + /// Get inactive tabs from the list of tabs based on the time condition to be considered inactive. /// Replaces LegacyInactiveTabModel and related classes /// @@ -128,13 +130,11 @@ extension TabManager { @discardableResult func addTab(_ request: URLRequest! = nil, - configuration: WKWebViewConfiguration! = nil, afterTab: Tab? = nil, zombie: Bool = false, isPrivate: Bool = false ) -> Tab { addTab(request, - configuration: configuration, afterTab: afterTab, zombie: zombie, isPrivate: isPrivate) diff --git a/firefox-ios/Client/TabManagement/TabManagerImplementation.swift b/firefox-ios/Client/TabManagement/TabManagerImplementation.swift index 7340243d0b3d..ae08c874e1f7 100644 --- a/firefox-ios/Client/TabManagement/TabManagerImplementation.swift +++ b/firefox-ios/Client/TabManagement/TabManagerImplementation.swift @@ -7,6 +7,7 @@ import TabDataStore import Storage import Common import Shared +import WebKit // This class subclasses the legacy tab manager temporarily so we can // gradually migrate to the new system @@ -308,9 +309,7 @@ class TabManagerImplementation: LegacyTabManager, Notifiable, WindowSimpleTabsPr let tabID = UUID(uuidString: tab.tabUUID) else { return } - Task { - await self.tabSessionStore.saveTabSession(tabID: tabID, sessionData: tabSession) - } + self.tabSessionStore.saveTabSession(tabID: tabID, sessionData: tabSession) } private func saveAllTabData() { @@ -360,15 +359,10 @@ class TabManagerImplementation: LegacyTabManager, Notifiable, WindowSimpleTabsPr preserveTabs() - Task(priority: .high) { - var sessionData: Data? - if !tab.isFxHomeTab { - sessionData = await tabSessionStore.fetchTabSession(tabID: tabUUID) - } - await selectTabWithSession(tab: tab, - previous: previous, - sessionData: sessionData) - } + let sessionData = tabSessionStore.fetchTabSession(tabID: tabUUID) + selectTabWithSession(tab: tab, + previous: previous, + sessionData: sessionData) // Default to false if the feature flag is not enabled var isPrivate = false @@ -389,7 +383,7 @@ class TabManagerImplementation: LegacyTabManager, Notifiable, WindowSimpleTabsPr $0.get()?.tabManager( self, didSelectedTabChange: tab, - previous: previous, + previousTab: previous, isRestoring: !tabRestoreHasFinished ) } @@ -414,10 +408,11 @@ class TabManagerImplementation: LegacyTabManager, Notifiable, WindowSimpleTabsPr if selectedTab?.isPrivate ?? false { _selectedIndex = -1 } - privateTabs.forEach { $0.close() } + privateTabs.forEach { tab in + tab.close() + delegates.forEach { $0.get()?.tabManager(self, didRemoveTab: tab, isRestoring: false) } + } tabs = normalTabs - - privateConfiguration = LegacyTabManager.makeWebViewConfig(isPrivate: true, prefs: profile.prefs) } private func willSelectTab(_ url: URL?) { @@ -433,9 +428,11 @@ class TabManagerImplementation: LegacyTabManager, Notifiable, WindowSimpleTabsPr store.dispatch(action) } - @MainActor private func selectTabWithSession(tab: Tab, previous: Tab?, sessionData: Data?) { - selectedTab?.createWebview(with: sessionData) + assert(Thread.isMainThread, "Currently expected to be called only on main thread.") + let configuration: WKWebViewConfiguration = tab.isPrivate ? self.privateConfiguration : self.configuration + + selectedTab?.createWebview(with: sessionData, configuration: configuration) selectedTab?.lastExecutedTime = Date.now() } diff --git a/firefox-ios/Client/Telemetry/AppStartupTelemetry.swift b/firefox-ios/Client/Telemetry/AppStartupTelemetry.swift index 176f6ce4d744..5dd25a41c9b2 100644 --- a/firefox-ios/Client/Telemetry/AppStartupTelemetry.swift +++ b/firefox-ios/Client/Telemetry/AppStartupTelemetry.swift @@ -41,7 +41,8 @@ final class AppStartupTelemetry { let loginsViewModel = PasswordManagerViewModel( profile: profile, searchController: searchController, - theme: LightTheme() + theme: LightTheme(), + loginProvider: profile.logins ) let dataSource = LoginDataSource(viewModel: loginsViewModel) loginsViewModel.loadLogins(loginDataSource: dataSource) diff --git a/firefox-ios/Client/Telemetry/TelemetryWrapper.swift b/firefox-ios/Client/Telemetry/TelemetryWrapper.swift index f434c94b83d0..6ccc87e3753b 100644 --- a/firefox-ios/Client/Telemetry/TelemetryWrapper.swift +++ b/firefox-ios/Client/Telemetry/TelemetryWrapper.swift @@ -312,8 +312,6 @@ extension TelemetryWrapper { case switchControl = "switch-control" case dynamicTextSize = "dynamic-text-size" case error = "error" - case engagement = "engagement" - case abandonment = "abandonment" } public enum EventObject: String { @@ -1927,10 +1925,6 @@ extension TelemetryWrapper { GleanMetrics.Awesomebar.shareButtonTapped.record() case (.action, .drag, .locationBar, _, _): GleanMetrics.Awesomebar.dragLocationBar.record() - case (.action, .engagement, .locationBar, _, _): - GleanMetrics.Awesomebar.engagement.record() - case (.action, .abandonment, .locationBar, _, _): - GleanMetrics.Awesomebar.abandonment.record() // MARK: - GleanPlumb Messaging case (.information, .view, .messaging, .messageImpression, let extras): guard let messageSurface = extras?[EventExtraKey.messageSurface.rawValue] as? String, diff --git a/firefox-ios/Client/Telemetry/Wrapper/AdjustWrapper.swift b/firefox-ios/Client/Telemetry/Wrapper/AdjustWrapper.swift index 5cf84519eba0..d0cd5ee88856 100644 --- a/firefox-ios/Client/Telemetry/Wrapper/AdjustWrapper.swift +++ b/firefox-ios/Client/Telemetry/Wrapper/AdjustWrapper.swift @@ -1,37 +1,39 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/ - -import Foundation -import Glean - -protocol AdjustWrapper { - func recordDeeplink(url: URL) - func record(campaign: String) - func record(adgroup: String) - func record(creative: String) - func record(network: String) -} - -struct DefaultAdjustWrapper: AdjustWrapper { - func recordDeeplink(url: URL) { - let extra = GleanMetrics.Adjust.DeeplinkReceivedExtra(receivedUrl: url.absoluteString) - GleanMetrics.Adjust.deeplinkReceived.record(extra) - } - - func record(campaign: String) { - GleanMetrics.Adjust.campaign.set(campaign) - } - - func record(adgroup: String) { - GleanMetrics.Adjust.adGroup.set(adgroup) - } - - func record(creative: String) { - GleanMetrics.Adjust.creative.set(creative) - } - - func record(network: String) { - GleanMetrics.Adjust.network.set(network) - } -} +// swiftlint:disable comment_spacing file_header +//// This Source Code Form is subject to the terms of the Mozilla Public +//// License, v. 2.0. If a copy of the MPL was not distributed with this +//// file, You can obtain one at http://mozilla.org/MPL/2.0/ +// +//import Foundation +//import Glean +// +//protocol AdjustWrapper { +// func recordDeeplink(url: URL) +// func record(campaign: String) +// func record(adgroup: String) +// func record(creative: String) +// func record(network: String) +//} +// +//struct DefaultAdjustWrapper: AdjustWrapper { +// func recordDeeplink(url: URL) { +// let extra = GleanMetrics.Adjust.DeeplinkReceivedExtra(receivedUrl: url.absoluteString) +// GleanMetrics.Adjust.deeplinkReceived.record(extra) +// } +// +// func record(campaign: String) { +// GleanMetrics.Adjust.campaign.set(campaign) +// } +// +// func record(adgroup: String) { +// GleanMetrics.Adjust.adGroup.set(adgroup) +// } +// +// func record(creative: String) { +// GleanMetrics.Adjust.creative.set(creative) +// } +// +// func record(network: String) { +// GleanMetrics.Adjust.network.set(network) +// } +//} +// swiftlint:enable comment_spacing file_header diff --git a/firefox-ios/Client/hr.lproj/InfoPlist.strings b/firefox-ios/Client/hr.lproj/InfoPlist.strings index 38330417b1dd..162b57994d64 100644 --- a/firefox-ios/Client/hr.lproj/InfoPlist.strings +++ b/firefox-ios/Client/hr.lproj/InfoPlist.strings @@ -7,6 +7,9 @@ /* Privacy - Camera Usage Description */ "NSCameraUsageDescription" = "Ovo vam omogućava slikanje i prijenos fotografija."; +/* Privacy - Face ID Usage Description */ +"NSFaceIDUsageDescription" = "Firefox zahtijeva Face ID za pristup tvojim spremljenim lozinkama i načinima plaćanja."; + /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Web stranice koje posjećuješ mogu zatražiti tvoju lokaciju."; diff --git a/firefox-ios/Client/id.lproj/InfoPlist.strings b/firefox-ios/Client/id.lproj/InfoPlist.strings index c9a9afb24365..082cb822d881 100644 --- a/firefox-ios/Client/id.lproj/InfoPlist.strings +++ b/firefox-ios/Client/id.lproj/InfoPlist.strings @@ -7,6 +7,9 @@ /* Privacy - Camera Usage Description */ "NSCameraUsageDescription" = "Firefox menggunakan kamera Anda untuk memindai kode QR dan mengambil foto dan video."; +/* Privacy - Face ID Usage Description */ +"NSFaceIDUsageDescription" = "Firefox memerlukan Face ID untuk mengakses kata sandi dan metode pembayaran Anda yang tersimpan."; + /* Privacy - Location When In Use Usage Description */ "NSLocationWhenInUseUsageDescription" = "Situs web yang Anda kunjungi dapat meminta lokasi Anda."; diff --git a/firefox-ios/Client/metrics.yaml b/firefox-ios/Client/metrics.yaml index 97ca132540ca..da204988ea0a 100755 --- a/firefox-ios/Client/metrics.yaml +++ b/firefox-ios/Client/metrics.yaml @@ -4620,38 +4620,6 @@ awesomebar: notification_emails: - fx-ios-data-stewards@mozilla.com expires: "2025-01-01" - engagement: - type: event - description: | - Recorded when the user completes their search session by - tapping a search result, or entering a URL or a search term. - bugs: - - https://mozilla-hub.atlassian.net/browse/FXIOS-8326 - - https://mozilla-hub.atlassian.net/browse/FXIOS-8550 - data_reviews: - - https://github.com/mozilla-mobile/firefox-ios/pull/18452 - - https://github.com/mozilla-mobile/firefox-ios/pull/19009 - notification_emails: - - fx-ios-data-stewards@mozilla.com - data_sensitivity: - - interaction - expires: "2025-01-01" - abandonment: - type: event - description: | - Recorded when the user dismisses the awesomebar without completing their - search. - bugs: - - https://mozilla-hub.atlassian.net/browse/FXIOS-8326 - - https://mozilla-hub.atlassian.net/browse/FXIOS-8550 - data_reviews: - - https://github.com/mozilla-mobile/firefox-ios/pull/18452 - - https://github.com/mozilla-mobile/firefox-ios/pull/19009 - notification_emails: - - fx-ios-data-stewards@mozilla.com - data_sensitivity: - - interaction - expires: "2025-01-01" # Share sheet specific metrics diff --git a/firefox-ios/CredentialProvider/CredentialListViewController.swift b/firefox-ios/CredentialProvider/CredentialListViewController.swift index c184391f2cd9..b03c6ddaf558 100644 --- a/firefox-ios/CredentialProvider/CredentialListViewController.swift +++ b/firefox-ios/CredentialProvider/CredentialListViewController.swift @@ -4,6 +4,7 @@ import UIKit import AuthenticationServices +import Common protocol CredentialListViewProtocol: AnyObject { var credentialExtensionContext: ASCredentialProviderExtensionContext? { get } @@ -109,8 +110,11 @@ class CredentialListViewController: UIViewController, CredentialListViewProtocol self.navigationItem.hidesSearchBarWhenScrolling = false self.definesPresentationContext = true - let searchIcon = UIImage(named: "searchLarge")?.withRenderingMode(.alwaysTemplate).tinted(UIColor.systemBlue) - let clearIcon = UIImage(named: "crossCircleFillLarge")?.withRenderingMode(.alwaysTemplate) + let searchIcon = UIImage(named: StandardImageIdentifiers.Large.search)? + .withRenderingMode(.alwaysTemplate) + .tinted(UIColor.systemBlue) + let clearIcon = UIImage(named: StandardImageIdentifiers.Large.crossCircleFill)? + .withRenderingMode(.alwaysTemplate) .tinted(UIColor.systemBlue) searchController.searchBar.setImage(searchIcon, for: UISearchBar.Icon.search, state: .normal) searchController.searchBar.setImage(clearIcon, for: UISearchBar.Icon.clear, state: .normal) diff --git a/firefox-ios/CredentialProvider/Info.plist b/firefox-ios/CredentialProvider/Info.plist index 60d0c97ffdb8..f8cebac915f7 100644 --- a/firefox-ios/CredentialProvider/Info.plist +++ b/firefox-ios/CredentialProvider/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 130.0 + 131.0 CFBundleVersion 1 MozDevelopmentTeam diff --git a/firefox-ios/Extensions/NotificationService/Info.plist b/firefox-ios/Extensions/NotificationService/Info.plist index 52f010dac4c2..4df6947735fb 100644 --- a/firefox-ios/Extensions/NotificationService/Info.plist +++ b/firefox-ios/Extensions/NotificationService/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 130.0 + 131.0 CFBundleVersion 1 MozDevelopmentTeam diff --git a/firefox-ios/Extensions/ShareTo/Info.plist b/firefox-ios/Extensions/ShareTo/Info.plist index 273f049d3f92..8581bf2caaa7 100644 --- a/firefox-ios/Extensions/ShareTo/Info.plist +++ b/firefox-ios/Extensions/ShareTo/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 130.0 + 131.0 CFBundleSignature ???? CFBundleVersion diff --git a/firefox-ios/Extensions/ShareTo/ShareViewController.swift b/firefox-ios/Extensions/ShareTo/ShareViewController.swift index 5d9d4636060d..d6a50d18525e 100644 --- a/firefox-ios/Extensions/ShareTo/ShareViewController.swift +++ b/firefox-ios/Extensions/ShareTo/ShareViewController.swift @@ -147,28 +147,28 @@ class ShareViewController: UIViewController { makeActionRow( addTo: stackView, label: .ShareOpenInFirefox, - imageName: "logoFirefoxLarge", + imageName: StandardImageIdentifiers.Large.logoFirefox, action: #selector(actionOpenInFirefoxNow), hasNavigation: false ) makeActionRow( addTo: stackView, label: .ShareLoadInBackground, - imageName: "tabTrayLarge", + imageName: StandardImageIdentifiers.Large.tabTray, action: #selector(actionLoadInBackground), hasNavigation: false ) makeActionRow( addTo: stackView, label: .ShareBookmarkThisPage, - imageName: "bookmarkLarge", + imageName: StandardImageIdentifiers.Large.bookmark, action: #selector(actionBookmarkThisPage), hasNavigation: false ) makeActionRow( addTo: stackView, label: .ShareAddToReadingList, - imageName: "readingListAddLarge", + imageName: StandardImageIdentifiers.Large.readingListAdd, action: #selector(actionAddToReadingList), hasNavigation: false ) @@ -176,7 +176,7 @@ class ShareViewController: UIViewController { makeActionRow( addTo: stackView, label: .ShareSendToDevice, - imageName: "deviceDesktopSendLarge", + imageName: StandardImageIdentifiers.Large.deviceDesktopSend, action: #selector(actionSendToDevice), hasNavigation: true ) @@ -185,7 +185,7 @@ class ShareViewController: UIViewController { makeActionRow( addTo: stackView, label: .ShareSearchInFirefox, - imageName: "searchLarge", + imageName: StandardImageIdentifiers.Large.search, action: #selector(actionSearchInFirefox), hasNavigation: false ) @@ -317,7 +317,7 @@ class ShareViewController: UIViewController { if hasNavigation { let navButton = UIImageView( - image: UIImage(named: "chevronRightLarge")?.withRenderingMode(.alwaysTemplate) + image: UIImage(named: StandardImageIdentifiers.Large.chevronRight)?.withRenderingMode(.alwaysTemplate) ) navButton.contentMode = .scaleAspectFit navButton.tintColor = theme.colors.textPrimary diff --git a/firefox-ios/Providers/Profile.swift b/firefox-ios/Providers/Profile.swift index e23f1d2d876b..d52dac7abd4d 100644 --- a/firefox-ios/Providers/Profile.swift +++ b/firefox-ios/Providers/Profile.swift @@ -51,8 +51,9 @@ public protocol SyncManager { /// This exists to pass in external context: e.g., the UIApplication can /// expose notification functionality in this way. -public protocol SendTabDelegate: AnyObject { +public protocol FxACommandsDelegate: AnyObject { func openSendTabs(for urls: [URL]) + func closeTabs(for urls: [URL]) } class ProfileFileAccessor: FileAccessor { @@ -234,7 +235,7 @@ open class BrowserProfile: Profile { let readingListDB: BrowserDB var syncManager: SyncManager! - var sendTabDelegate: SendTabDelegate? + var fxaCommandsDelegate: FxACommandsDelegate? /** * N.B., BrowserProfile is used from our extensions, often via a pattern like @@ -249,7 +250,7 @@ open class BrowserProfile: Profile { * However, if we provide it here, it's assumed that we're initializing it from the application. */ init(localName: String, - sendTabDelegate: SendTabDelegate? = nil, + fxaCommandsDelegate: FxACommandsDelegate? = nil, creditCardAutofillEnabled: Bool = false, clear: Bool = false, logger: Logger = DefaultLogger.shared) { @@ -260,7 +261,7 @@ open class BrowserProfile: Profile { self.files = ProfileFileAccessor(localName: localName) self.keychain = MZKeychainWrapper.sharedClientAppContainerKeychain self.logger = logger - self.sendTabDelegate = sendTabDelegate + self.fxaCommandsDelegate = fxaCommandsDelegate if clear { do { @@ -603,16 +604,26 @@ open class BrowserProfile: Profile { if let accountManager = self.rustFxA.accountManager { accountManager.deviceConstellation()?.pollForCommands { commands in guard let commands = try? commands.get() else { return } - let urls = commands.compactMap { command in + + var receivedTabURLs: [URL] = [] + var closedTabURLs: [URL] = [] + for command in commands { switch command { case .tabReceived(_, let tabData): - let url = tabData.entries.last?.url ?? "" - return URL(string: url, invalidCharacters: false) - default: - return nil + if let urlString = tabData.entries.last?.url, let url = URL(string: urlString) { + receivedTabURLs.append(url) + } + case .tabsClosed(sender: _, let closeTabPayload): + closedTabURLs.append(contentsOf: closeTabPayload.urls.compactMap { URL(string: $0) }) } } - self.sendTabDelegate?.openSendTabs(for: urls) + if !receivedTabURLs.isEmpty { + self.fxaCommandsDelegate?.openSendTabs(for: receivedTabURLs) + } + + if !closedTabURLs.isEmpty { + self.fxaCommandsDelegate?.closeTabs(for: closedTabURLs) + } } } } diff --git a/firefox-ios/RustFxA/FxAWebViewModel.swift b/firefox-ios/RustFxA/FxAWebViewModel.swift index 1b3d3bec8a88..d250944859d0 100644 --- a/firefox-ios/RustFxA/FxAWebViewModel.swift +++ b/firefox-ios/RustFxA/FxAWebViewModel.swift @@ -309,7 +309,7 @@ extension FxAWebViewModel { /// signing up for an account). This latter case is also used for the sign-in state. private func onSessionStatus(id: Int, webView: WKWebView) { let autofillCreditCardStatus = featureFlags.isFeatureEnabled(.creditCardAutofillStatus, checking: .buildOnly) - let addressAutofillStatus = featureFlags.isFeatureEnabled(.addressAutofill, checking: .buildOnly) + let addressAutofillStatus = AddressLocaleFeatureValidator.isValidRegion() let creditCardCapability = autofillCreditCardStatus ? ", \"creditcards\"" : "" let addressAutofillCapability = addressAutofillStatus ? ", \"addresses\"" : "" diff --git a/firefox-ios/RustFxA/RustFirefoxAccounts.swift b/firefox-ios/RustFxA/RustFirefoxAccounts.swift index 0dd5f77cb624..15e81426fe70 100644 --- a/firefox-ios/RustFxA/RustFirefoxAccounts.swift +++ b/firefox-ios/RustFxA/RustFirefoxAccounts.swift @@ -8,6 +8,7 @@ import Shared import class MozillaAppServices.FxAccountManager import class MozillaAppServices.FxAConfig +import enum MozillaAppServices.DeviceCapability import enum MozillaAppServices.DeviceType import enum MozillaAppServices.OAuthScope import struct MozillaAppServices.DeviceConfig @@ -23,6 +24,18 @@ final class Unknown: NSObject, NSCoding { } } +// A convenience to allow other callers to pass in Nimbus/Flaggable features +// to RustFirefoxAccounts +public struct RustFxAFeatures: OptionSet { + public let rawValue: Int + + public static let closeRemoteTabs = RustFxAFeatures(rawValue: 1 << 0) + + public init(rawValue: Int) { + self.rawValue = rawValue + } +} + // TODO: renamed FirefoxAccounts.swift once the old code is removed fully. /** A singleton that wraps the Rust FxA library. @@ -71,6 +84,7 @@ open class RustFirefoxAccounts { */ public static func startup( prefs: Prefs, + features: RustFxAFeatures = RustFxAFeatures(), logger: Logger = DefaultLogger.shared, completion: @escaping (FxAccountManager) -> Void ) { @@ -84,7 +98,7 @@ open class RustFirefoxAccounts { if let accManager = RustFirefoxAccounts.shared.accountManager { completion(accManager) } - let manager = RustFirefoxAccounts.shared.createAccountManager() + let manager = RustFirefoxAccounts.shared.createAccountManager(features: features) manager.initialize { result in assert(Thread.isMainThread) if !Thread.isMainThread { @@ -117,7 +131,7 @@ open class RustFirefoxAccounts { return RustFirefoxAccounts.prefs?.boolForKey(PrefsKeys.KeyEnableChinaSyncService) ?? AppInfo.isChinaEdition } - private func createAccountManager() -> FxAccountManager { + private func createAccountManager(features: RustFxAFeatures) -> FxAccountManager { let prefs = RustFirefoxAccounts.prefs if prefs == nil { logger.log("prefs is unexpectedly nil", level: .warning, category: .sync) @@ -162,10 +176,15 @@ open class RustFirefoxAccounts { } let type = UIDevice.current.userInterfaceIdiom == .pad ? DeviceType.tablet : DeviceType.mobile + + var capabilities: [DeviceCapability] = [.sendTab] + if features.contains(.closeRemoteTabs) { + capabilities.append(.closeTabs) + } let deviceConfig = DeviceConfig( name: DeviceInfo.defaultClientName(), deviceType: type, - capabilities: [.sendTab] + capabilities: capabilities ) let accessGroupPrefix = Bundle.main.object(forInfoDictionaryKey: "MozDevelopmentTeam") as! String let accessGroupIdentifier = AppInfo.keychainAccessGroupWithPrefix(accessGroupPrefix) diff --git a/firefox-ios/Shared/Extensions/String+Extension.swift b/firefox-ios/Shared/Extensions/String+Extension.swift index 96b6b6beee3b..934c06532b5e 100644 --- a/firefox-ios/Shared/Extensions/String+Extension.swift +++ b/firefox-ios/Shared/Extensions/String+Extension.swift @@ -52,16 +52,6 @@ public extension String { return self.removingPercentEncoding } - /// Returns a new string made by removing the leading String characters contained - /// in a given character set. - func stringByTrimmingLeadingCharactersInSet(_ set: CharacterSet) -> String { - var trimmed = self - while trimmed.rangeOfCharacter(from: set)?.lowerBound == trimmed.startIndex { - trimmed.remove(at: trimmed.startIndex) - } - return trimmed - } - /// Adds a newline at the closest space from the middle of a string. /// Example turning "Mark as Read" into "Mark as\n Read" func stringSplitWithNewline() -> String { diff --git a/firefox-ios/Shared/KeyboardHelper.swift b/firefox-ios/Shared/KeyboardHelper.swift index 6084a6164b42..1fb7c30ebc0b 100644 --- a/firefox-ios/Shared/KeyboardHelper.swift +++ b/firefox-ios/Shared/KeyboardHelper.swift @@ -46,12 +46,14 @@ public protocol KeyboardHelperDelegate: AnyObject { func keyboardHelper(_ keyboardHelper: KeyboardHelper, keyboardWillHideWithState state: KeyboardState) func keyboardHelper(_ keyboardHelper: KeyboardHelper, keyboardWillChangeWithState state: KeyboardState) func keyboardHelper(_ keyboardHelper: KeyboardHelper, keyboardDidChangeWithState state: KeyboardState) + func keyboardHelper(_ keyboardHelper: KeyboardHelper, keyboardDidHideWithState state: KeyboardState) } public extension KeyboardHelperDelegate { func keyboardHelper(_ keyboardHelper: KeyboardHelper, keyboardDidShowWithState state: KeyboardState) {} func keyboardHelper(_ keyboardHelper: KeyboardHelper, keyboardWillChangeWithState state: KeyboardState) {} func keyboardHelper(_ keyboardHelper: KeyboardHelper, keyboardDidChangeWithState state: KeyboardState) {} + func keyboardHelper(_ keyboardHelper: KeyboardHelper, keyboardDidHideWithState state: KeyboardState) {} } /** @@ -91,6 +93,12 @@ open class KeyboardHelper: NSObject { name: UIResponder.keyboardWillHideNotification, object: nil ) + NotificationCenter.default.addObserver( + self, + selector: #selector(keyboardDidHide), + name: UIResponder.keyboardDidHideNotification, + object: nil + ) NotificationCenter.default.addObserver( self, selector: #selector(keyboardDidChange), @@ -153,6 +161,16 @@ open class KeyboardHelper: NSObject { } } + @objc + private func keyboardDidHide(_ notification: Notification) { + if let userInfo = notification.userInfo { + for weakDelegate in delegates { + weakDelegate.delegate?.keyboardHelper(self, + keyboardDidHideWithState: KeyboardState(userInfo)) + } + } + } + @objc private func keyboardWillChange(_ notification: Notification) { if let userInfo = notification.userInfo { diff --git a/firefox-ios/Shared/Supporting Files/br.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/br.lproj/EditAddress.strings index fb3053dfe7cb..22792a67b605 100644 --- a/firefox-ios/Shared/Supporting Files/br.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/br.lproj/EditAddress.strings @@ -112,3 +112,27 @@ /* Button label for closing the view where user can view their address info. */ "Addresses.EditAddress.CloseNavBarButtonLabel.v129" = "Serriñ"; +/* Button label for editing the address details shown in the form. */ +"Addresses.EditAddress.EditNavBarButtonLabel.v129" = "Aozañ"; + +/* Title for button that offers the user the option to remove an address. */ +"Addresses.EditAddress.RemoveAddressButtonTitle.v129" = "Dilemel ar chomlec’h"; + +/* Toast message confirming that an address has been successfully removed. */ +"Addresses.Toast.AddressRemovedConfirmation.v129" = "Chomlec’h dilamet"; + +/* Toast message confirming that an address has been successfully saved. */ +"Addresses.Toast.AddressSavedConfirmation.v129" = "Chomlec’h enrollet"; + +/* Toast message indicating an error occurred while trying to save an address. */ +"Addresses.Toast.AddressSaveError.v129" = "N’haller ket enrollañ ar chomlec’h"; + +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "N’haller ket dilemel ar chomlec’h"; + +/* Suggestion to try again after an error occurred while trying to save an address. */ +"Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Klask en-dro"; + +/* Toast message confirming that an address has been successfully updated. */ +"Addresses.Toast.AddressUpdatedConfirmation.v129" = "Titouroù ar chomlec’h hizivaet"; + diff --git a/firefox-ios/Shared/Supporting Files/br.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/br.lproj/EnhancedTrackingProtection.strings index baadd7c377bb..2756b91f74d0 100644 --- a/firefox-ios/Shared/Supporting Files/br.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/br.lproj/EnhancedTrackingProtection.strings @@ -13,3 +13,6 @@ /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ "Menu.EnhancedTrackingProtection.Details.Verifier.v128" = "Gwiriet gant %@"; +/* Title for the switch to enable/disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.Switch.Title.v128" = "Gwarez araokaet a-enep d’an heulierien"; + diff --git a/firefox-ios/Shared/Supporting Files/br.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/br.lproj/Microsurvey.strings index 2006e81e9dc8..3cd23de664b8 100644 --- a/firefox-ios/Shared/Supporting Files/br.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/br.lproj/Microsurvey.strings @@ -22,9 +22,15 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the logo image that appears on the bottom sheet that informs the user that it is coming from the app specifically. Placeholder is for the app name. */ "Microsurvey.Survey.LogoImage.AccessibilityLabel.v129" = "Logo %@"; +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.LikertScaleOption2.v127" = "Plijet"; + /* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ "Microsurvey.Survey.Options.LikertScaleOption3.v127" = "Ali ebet"; +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.LikertScaleOption4.v127" = "Displijet"; + /* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. It indicates that the user has not use the feature that the survey is inquiring about. */ "Microsurvey.Survey.Options.LikertScaleOption6.v129" = "Ne ran ket gantañ"; diff --git a/firefox-ios/Shared/Supporting Files/br.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/br.lproj/Toolbar.strings new file mode 100644 index 000000000000..1edb08859831 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/br.lproj/Toolbar.strings @@ -0,0 +1,3 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Ivinell nevez"; + diff --git a/firefox-ios/Shared/Supporting Files/bs.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/bs.lproj/EditAddress.strings index e47078cb4a7c..00ae4763887f 100644 --- a/firefox-ios/Shared/Supporting Files/bs.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/bs.lproj/EditAddress.strings @@ -88,3 +88,54 @@ /* Label for the telephone number field, allowing users to input their contact number. This is essential for communication and service provision, ensuring contact details are autofilled correctly. */ "Addresses.EditAddress.AutofillAddressTel.v129" = "Telefon"; +/* Label for the input field for the townland, a specific type of land division used in rural areas. Enhances address detail for users in regions where townlands are a common addressing component. */ +"Addresses.EditAddress.AutofillAddressTownland.v129" = "Townland"; + +/* Label for the field to input the name of a village or township. This is crucial for addresses in rural areas, ensuring the autofill feature accurately captures all necessary geographical details. */ +"Addresses.EditAddress.AutofillAddressVillageTownship.v129" = "Selo ili grad"; + +/* Label for the ZIP code field, primarily used in the United States for mail sorting. Key for autofill to accurately complete addresses for shipping, billing, and service provision. */ +"Addresses.EditAddress.AutofillAddressZip.v129" = "Poštanski broj"; + +/* Label for the button to cancel the current autofill operation or exit the form without saving changes. Provides users with an option to back out of a process without making any modifications. */ +"Addresses.EditAddress.AutofillCancelButton.v129" = "Otkaži"; + +/* Title for the option allowing users to edit an existing saved address. This is used within the settings for autofill, enabling users to update their address details for accurate form autofilling. */ +"Addresses.EditAddress.AutofillEditAddressTitle.v129" = "Uredi adresu"; + +/* Title for the input field where users can enter their street address. This is used within the settings for autofill, allowing users to provide their street address for accurate form autofilling. */ +"Addresses.EditAddress.AutofillEditStreetAddressTitle.v129" = "Adresa"; + +/* Label for the button to save the current address details entered or edited by the user. This action confirms the user's changes and updates their autofill settings accordingly. */ +"Addresses.EditAddress.AutofillSaveButton.v129" = "Sačuvaj"; + +/* Title for the option allowing users to view an existing saved address. This is used within the settings for autofill, enabling users to see their address details for accurate form autofilling. */ +"Addresses.EditAddress.AutofillViewAddressTitle.v129" = "Prikaži adresu"; + +/* Button label for closing the view where user can view their address info. */ +"Addresses.EditAddress.CloseNavBarButtonLabel.v129" = "Zatvori"; + +/* Button label for editing the address details shown in the form. */ +"Addresses.EditAddress.EditNavBarButtonLabel.v129" = "Uredi"; + +/* Title for button that offers the user the option to remove an address. */ +"Addresses.EditAddress.RemoveAddressButtonTitle.v129" = "Ukloni adresu"; + +/* Toast message confirming that an address has been successfully removed. */ +"Addresses.Toast.AddressRemovedConfirmation.v129" = "Adresa je uklonjena"; + +/* Toast message confirming that an address has been successfully saved. */ +"Addresses.Toast.AddressSavedConfirmation.v129" = "Adresa je sačuvana"; + +/* Toast message indicating an error occurred while trying to save an address. */ +"Addresses.Toast.AddressSaveError.v129" = "Adresa se ne može sačuvati"; + +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Adresa se ne može ukloniti"; + +/* Suggestion to try again after an error occurred while trying to save an address. */ +"Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Pokušaj ponovo"; + +/* Toast message confirming that an address has been successfully updated. */ +"Addresses.Toast.AddressUpdatedConfirmation.v129" = "Informacije o adresi su ažurirane"; + diff --git a/firefox-ios/Shared/Supporting Files/bs.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/bs.lproj/EnhancedTrackingProtection.strings index 16beec2ba692..75d66b5d8089 100644 --- a/firefox-ios/Shared/Supporting Files/bs.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/bs.lproj/EnhancedTrackingProtection.strings @@ -22,8 +22,19 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Veza nije sigurna"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Nisu pronađeni tragači"; + +/* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Kolačići za praćenje između web stranica: %@"; + +/* Text to let users know how many fingerprinters were blocked on the current website. The placeholder will show the number of fingerprinters detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.Fingerprinter.v129" = "Sakupljači digitalnih otisaka: %@"; + +/* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Programi za praćenje na društvenim mrežama: %@"; + +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Tragači blokirani: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -35,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Isključili ste zaštitu"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Zaštićeni ste. Ako nešto uočimo, obavijestit ćemo vas."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "Vaša veza nije sigurna."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Budite oprezni na ovoj stranici"; @@ -54,6 +67,9 @@ /* A switch to disable enhanced tracking protection inside the menu. */ "Menu.EnhancedTrackingProtection.SwitchOff.Text.v128" = "Zaštite su ISKLJUČENE. Predlažemo da ga ponovo uključite."; +/* A switch to disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.SwitchOff.Text.v129" = "Zaštite su ISKLJUČENE. Predlažemo da ih ponovo uključite."; + /* A switch to disable enhanced tracking protection inside the menu. */ "Menu.EnhancedTrackingProtection.SwitchOn.Text.v128" = "Ako nešto izgleda pokvareno na ovoj stranici, pokušajte to isključiti."; diff --git a/firefox-ios/Shared/Supporting Files/bs.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/bs.lproj/Microsurvey.strings index e4ea46fb374a..5c0c5823f232 100644 --- a/firefox-ios/Shared/Supporting Files/bs.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/bs.lproj/Microsurvey.strings @@ -31,6 +31,9 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the title for the header on the screen. */ "Microsurvey.Survey.HeaderLabel.v127" = "Popunite ovu anketu"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the title for the header on the screen. */ +"Microsurvey.Survey.HeaderLabel.v129" = "Molimo popunite anketu"; + /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the logo image that appears on the bottom sheet that informs the user that it is coming from the app specifically. Placeholder is for the app name. */ "Microsurvey.Survey.LogoImage.AccessibilityLabel.v129" = "%@ logo"; @@ -49,6 +52,9 @@ /* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ "Microsurvey.Survey.Options.LikertScaleOption5.v127" = "Vrlo nezadovoljan"; +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. It indicates that the user has not use the feature that the survey is inquiring about. */ +"Microsurvey.Survey.Options.LikertScaleOption6.v129" = "Ne koristim ga"; + /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states the order the survey option in the list of options. First placeholder is the number the option is in the list and the second placeholder is the total number of options such as 1 out of 6. */ "Microsurvey.Survey.OptionsOrder.AccessibilityLabel.v129" = "%1$@ od %2$@"; @@ -61,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Neodabrano"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Anketa"; + diff --git a/firefox-ios/Shared/Supporting Files/bs.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/bs.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..57cd5df52ede --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/bs.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Učitaj ponovo"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "Došlo je do SSL greške i nije moguće uspostaviti sigurnosnu vezu sa serverom."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "Budite oprezni. Nešto ne izgleda kako treba."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "Pokušajte se povezati na drugom uređaju. Provjerite svoj modem ili ruter. Prekinite vezu i ponovo se povežite na Wi-Fi."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "Izgleda da postoji problem s vašom internet vezom."; + diff --git a/firefox-ios/Shared/Supporting Files/bs.lproj/PasswordAutofill.strings b/firefox-ios/Shared/Supporting Files/bs.lproj/PasswordAutofill.strings index 42d920b62f0d..d1d6ae06da7a 100644 --- a/firefox-ios/Shared/Supporting Files/bs.lproj/PasswordAutofill.strings +++ b/firefox-ios/Shared/Supporting Files/bs.lproj/PasswordAutofill.strings @@ -1,3 +1,6 @@ +/* This label is used in a cell found in the list of autofill login options in place of an actual username to denote that no username was saved for this login */ +"PasswordAutofill.LoginListCellNoUsername.v129" = "(nema korisničkog imena)"; + /* This label is used for a button in the password list screen allowing users to manage their saved passwords. It's meant to direct users to where they can add, remove, or edit their saved passwords. */ "PasswordAutofill.ManagePasswordsButton.v124" = "Upravljajte lozinkama"; diff --git a/firefox-ios/Shared/Supporting Files/bs.lproj/ScanQRCode.strings b/firefox-ios/Shared/Supporting Files/bs.lproj/ScanQRCode.strings new file mode 100644 index 000000000000..7d996b43089b --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/bs.lproj/ScanQRCode.strings @@ -0,0 +1,9 @@ +/* Allow button to open URL from scanned QR Code */ +"ScanQRCode.ConfirmOpenURL.AllowButton.v129" = "Dozvoli"; + +/* Deny button to cancel opening URL from scanned QR Code */ +"ScanQRCode.ConfirmOpenURL.DenyButton.v129" = "Odbij"; + +/* Text of the prompt to ask user permission to open a URL from a scanned QR code. Placeholder is app name. */ +"ScanQRCode.ConfirmOpenURL.Message.v129" = "Dozvoliti %@ da se otvori?"; + diff --git a/firefox-ios/Shared/Supporting Files/bs.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/bs.lproj/Settings.strings index ce72cd192d34..e3de4d33abd7 100644 --- a/firefox-ios/Shared/Supporting Files/bs.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/bs.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Upravljaj adresama"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "Adresa za %@"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "SAČUVANE ADRESE"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Sačuvaj adrese na %@"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "Sigurno sačuvajte svoje podatke da biste im kasnije mogli brzo pristupiti."; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "Uključuje brojeve telefona i email adrese"; diff --git a/firefox-ios/Shared/Supporting Files/bs.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/bs.lproj/Toolbar.strings new file mode 100644 index 000000000000..c51d262fa1b2 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/bs.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Novi tab"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Zatvori ovaj tab"; + diff --git a/firefox-ios/Shared/Supporting Files/ca.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/ca.lproj/EditAddress.strings index 3843d1a22ae0..ed1b0621f088 100644 --- a/firefox-ios/Shared/Supporting Files/ca.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/ca.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "No s’ha pogut desar l’adreça"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "No s’ha pogut eliminar l’adreça"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Torna-ho a provar"; diff --git a/firefox-ios/Shared/Supporting Files/ca.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/ca.lproj/EnhancedTrackingProtection.strings index 762f0e10c271..d2bb5734b439 100644 --- a/firefox-ios/Shared/Supporting Files/ca.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/ca.lproj/EnhancedTrackingProtection.strings @@ -31,8 +31,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Elements de seguiment de xarxes socials: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Elements de seguiment blocats: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,8 +43,7 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Heu desactivat les proteccions"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Esteu protegit. Si detectem alguna cosa, us ho farem saber."; /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ diff --git a/firefox-ios/Shared/Supporting Files/ca.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/ca.lproj/Microsurvey.strings index 762267c48a92..14dc36419596 100644 --- a/firefox-ios/Shared/Supporting Files/ca.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/ca.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "No seleccionat"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Enquesta"; + diff --git a/firefox-ios/Shared/Supporting Files/ca.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/ca.lproj/Toolbar.strings new file mode 100644 index 000000000000..457be81c97d9 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ca.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Pestanya nova"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Tanca aquesta pestanya"; + diff --git a/firefox-ios/Shared/Supporting Files/co.lproj/CredentialProvider.strings b/firefox-ios/Shared/Supporting Files/co.lproj/CredentialProvider.strings index c107301efc77..6b00efdbd975 100644 --- a/firefox-ios/Shared/Supporting Files/co.lproj/CredentialProvider.strings +++ b/firefox-ios/Shared/Supporting Files/co.lproj/CredentialProvider.strings @@ -5,7 +5,7 @@ "LoginsList.NoLoginsFound.Title.v122" = "Nisuna parolla d’intesa arregistrata"; /* Label displayed when a user searches for an item, and no matches can be found against the search query */ -"LoginsList.NoMatchingResult.Title.v122" = "Nisuna parolla d’intesa trova"; +"LoginsList.NoMatchingResult.Title.v122" = "Ùn si trova nisuna parolla d’intesa"; /* Placeholder text for search field in the credential provider list */ "LoginsList.Search.Placeholder.v122" = "Ricercà parolle d’intesa"; diff --git a/firefox-ios/Shared/Supporting Files/co.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/co.lproj/EditAddress.strings new file mode 100644 index 000000000000..cf21681d1037 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/co.lproj/EditAddress.strings @@ -0,0 +1,141 @@ +/* Title for the cancel button in the remove address alert. */ +"Addresses.EditAddress.Alert.CancelButton.v129" = "Abbandunà"; + +/* Message explaining the consequences of removing an address from all synced devices. */ +"Addresses.EditAddress.Alert.Message.v129" = "L’indirizzu serà cacciatu da tutti i vostri apparechji sincrunizati."; + +/* Title for the remove button in the remove address alert. */ +"Addresses.EditAddress.Alert.RemoveButton.v129" = "Caccià"; + +/* Title for the alert indicating the action to remove an address. */ +"Addresses.EditAddress.Alert.Title.v129" = "Caccià l’indirizzu"; + +/* Title for the interface option where users can add a new address for autofill purposes. This facilitates quicker form completion by automatically filling in the user's address information. */ +"Addresses.EditAddress.AutofillAddAddressTitle.v129" = "Aghjunghje un indirizzu"; + +/* Label for the area field, allowing users to specify a particular area within a city or region. This detail can improve the specificity and accuracy of autofilled addresses. */ +"Addresses.EditAddress.AutofillAddressArea.v129" = "Area"; + +/* Label for the field where users input the city part of their address. This information is crucial for mail delivery and service provision, ensuring accurate city identification in autofill settings. */ +"Addresses.EditAddress.AutofillAddressCity.v129" = "Cità"; + +/* Label for the field where users can specify just the country, used in contexts where full address details are not required. Simplifies autofill when only country information is necessary. */ +"Addresses.EditAddress.AutofillAddressCountryOnly.v129" = "Paese"; + +/* Label for the country or region field in address forms, allowing users to specify their country or territorial region. This is fundamental for international mail and services, ensuring autofill accuracy across borders. */ +"Addresses.EditAddress.AutofillAddressCountryRegion.v129" = "Paese o regione"; + +/* Label for the county field, crucial for addressing in regions where county lines play a key role in postal services. Enhances autofill accuracy by including county information. */ +"Addresses.EditAddress.AutofillAddressCounty.v129" = "Pieve"; + +/* Label for the department field, used in countries like France and Colombia where departments are a key administrative division. Ensures correct departmental information is autofilled. */ +"Addresses.EditAddress.AutofillAddressDepartment.v129" = "Dipartimentu"; + +/* Label for the district field in the address form, allowing users to specify their district for more precise location identification. This aids in refining address details for accurate autofill. */ +"Addresses.EditAddress.AutofillAddressDistrict.v129" = "Circundariu"; + +/* Label for the Do/Si field, pertinent to addresses in South Korea. Do/Si refers to provincial level divisions, and specifying this enhances address accuracy in autofill settings. */ +"Addresses.EditAddress.AutofillAddressDoSi.v129" = "Do/Si"; + +/* Label for the Eircode field, specific to Ireland. It's a unique postal code system that helps in precise location identification, enhancing the effectiveness of autofill. */ +"Addresses.EditAddress.AutofillAddressEircode.v129" = "Eircode"; + +/* Label for the email address field, where users input their email. Critical for digital communication and account verification, this ensures email addresses are autofilled accurately. */ +"Addresses.EditAddress.AutofillAddressEmail.v129" = "Indirizzu elettronicu"; + +/* Label for the emirate field, essential for addresses in the United Arab Emirates. Including emirate details ensures the autofill feature accurately represents user addresses. */ +"Addresses.EditAddress.AutofillAddressEmirate.v129" = "Emiratu"; + +/* Label for the field where users specify the name of an island, if applicable. Important for addresses in archipelagic regions, aiding in precise location identification during autofill. */ +"Addresses.EditAddress.AutofillAddressIsland.v129" = "Isula"; + +/* Label for the field where the user inputs their full name as part of an address form. Essential for personalized form submissions and ensuring information accuracy in autofilled forms. */ +"Addresses.EditAddress.AutofillAddressName.v129" = "Nome"; + +/* Label for the field where users can input the name of their neighborhood. This detail adds precision to addresses, especially in densely populated areas, improving the accuracy of autofill. */ +"Addresses.EditAddress.AutofillAddressNeighborhood.v129" = "Quartieru"; + +/* Label for the oblast field, relevant for addresses in countries like Russia and Ukraine. Oblasts are a significant administrative division, and their specification aids in autofill accuracy. */ +"Addresses.EditAddress.AutofillAddressOblast.v129" = "Oblast"; + +/* Label for the input field designated for the organization's name related to the address. Helps in distinguishing addresses used for business or personal purposes in autofill settings. */ +"Addresses.EditAddress.AutofillAddressOrganization.v129" = "Urganismu"; + +/* Label for the parish field, significant in places where parishes are used for local administration and addressing. Ensures users can specify parish details for better autofill accuracy. */ +"Addresses.EditAddress.AutofillAddressParish.v129" = "Parochja"; + +/* Label for the PIN (Postal Index Number) field, used in India. It's a code representing a specific area, crucial for accurate mail delivery and autofill functionality. */ +"Addresses.EditAddress.AutofillAddressPin.v129" = "Pin"; + +/* Label for the postal code field, universally used in address forms to specify the area code for mail sorting. Essential for autofill to ensure mail and services are accurately routed. */ +"Addresses.EditAddress.AutofillAddressPostalCode.v129" = "Codice pustale"; + +/* Label for the post town field, used primarily in the UK and some other regions for mail sorting. Essential for users in applicable areas to specify for correct mail delivery through autofill. */ +"Addresses.EditAddress.AutofillAddressPostTown.v129" = "Cità pustale"; + +/* Label for the prefecture field, essential for addresses in countries like Japan where prefectures are a major administrative division. Aids in precise location specification for autofill. */ +"Addresses.EditAddress.AutofillAddressPrefecture.v129" = "Prefettura"; + +/* Label for the province field, required in countries where provinces are a primary administrative division. Helps in pinpointing the user's location more accurately for autofill purposes. */ +"Addresses.EditAddress.AutofillAddressProvince.v129" = "Pruvincia"; + +/* Label for the state field, a necessary component of addresses in many countries, especially the USA. It ensures that state-specific details are correctly filled in forms using autofill. */ +"Addresses.EditAddress.AutofillAddressState.v129" = "Statu"; + +/* Label for the suburb field, enabling users to add suburb details to their address. This is important for accurate delivery and services in suburban areas, enhancing autofill functionality. */ +"Addresses.EditAddress.AutofillAddressSuburb.v129" = "Circondu"; + +/* Label for the telephone number field, allowing users to input their contact number. This is essential for communication and service provision, ensuring contact details are autofilled correctly. */ +"Addresses.EditAddress.AutofillAddressTel.v129" = "Telefonu"; + +/* Label for the input field for the townland, a specific type of land division used in rural areas. Enhances address detail for users in regions where townlands are a common addressing component. */ +"Addresses.EditAddress.AutofillAddressTownland.v129" = "Paisolu"; + +/* Label for the field to input the name of a village or township. This is crucial for addresses in rural areas, ensuring the autofill feature accurately captures all necessary geographical details. */ +"Addresses.EditAddress.AutofillAddressVillageTownship.v129" = "Paese o cantone"; + +/* Label for the ZIP code field, primarily used in the United States for mail sorting. Key for autofill to accurately complete addresses for shipping, billing, and service provision. */ +"Addresses.EditAddress.AutofillAddressZip.v129" = "Codice pustale (Stati Uniti)"; + +/* Label for the button to cancel the current autofill operation or exit the form without saving changes. Provides users with an option to back out of a process without making any modifications. */ +"Addresses.EditAddress.AutofillCancelButton.v129" = "Abbandunà"; + +/* Title for the option allowing users to edit an existing saved address. This is used within the settings for autofill, enabling users to update their address details for accurate form autofilling. */ +"Addresses.EditAddress.AutofillEditAddressTitle.v129" = "Mudificà l’indirizzu"; + +/* Title for the input field where users can enter their street address. This is used within the settings for autofill, allowing users to provide their street address for accurate form autofilling. */ +"Addresses.EditAddress.AutofillEditStreetAddressTitle.v129" = "Indirizzu pustale"; + +/* Label for the button to save the current address details entered or edited by the user. This action confirms the user's changes and updates their autofill settings accordingly. */ +"Addresses.EditAddress.AutofillSaveButton.v129" = "Arregistrà"; + +/* Title for the option allowing users to view an existing saved address. This is used within the settings for autofill, enabling users to see their address details for accurate form autofilling. */ +"Addresses.EditAddress.AutofillViewAddressTitle.v129" = "Fighjà l’indirizzu"; + +/* Button label for closing the view where user can view their address info. */ +"Addresses.EditAddress.CloseNavBarButtonLabel.v129" = "Chjode"; + +/* Button label for editing the address details shown in the form. */ +"Addresses.EditAddress.EditNavBarButtonLabel.v129" = "Mudificà"; + +/* Title for button that offers the user the option to remove an address. */ +"Addresses.EditAddress.RemoveAddressButtonTitle.v129" = "Caccià l’indirizzu"; + +/* Toast message confirming that an address has been successfully removed. */ +"Addresses.Toast.AddressRemovedConfirmation.v129" = "Indirizzu cacciatu"; + +/* Toast message confirming that an address has been successfully saved. */ +"Addresses.Toast.AddressSavedConfirmation.v129" = "Indirizzu arregistratu"; + +/* Toast message indicating an error occurred while trying to save an address. */ +"Addresses.Toast.AddressSaveError.v129" = "L’indirizzu ùn pò micca esse arregistratu"; + +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "L’indirizzu ùn pò micca esse cacciatu"; + +/* Suggestion to try again after an error occurred while trying to save an address. */ +"Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Pruvà torna"; + +/* Toast message confirming that an address has been successfully updated. */ +"Addresses.Toast.AddressUpdatedConfirmation.v129" = "Infurmazioni d’indirizzu mudificate"; + diff --git a/firefox-ios/Shared/Supporting Files/co.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/co.lproj/EnhancedTrackingProtection.strings index 7c6e80c96974..7b3d1004f580 100644 --- a/firefox-ios/Shared/Supporting Files/co.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/co.lproj/EnhancedTrackingProtection.strings @@ -22,8 +22,19 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Cunnessione micca assicurata"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Ùn si trova nisunu perseguitatore"; + +/* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Canistrelli di spiunagiu intersiti : %@"; + +/* Text to let users know how many fingerprinters were blocked on the current website. The placeholder will show the number of fingerprinters detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.Fingerprinter.v129" = "Detettori d’impronta numerica : %@"; + +/* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Perseguitatori di rete suciale : %@"; + +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Perseguitatori bluccati : %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -35,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Avete disattivatu e prutezzioni"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Site prutetti. S’è no fighjemu qualcosa, a vi feremu sapè."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "A vostra cunnessione ùn hè micca assicurata."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Fate casu nant’à stu situ"; @@ -54,6 +67,9 @@ /* A switch to disable enhanced tracking protection inside the menu. */ "Menu.EnhancedTrackingProtection.SwitchOff.Text.v128" = "E prutezzioni sò disattivate. Vi ricumandemu d’attivalle torna."; +/* A switch to disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.SwitchOff.Text.v129" = "E prutezzioni sò disattivate. Vi ricumandemu di riattivalle torna."; + /* A switch to disable enhanced tracking protection inside the menu. */ "Menu.EnhancedTrackingProtection.SwitchOn.Text.v128" = "S’è qualcosa ùn pare micca funziunà nant’à stu situ, pruvate di disattivà sta funzione."; diff --git a/firefox-ios/Shared/Supporting Files/co.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/co.lproj/Microsurvey.strings index 980178212a95..54bdcaf851d0 100644 --- a/firefox-ios/Shared/Supporting Files/co.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/co.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Diselezziunatu"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Inchiesta"; + diff --git a/firefox-ios/Shared/Supporting Files/co.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/co.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..89bccd413774 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/co.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Attualizà"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "Un sbagliu SSL hè accadutu è ùn si pò stabilisce una cunnessione sicura cù u servitore."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "Fate casu. Pare ch’ella ci sia un prublema."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "Pruvate di cunnettevi nant’à un altru apparechju. Verificate u vostru modem o « router ». Discunnittitevi è ricunnittitevi à u Wi-Fi."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "Pare ch’ella ci sia un prublema cù a vostra cunnessione internet."; + diff --git a/firefox-ios/Shared/Supporting Files/co.lproj/PasswordAutofill.strings b/firefox-ios/Shared/Supporting Files/co.lproj/PasswordAutofill.strings index 359b0f87986f..d7efabd56cd0 100644 --- a/firefox-ios/Shared/Supporting Files/co.lproj/PasswordAutofill.strings +++ b/firefox-ios/Shared/Supporting Files/co.lproj/PasswordAutofill.strings @@ -1,3 +1,6 @@ +/* This label is used in a cell found in the list of autofill login options in place of an actual username to denote that no username was saved for this login */ +"PasswordAutofill.LoginListCellNoUsername.v129" = "(nisunu nome d’utilizatore)"; + /* This label is used for a button in the password list screen allowing users to manage their saved passwords. It's meant to direct users to where they can add, remove, or edit their saved passwords. */ "PasswordAutofill.ManagePasswordsButton.v124" = "Amministrà e parolle d’intesa"; diff --git a/firefox-ios/Shared/Supporting Files/co.lproj/ScanQRCode.strings b/firefox-ios/Shared/Supporting Files/co.lproj/ScanQRCode.strings new file mode 100644 index 000000000000..c331f818c01b --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/co.lproj/ScanQRCode.strings @@ -0,0 +1,9 @@ +/* Allow button to open URL from scanned QR Code */ +"ScanQRCode.ConfirmOpenURL.AllowButton.v129" = "Permette"; + +/* Deny button to cancel opening URL from scanned QR Code */ +"ScanQRCode.ConfirmOpenURL.DenyButton.v129" = "Ricusà"; + +/* Text of the prompt to ask user permission to open a URL from a scanned QR code. Placeholder is app name. */ +"ScanQRCode.ConfirmOpenURL.Message.v129" = "Permette l’apertura da %@ ?"; + diff --git a/firefox-ios/Shared/Supporting Files/co.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/co.lproj/Settings.strings index 3306ba56806c..c133a9e2ab6b 100644 --- a/firefox-ios/Shared/Supporting Files/co.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/co.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Urganizà l’indirizzi"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "Indirizzu per %@"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "INDIRIZZI ARREGISTRATI"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Arregistrà l’indirizzi in %@"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "Arregistrate e vostre infurmazioni in tutta sicurità per accedeci prestu prestu un’altra volta."; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "Include i numeri di telefonu è l’indirizzi elettronichi"; diff --git a/firefox-ios/Shared/Supporting Files/co.lproj/Shopping.strings b/firefox-ios/Shared/Supporting Files/co.lproj/Shopping.strings index 6a818cea8e44..f4aaa96b87a0 100644 --- a/firefox-ios/Shared/Supporting Files/co.lproj/Shopping.strings +++ b/firefox-ios/Shared/Supporting Files/co.lproj/Shopping.strings @@ -248,8 +248,8 @@ "Shopping.Sheet.Title.v120" = "Verificadore d’avisu"; /* Text for body of error card displayed to the user when the device is disconnected from the network. */ -"Shopping.WarningCard.CheckNoConnection.Description.v120" = "Verificate a vostra cunnessione di reta è pruvate di ricaricà a pagina."; +"Shopping.WarningCard.CheckNoConnection.Description.v120" = "Verificate a vostra cunnessione à a reta è pruvate di ricaricà a pagina."; /* Title for error card displayed to the user when the device is disconnected from the network. */ -"Shopping.WarningCard.CheckNoConnection.Title.v120" = "Nisuna cunnessione di reta"; +"Shopping.WarningCard.CheckNoConnection.Title.v120" = "Nisuna cunnessione à a reta"; diff --git a/firefox-ios/Shared/Supporting Files/co.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/co.lproj/Toolbar.strings new file mode 100644 index 000000000000..5244f79135cf --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/co.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Unghjetta nova"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Chjode st’unghjetta"; + diff --git a/firefox-ios/Shared/Supporting Files/cs.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/cs.lproj/EditAddress.strings index 30ce8f3ddd39..d1aa5bf08f3e 100644 --- a/firefox-ios/Shared/Supporting Files/cs.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/cs.lproj/EditAddress.strings @@ -121,3 +121,21 @@ /* Title for button that offers the user the option to remove an address. */ "Addresses.EditAddress.RemoveAddressButtonTitle.v129" = "Odebrat adresu"; +/* Toast message confirming that an address has been successfully removed. */ +"Addresses.Toast.AddressRemovedConfirmation.v129" = "Adresa byla odebrána"; + +/* Toast message confirming that an address has been successfully saved. */ +"Addresses.Toast.AddressSavedConfirmation.v129" = "Adresa byla uložena"; + +/* Toast message indicating an error occurred while trying to save an address. */ +"Addresses.Toast.AddressSaveError.v129" = "Adresu se nepodařilo uložit"; + +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Adresu se nepodařilo odebrat"; + +/* Suggestion to try again after an error occurred while trying to save an address. */ +"Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Zkusit znovu"; + +/* Toast message confirming that an address has been successfully updated. */ +"Addresses.Toast.AddressUpdatedConfirmation.v129" = "Informace o adrese byla aktualizována"; + diff --git a/firefox-ios/Shared/Supporting Files/cs.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/cs.lproj/EnhancedTrackingProtection.strings index 7289f55d2ec0..62ce9463afec 100644 --- a/firefox-ios/Shared/Supporting Files/cs.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/cs.lproj/EnhancedTrackingProtection.strings @@ -22,8 +22,19 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Spojení není zabezpečené"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Nebyly nalezeny žádné sledovací prvky"; + +/* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Sledovací cross-site cookies: %@"; + +/* Text to let users know how many fingerprinters were blocked on the current website. The placeholder will show the number of fingerprinters detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.Fingerprinter.v129" = "Vytváření otisku prohlížeče: %@"; + +/* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Sledovací prvky sociálních sítí: %@"; + +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Zablokované sledovací prvky: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -35,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Vypnuli jste ochranu"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Jste chráněni. Pokud něco zjistíme, dáme vám vědět."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "Vaše připojení není zabezpečené."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Buďte na této stránce opatrní"; @@ -54,6 +67,9 @@ /* A switch to disable enhanced tracking protection inside the menu. */ "Menu.EnhancedTrackingProtection.SwitchOff.Text.v128" = "Ochrana je vypnuta. Doporučujeme ji znovu zapnout."; +/* A switch to disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.SwitchOff.Text.v129" = "Ochrana je VYPNUTÁ. Doporučujeme ji znovu zapnout."; + /* A switch to disable enhanced tracking protection inside the menu. */ "Menu.EnhancedTrackingProtection.SwitchOn.Text.v128" = "Pokud se vám zdá, že je něco na této stránce nefunkční, zkuste to vypnout."; diff --git a/firefox-ios/Shared/Supporting Files/cs.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/cs.lproj/Microsurvey.strings index d0cd2f26635a..16d909566053 100644 --- a/firefox-ios/Shared/Supporting Files/cs.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/cs.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Výběr zrušen"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Dotazník"; + diff --git a/firefox-ios/Shared/Supporting Files/cs.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/cs.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..f67b00fb2bb4 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/cs.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Obnovit"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "Došlo k chybě protokolu SSL a zabezpečené připojení k serveru nelze navázat."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "Buďte opatrní. Něco není v pořádku."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "Zkuste se připojit na jiném zařízení. Zkontrolujte modem nebo router. Odpojte se a znovu se připojte k Wi-Fi."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "Zdá se, že došlo k problému s připojením k internetu."; + diff --git a/firefox-ios/Shared/Supporting Files/cs.lproj/PasswordAutofill.strings b/firefox-ios/Shared/Supporting Files/cs.lproj/PasswordAutofill.strings index a8d992ad724a..dd957155eb05 100644 --- a/firefox-ios/Shared/Supporting Files/cs.lproj/PasswordAutofill.strings +++ b/firefox-ios/Shared/Supporting Files/cs.lproj/PasswordAutofill.strings @@ -1,3 +1,6 @@ +/* This label is used in a cell found in the list of autofill login options in place of an actual username to denote that no username was saved for this login */ +"PasswordAutofill.LoginListCellNoUsername.v129" = "(žádné uživatelské jméno)"; + /* This label is used for a button in the password list screen allowing users to manage their saved passwords. It's meant to direct users to where they can add, remove, or edit their saved passwords. */ "PasswordAutofill.ManagePasswordsButton.v124" = "Správa hesel"; diff --git a/firefox-ios/Shared/Supporting Files/cs.lproj/ScanQRCode.strings b/firefox-ios/Shared/Supporting Files/cs.lproj/ScanQRCode.strings new file mode 100644 index 000000000000..3f3b504a5968 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/cs.lproj/ScanQRCode.strings @@ -0,0 +1,9 @@ +/* Allow button to open URL from scanned QR Code */ +"ScanQRCode.ConfirmOpenURL.AllowButton.v129" = "Povolit"; + +/* Deny button to cancel opening URL from scanned QR Code */ +"ScanQRCode.ConfirmOpenURL.DenyButton.v129" = "Zakázat"; + +/* Text of the prompt to ask user permission to open a URL from a scanned QR code. Placeholder is app name. */ +"ScanQRCode.ConfirmOpenURL.Message.v129" = "Povolit aplikaci %@ ji otevřít?"; + diff --git a/firefox-ios/Shared/Supporting Files/cs.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/cs.lproj/Settings.strings index 804523c0fe9a..bc16f1c8f97c 100644 --- a/firefox-ios/Shared/Supporting Files/cs.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/cs.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Správa adres"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "Adresa pro %@"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "ULOŽENÉ ADRESY"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Uložit adresy do %@"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "Bezpečně si uložte své informace, abyste k nim měli později rychlý přístup."; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "Zahrnuje telefonní čísla a e-mailové adresy"; diff --git a/firefox-ios/Shared/Supporting Files/cs.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/cs.lproj/Toolbar.strings new file mode 100644 index 000000000000..9992c9ce23a7 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/cs.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Nový panel"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Zavřít tento panel"; + diff --git a/firefox-ios/Shared/Supporting Files/cy.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/cy.lproj/EditAddress.strings index 58bedcbce683..ffb343e9439f 100644 --- a/firefox-ios/Shared/Supporting Files/cy.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/cy.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "Nid Oedd Modd Cadw'r Cyfeiriad"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Nid Oedd Modd Dileu'r Cyfeiriad"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Ceisiwch eto"; diff --git a/firefox-ios/Shared/Supporting Files/cy.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/cy.lproj/EnhancedTrackingProtection.strings index 6dc710435931..1e0a725cbf1c 100644 --- a/firefox-ios/Shared/Supporting Files/cy.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/cy.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Nid yw’r cysylltiad yn ddiogel"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Heb ganfod tracwyr"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Cwcis tracio traws-gwefan: %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Tracwyr cyfryngau cymdeithasol: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Tracwyr wedi'u rhwystro: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Rydych wedi diffodd y mesurau diogelwch"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Rydych wedi'ch diogelu. Os byddwn yn sylwi ar rywbeth, byddwn yn rhoi gwybod ichi."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "Nid yw eich cysylltiad yn ddiogel."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Byddwch yn ofalus ar y wefan hon"; diff --git a/firefox-ios/Shared/Supporting Files/cy.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/cy.lproj/Microsurvey.strings index 23e7cc6373f9..93df06f91f49 100644 --- a/firefox-ios/Shared/Supporting Files/cy.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/cy.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Heb ei ddewis"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Arolwg"; + diff --git a/firefox-ios/Shared/Supporting Files/cy.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/cy.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..430367c5177d --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/cy.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Ail-lwytho"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "Mae gwall SSL wedi digwydd ac nid oes modd gwneud cysylltiad diogel â'r gweinydd."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "Byddwch yn ofalus. Nid yw rhywbeth yn edrych yn iawn."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "Ceisiwch gysylltu ar ddyfais wahanol. Gwiriwch eich modem neu lwybrydd. Datgysylltwch ac ailgysylltwch â'r Wi-Fi."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "Mae'n ymddangos bod problem gyda'ch cysylltiad rhyngrwyd."; + diff --git a/firefox-ios/Shared/Supporting Files/cy.lproj/PasswordAutofill.strings b/firefox-ios/Shared/Supporting Files/cy.lproj/PasswordAutofill.strings index e2f3d4fece34..68f069f4c1f7 100644 --- a/firefox-ios/Shared/Supporting Files/cy.lproj/PasswordAutofill.strings +++ b/firefox-ios/Shared/Supporting Files/cy.lproj/PasswordAutofill.strings @@ -1,3 +1,6 @@ +/* This label is used in a cell found in the list of autofill login options in place of an actual username to denote that no username was saved for this login */ +"PasswordAutofill.LoginListCellNoUsername.v129" = "(dim enw defnyddiwr)"; + /* This label is used for a button in the password list screen allowing users to manage their saved passwords. It's meant to direct users to where they can add, remove, or edit their saved passwords. */ "PasswordAutofill.ManagePasswordsButton.v124" = "Rheoli cyfrineiriau"; diff --git a/firefox-ios/Shared/Supporting Files/cy.lproj/ScanQRCode.strings b/firefox-ios/Shared/Supporting Files/cy.lproj/ScanQRCode.strings new file mode 100644 index 000000000000..c8921eabbb96 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/cy.lproj/ScanQRCode.strings @@ -0,0 +1,9 @@ +/* Allow button to open URL from scanned QR Code */ +"ScanQRCode.ConfirmOpenURL.AllowButton.v129" = "Caniatáu"; + +/* Deny button to cancel opening URL from scanned QR Code */ +"ScanQRCode.ConfirmOpenURL.DenyButton.v129" = "Gwrthod"; + +/* Text of the prompt to ask user permission to open a URL from a scanned QR code. Placeholder is app name. */ +"ScanQRCode.ConfirmOpenURL.Message.v129" = "Caniatáu i %@ agor?"; + diff --git a/firefox-ios/Shared/Supporting Files/cy.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/cy.lproj/Settings.strings index 9ad9bca7e336..b91970353a49 100644 --- a/firefox-ios/Shared/Supporting Files/cy.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/cy.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Rheoli cyfeiriadau"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "Cyfeiriad %@"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "CYFEIRIADAU WEDI'U CADW"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Cadw Cyfeiriadau i %@"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "Cadwch eich manylion yn ddiogel er mwyn cael mynediad cyflym atyn nhw'n nes ymlaen."; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "Yn cynnwys rhifau ffôn a chyfeiriadau e-bost"; diff --git a/firefox-ios/Shared/Supporting Files/cy.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/cy.lproj/Toolbar.strings new file mode 100644 index 000000000000..c1cb28c213eb --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/cy.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Tab Newydd"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Cau'r Tab Hwn"; + diff --git a/firefox-ios/Shared/Supporting Files/da.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/da.lproj/EditAddress.strings new file mode 100644 index 000000000000..4d78e65b1d5e --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/da.lproj/EditAddress.strings @@ -0,0 +1,141 @@ +/* Title for the cancel button in the remove address alert. */ +"Addresses.EditAddress.Alert.CancelButton.v129" = "Annuller"; + +/* Message explaining the consequences of removing an address from all synced devices. */ +"Addresses.EditAddress.Alert.Message.v129" = "Adressen vil blive fjernet fra alle dine synkroniserede enheder."; + +/* Title for the remove button in the remove address alert. */ +"Addresses.EditAddress.Alert.RemoveButton.v129" = "Fjern"; + +/* Title for the alert indicating the action to remove an address. */ +"Addresses.EditAddress.Alert.Title.v129" = "Fjern adresse"; + +/* Title for the interface option where users can add a new address for autofill purposes. This facilitates quicker form completion by automatically filling in the user's address information. */ +"Addresses.EditAddress.AutofillAddAddressTitle.v129" = "Tilføj adresse"; + +/* Label for the area field, allowing users to specify a particular area within a city or region. This detail can improve the specificity and accuracy of autofilled addresses. */ +"Addresses.EditAddress.AutofillAddressArea.v129" = "Area"; + +/* Label for the field where users input the city part of their address. This information is crucial for mail delivery and service provision, ensuring accurate city identification in autofill settings. */ +"Addresses.EditAddress.AutofillAddressCity.v129" = "By"; + +/* Label for the field where users can specify just the country, used in contexts where full address details are not required. Simplifies autofill when only country information is necessary. */ +"Addresses.EditAddress.AutofillAddressCountryOnly.v129" = "Land"; + +/* Label for the country or region field in address forms, allowing users to specify their country or territorial region. This is fundamental for international mail and services, ensuring autofill accuracy across borders. */ +"Addresses.EditAddress.AutofillAddressCountryRegion.v129" = "Land eller region"; + +/* Label for the county field, crucial for addressing in regions where county lines play a key role in postal services. Enhances autofill accuracy by including county information. */ +"Addresses.EditAddress.AutofillAddressCounty.v129" = "County"; + +/* Label for the department field, used in countries like France and Colombia where departments are a key administrative division. Ensures correct departmental information is autofilled. */ +"Addresses.EditAddress.AutofillAddressDepartment.v129" = "Department"; + +/* Label for the district field in the address form, allowing users to specify their district for more precise location identification. This aids in refining address details for accurate autofill. */ +"Addresses.EditAddress.AutofillAddressDistrict.v129" = "District"; + +/* Label for the Do/Si field, pertinent to addresses in South Korea. Do/Si refers to provincial level divisions, and specifying this enhances address accuracy in autofill settings. */ +"Addresses.EditAddress.AutofillAddressDoSi.v129" = "Do/Si"; + +/* Label for the Eircode field, specific to Ireland. It's a unique postal code system that helps in precise location identification, enhancing the effectiveness of autofill. */ +"Addresses.EditAddress.AutofillAddressEircode.v129" = "Eircode"; + +/* Label for the email address field, where users input their email. Critical for digital communication and account verification, this ensures email addresses are autofilled accurately. */ +"Addresses.EditAddress.AutofillAddressEmail.v129" = "Mailadresse"; + +/* Label for the emirate field, essential for addresses in the United Arab Emirates. Including emirate details ensures the autofill feature accurately represents user addresses. */ +"Addresses.EditAddress.AutofillAddressEmirate.v129" = "Emirat"; + +/* Label for the field where users specify the name of an island, if applicable. Important for addresses in archipelagic regions, aiding in precise location identification during autofill. */ +"Addresses.EditAddress.AutofillAddressIsland.v129" = "Ø"; + +/* Label for the field where the user inputs their full name as part of an address form. Essential for personalized form submissions and ensuring information accuracy in autofilled forms. */ +"Addresses.EditAddress.AutofillAddressName.v129" = "Navn"; + +/* Label for the field where users can input the name of their neighborhood. This detail adds precision to addresses, especially in densely populated areas, improving the accuracy of autofill. */ +"Addresses.EditAddress.AutofillAddressNeighborhood.v129" = "Neighborhood"; + +/* Label for the oblast field, relevant for addresses in countries like Russia and Ukraine. Oblasts are a significant administrative division, and their specification aids in autofill accuracy. */ +"Addresses.EditAddress.AutofillAddressOblast.v129" = "Oblast"; + +/* Label for the input field designated for the organization's name related to the address. Helps in distinguishing addresses used for business or personal purposes in autofill settings. */ +"Addresses.EditAddress.AutofillAddressOrganization.v129" = "Organisation"; + +/* Label for the parish field, significant in places where parishes are used for local administration and addressing. Ensures users can specify parish details for better autofill accuracy. */ +"Addresses.EditAddress.AutofillAddressParish.v129" = "Parish"; + +/* Label for the PIN (Postal Index Number) field, used in India. It's a code representing a specific area, crucial for accurate mail delivery and autofill functionality. */ +"Addresses.EditAddress.AutofillAddressPin.v129" = "Pin"; + +/* Label for the postal code field, universally used in address forms to specify the area code for mail sorting. Essential for autofill to ensure mail and services are accurately routed. */ +"Addresses.EditAddress.AutofillAddressPostalCode.v129" = "Postnummer"; + +/* Label for the post town field, used primarily in the UK and some other regions for mail sorting. Essential for users in applicable areas to specify for correct mail delivery through autofill. */ +"Addresses.EditAddress.AutofillAddressPostTown.v129" = "Post town"; + +/* Label for the prefecture field, essential for addresses in countries like Japan where prefectures are a major administrative division. Aids in precise location specification for autofill. */ +"Addresses.EditAddress.AutofillAddressPrefecture.v129" = "Prefecture"; + +/* Label for the province field, required in countries where provinces are a primary administrative division. Helps in pinpointing the user's location more accurately for autofill purposes. */ +"Addresses.EditAddress.AutofillAddressProvince.v129" = "Område"; + +/* Label for the state field, a necessary component of addresses in many countries, especially the USA. It ensures that state-specific details are correctly filled in forms using autofill. */ +"Addresses.EditAddress.AutofillAddressState.v129" = "Stat"; + +/* Label for the suburb field, enabling users to add suburb details to their address. This is important for accurate delivery and services in suburban areas, enhancing autofill functionality. */ +"Addresses.EditAddress.AutofillAddressSuburb.v129" = "Suburb"; + +/* Label for the telephone number field, allowing users to input their contact number. This is essential for communication and service provision, ensuring contact details are autofilled correctly. */ +"Addresses.EditAddress.AutofillAddressTel.v129" = "Telefonnummer"; + +/* Label for the input field for the townland, a specific type of land division used in rural areas. Enhances address detail for users in regions where townlands are a common addressing component. */ +"Addresses.EditAddress.AutofillAddressTownland.v129" = "Townland"; + +/* Label for the field to input the name of a village or township. This is crucial for addresses in rural areas, ensuring the autofill feature accurately captures all necessary geographical details. */ +"Addresses.EditAddress.AutofillAddressVillageTownship.v129" = "Village eller Township"; + +/* Label for the ZIP code field, primarily used in the United States for mail sorting. Key for autofill to accurately complete addresses for shipping, billing, and service provision. */ +"Addresses.EditAddress.AutofillAddressZip.v129" = "Zip code"; + +/* Label for the button to cancel the current autofill operation or exit the form without saving changes. Provides users with an option to back out of a process without making any modifications. */ +"Addresses.EditAddress.AutofillCancelButton.v129" = "Annuller"; + +/* Title for the option allowing users to edit an existing saved address. This is used within the settings for autofill, enabling users to update their address details for accurate form autofilling. */ +"Addresses.EditAddress.AutofillEditAddressTitle.v129" = "Rediger adresse"; + +/* Title for the input field where users can enter their street address. This is used within the settings for autofill, allowing users to provide their street address for accurate form autofilling. */ +"Addresses.EditAddress.AutofillEditStreetAddressTitle.v129" = "Postadresse"; + +/* Label for the button to save the current address details entered or edited by the user. This action confirms the user's changes and updates their autofill settings accordingly. */ +"Addresses.EditAddress.AutofillSaveButton.v129" = "Gem"; + +/* Title for the option allowing users to view an existing saved address. This is used within the settings for autofill, enabling users to see their address details for accurate form autofilling. */ +"Addresses.EditAddress.AutofillViewAddressTitle.v129" = "Vis adresse"; + +/* Button label for closing the view where user can view their address info. */ +"Addresses.EditAddress.CloseNavBarButtonLabel.v129" = "Luk"; + +/* Button label for editing the address details shown in the form. */ +"Addresses.EditAddress.EditNavBarButtonLabel.v129" = "Rediger"; + +/* Title for button that offers the user the option to remove an address. */ +"Addresses.EditAddress.RemoveAddressButtonTitle.v129" = "Fjern adresse"; + +/* Toast message confirming that an address has been successfully removed. */ +"Addresses.Toast.AddressRemovedConfirmation.v129" = "Adresse fjernet"; + +/* Toast message confirming that an address has been successfully saved. */ +"Addresses.Toast.AddressSavedConfirmation.v129" = "Adressen blev gemt"; + +/* Toast message indicating an error occurred while trying to save an address. */ +"Addresses.Toast.AddressSaveError.v129" = "Adressen kunne ikke gemmes"; + +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Adressen kunne ikke fjernes"; + +/* Suggestion to try again after an error occurred while trying to save an address. */ +"Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Prøv igen"; + +/* Toast message confirming that an address has been successfully updated. */ +"Addresses.Toast.AddressUpdatedConfirmation.v129" = "Adresse-oplysningerne blev opdateret"; + diff --git a/firefox-ios/Shared/Supporting Files/da.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/da.lproj/EnhancedTrackingProtection.strings index 0689a3d68f97..e8448f4d96d9 100644 --- a/firefox-ios/Shared/Supporting Files/da.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/da.lproj/EnhancedTrackingProtection.strings @@ -22,8 +22,19 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Forbindelsen er ikke sikker"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Ingen sporings-mekanismer fundet"; + +/* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Sporings-cookies på tværs af websteder: %@"; + +/* Text to let users know how many fingerprinters were blocked on the current website. The placeholder will show the number of fingerprinters detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.Fingerprinter.v129" = "Fingerprinters: %@"; + +/* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Sporing via sociale medier: %@"; + +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Forsøg på sporing blokeret: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -35,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Du har slået beskyttelse fra"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Du er beskyttet. Vi giver dig besked, hvis vi opdager noget."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "Din forbindelse er ikke sikker."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Vær forsigtig på dette websted"; @@ -54,6 +67,9 @@ /* A switch to disable enhanced tracking protection inside the menu. */ "Menu.EnhancedTrackingProtection.SwitchOff.Text.v128" = "Beskyttelse er SLÅET FRA. Vi anbefaler, at du slår det til igen."; +/* A switch to disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.SwitchOff.Text.v129" = "Beskyttelse er SLÅET FRA. Vi anbefaler, at du slår det til igen."; + /* A switch to disable enhanced tracking protection inside the menu. */ "Menu.EnhancedTrackingProtection.SwitchOn.Text.v128" = "Hvis noget på webstedet ikke ser ud til at virke som det skal, så prøv at slå beskyttelse fra."; diff --git a/firefox-ios/Shared/Supporting Files/da.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/da.lproj/Microsurvey.strings index cb39cbf2214c..f16497da59c9 100644 --- a/firefox-ios/Shared/Supporting Files/da.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/da.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Ikke valgt"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Undersøgelse"; + diff --git a/firefox-ios/Shared/Supporting Files/da.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/da.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..5a90c7f02a15 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/da.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Genindlæs"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "Der er opstået en SSL-fejl, og en sikker forbindelse til serveren kan ikke oprettes."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "Vær forsigtig. Noget ser ikke rigtigt ud."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "Prøv at oprette forbindelse med en anden enhed. Kontroller dit modem eller din router. Afbryd wi-fi-forbindelsen og opret den igen."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "Det ser ud til, at der er et problem med din internetforbindelse."; + diff --git a/firefox-ios/Shared/Supporting Files/da.lproj/PasswordAutofill.strings b/firefox-ios/Shared/Supporting Files/da.lproj/PasswordAutofill.strings index 711ff5834c46..006515f83b65 100644 --- a/firefox-ios/Shared/Supporting Files/da.lproj/PasswordAutofill.strings +++ b/firefox-ios/Shared/Supporting Files/da.lproj/PasswordAutofill.strings @@ -1,3 +1,6 @@ +/* This label is used in a cell found in the list of autofill login options in place of an actual username to denote that no username was saved for this login */ +"PasswordAutofill.LoginListCellNoUsername.v129" = "(intet brugernavn)"; + /* This label is used for a button in the password list screen allowing users to manage their saved passwords. It's meant to direct users to where they can add, remove, or edit their saved passwords. */ "PasswordAutofill.ManagePasswordsButton.v124" = "Håndter adgangskoder"; diff --git a/firefox-ios/Shared/Supporting Files/da.lproj/ScanQRCode.strings b/firefox-ios/Shared/Supporting Files/da.lproj/ScanQRCode.strings new file mode 100644 index 000000000000..b693442ac70b --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/da.lproj/ScanQRCode.strings @@ -0,0 +1,9 @@ +/* Allow button to open URL from scanned QR Code */ +"ScanQRCode.ConfirmOpenURL.AllowButton.v129" = "Tillad"; + +/* Deny button to cancel opening URL from scanned QR Code */ +"ScanQRCode.ConfirmOpenURL.DenyButton.v129" = "Afvis"; + +/* Text of the prompt to ask user permission to open a URL from a scanned QR code. Placeholder is app name. */ +"ScanQRCode.ConfirmOpenURL.Message.v129" = "Tillad at %@ åbnes?"; + diff --git a/firefox-ios/Shared/Supporting Files/da.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/da.lproj/Settings.strings index 442c9e9ccfef..353ccfff463d 100644 --- a/firefox-ios/Shared/Supporting Files/da.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/da.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Håndter adresser"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "Adresse til %@"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "GEMTE ADRESSER"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Gem adresser i %@"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "Gem dine oplysninger sikkert for at have adgang til dem igen senere."; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "Inkluderer telefonnumre og mailadresser"; diff --git a/firefox-ios/Shared/Supporting Files/da.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/da.lproj/Toolbar.strings new file mode 100644 index 000000000000..2541f1d3f1b7 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/da.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Nyt faneblad"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Luk dette faneblad"; + diff --git a/firefox-ios/Shared/Supporting Files/de.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/de.lproj/EditAddress.strings index 2f8c2e92b5c2..48a2690a7d75 100644 --- a/firefox-ios/Shared/Supporting Files/de.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/de.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "Adresse konnte nicht gespeichert werden"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Adresse konnte nicht entfernt werden"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Erneut versuchen"; diff --git a/firefox-ios/Shared/Supporting Files/de.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/de.lproj/EnhancedTrackingProtection.strings index 870a5483ade0..0d948a827f6e 100644 --- a/firefox-ios/Shared/Supporting Files/de.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/de.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Verbindung nicht sicher"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Keine Tracker gefunden"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Cookies zur seitenübergreifenden Aktivitätenverfolgung: %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Skripte zur Aktivitätenverfolgung durch soziale Netzwerke: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Blockierte Tracker: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Sie haben die Schutzmaßnahmen deaktiviert"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Sie sind geschützt. Wenn wir etwas entdecken, sagen wir Ihnen es."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "Ihre Verbindung ist nicht sicher."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Seien Sie vorsichtig auf dieser Website"; diff --git a/firefox-ios/Shared/Supporting Files/de.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/de.lproj/Microsurvey.strings index 70d3dc058305..59e408965701 100644 --- a/firefox-ios/Shared/Supporting Files/de.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/de.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Auswahl aufgehoben"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Umfrage"; + diff --git a/firefox-ios/Shared/Supporting Files/de.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/de.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..3dc1f254dabb --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/de.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Neu laden"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "Ein SSL-Fehler ist aufgetreten, daher kann keine sichere Verbindung zu diesem Server hergestellt werden."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "Seien Sie vorsichtig. Irgendetwas stimmt hier nicht."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "Versuchen Sie, auf einem anderen Gerät eine Verbindung herzustellen. Überprüfen Sie Ihr Modem oder Router. Trennen Sie die WLAN-Verbindung und stellen Sie die Verbindung wieder her."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "Anscheinend gibt es ein Problem mit Ihrer Internetverbindung."; + diff --git a/firefox-ios/Shared/Supporting Files/de.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/de.lproj/Settings.strings index 6dfe889e8efc..464baee6a577 100644 --- a/firefox-ios/Shared/Supporting Files/de.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/de.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Adressen verwalten"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "Adresse für %@"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "GESPEICHERTE ADRESSEN"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Adressen in %@ speichern"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "Speichern Sie Ihre Daten sicher, um später schnell darauf zugreifen zu können."; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "Enthält Telefonnummern und E-Mail-Adressen"; diff --git a/firefox-ios/Shared/Supporting Files/de.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/de.lproj/Toolbar.strings new file mode 100644 index 000000000000..f051d3add7fa --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/de.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Neuer Tab"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Diesen Tab schließen"; + diff --git a/firefox-ios/Shared/Supporting Files/dsb.lproj/Alerts.strings b/firefox-ios/Shared/Supporting Files/dsb.lproj/Alerts.strings index fccbb9aa17fe..c5ac5fcb5e47 100644 --- a/firefox-ios/Shared/Supporting Files/dsb.lproj/Alerts.strings +++ b/firefox-ios/Shared/Supporting Files/dsb.lproj/Alerts.strings @@ -14,7 +14,7 @@ "Alerts.RestoreTabs.Button.No.v109" = "Ně"; /* The title for the affirmative action of the restore tabs pop-up alert. This alert shows when opening up Firefox after it crashed, and will restore existing tabs. */ -"Alerts.RestoreTabs.Button.Yes.v109" = "Rejtarki wótnowiś"; +"Alerts.RestoreTabs.Button.Yes.v109" = "Rejtariki wótnowiś"; /* The body of the restore tabs pop-up alert. This alert shows when opening up Firefox after it crashed. */ "Alerts.RestoreTabs.Message.v109" = "Wódajśo. Wótnowśo rejtariki, aby tam pókšacował, źož sćo pśestał."; diff --git a/firefox-ios/Shared/Supporting Files/dsb.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/dsb.lproj/EditAddress.strings index 0421095ce1ed..81356f6d05cb 100644 --- a/firefox-ios/Shared/Supporting Files/dsb.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/dsb.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "Adresa njedajo se składowaś"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Adresa njedajo se wótwónoźeś"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Hyšći raz wopytaś"; diff --git a/firefox-ios/Shared/Supporting Files/dsb.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/dsb.lproj/EnhancedTrackingProtection.strings index f24d67961a7c..b58f023e08a2 100644 --- a/firefox-ios/Shared/Supporting Files/dsb.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/dsb.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Zwisk njejo wěsty"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Žedne pśeslědowaki namakane"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Sedła pśesegujucy slědujucy cookie: %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Pśeslědowaki socialnych medijow: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Pśeslědowaki blokěrowane: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,8 +46,7 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Sćo znjemóžnił šćity"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Sćo šćitany. Jolic něco namakajomy, dajomy wam to k wěsći."; /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ diff --git a/firefox-ios/Shared/Supporting Files/dsb.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/dsb.lproj/Microsurvey.strings index 8cf591593f0f..8354c6297db9 100644 --- a/firefox-ios/Shared/Supporting Files/dsb.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/dsb.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Njewubrany"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Napšašowanje"; + diff --git a/firefox-ios/Shared/Supporting Files/dsb.lproj/TabsTray.strings b/firefox-ios/Shared/Supporting Files/dsb.lproj/TabsTray.strings index 597858e8b78a..9a5ed98a62a5 100644 --- a/firefox-ios/Shared/Supporting Files/dsb.lproj/TabsTray.strings +++ b/firefox-ios/Shared/Supporting Files/dsb.lproj/TabsTray.strings @@ -11,7 +11,7 @@ "InactiveTabs.TabTray.CloseSwipeActionTitle.v115" = "Zacyniś"; /* Users can disable syncing tabs from other devices. In the Sync Tabs panel of the Tab Tray, we inform the user tab syncing can be switched back on to view those tabs. */ -"TabsTray.Sync.SyncTabsDisabled.v116" = "Synchronizěrowanje rejtarkow zašaltowaś, aby se lisćina rejtarkow z drugich rědow pokazała."; +"TabsTray.Sync.SyncTabsDisabled.v116" = "Synchronizěrowanje rejtarikow zašaltowaś, aby se lisćina rejtarikow z drugich rědow pokazała."; /* Button label to sync tabs in your account */ "TabsTray.SyncTabs.SyncTabsButton.Title.v119" = "Rejtariki synchronizěrowaś"; diff --git a/firefox-ios/Shared/Supporting Files/dsb.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/dsb.lproj/Toolbar.strings new file mode 100644 index 000000000000..08c904127532 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/dsb.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Nowy rejtarik"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Toś ten rejtarik zacyniś"; + diff --git a/firefox-ios/Shared/Supporting Files/el.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/el.lproj/EditAddress.strings index 2d7531c75c33..9759c32ba665 100644 --- a/firefox-ios/Shared/Supporting Files/el.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/el.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "Δεν ήταν δυνατή η αποθήκευση της διεύθυνσης"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Δεν ήταν δυνατή η αφαίρεση της διεύθυνσης"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Δοκιμή ξανά"; diff --git a/firefox-ios/Shared/Supporting Files/el.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/el.lproj/EnhancedTrackingProtection.strings index 4469538291b3..284b45140cf5 100644 --- a/firefox-ios/Shared/Supporting Files/el.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/el.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Μη ασφαλής σύνδεση"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Δεν βρέθηκαν ιχνηλάτες"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Cookie καταγραφής μεταξύ ιστοτόπων: %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Ιχνηλάτες μέσων κοινωνικής δικτύωσης: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Αποκλεισμένοι ιχνηλάτες: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Απενεργοποιήσατε την προστασία"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Προστατεύεστε. Αν εντοπίσουμε κάτι, θα σας ενημερώσουμε."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "Η σύνδεση δεν είναι ασφαλής."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Να προσέχετε σε αυτόν τον ιστότοπο"; diff --git a/firefox-ios/Shared/Supporting Files/el.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/el.lproj/Microsurvey.strings index b003273d4abd..afe1130c8f09 100644 --- a/firefox-ios/Shared/Supporting Files/el.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/el.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Μη επιλεγμένο"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Έρευνα"; + diff --git a/firefox-ios/Shared/Supporting Files/el.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/el.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..7d6b825dbb6e --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/el.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Ανανέωση"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "Προέκυψε σφάλμα SSL και δεν ήταν δυνατή η ασφαλής σύνδεση με τον διακομιστή."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "Να προσέχετε. Κάτι δεν πάει καλά."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "Δοκιμάστε να συνδεθείτε από διαφορετική συσκευή. Ελέγξτε το modem ή το router σας. Αποσυνδέστε και επανασυνδέστε το Wi-Fi."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "Φαίνεται ότι υπάρχει κάποιο πρόβλημα με τη σύνδεσή σας στο διαδίκτυο."; + diff --git a/firefox-ios/Shared/Supporting Files/el.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/el.lproj/Settings.strings index 20d4b6dba3d1..85c55b7140b8 100644 --- a/firefox-ios/Shared/Supporting Files/el.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/el.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Διαχείριση διευθύνσεων"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "Διεύθυνση για %@"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "ΑΠΟΘΗΚΕΥΜΕΝΕΣ ΔΙΕΥΘΥΝΣΕΙΣ"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Αποθήκευση διευθύνσεων σε %@"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "Αποθηκεύστε με ασφάλεια τις πληροφορίες σας για γρήγορη πρόσβαση αργότερα."; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "Περιλαμβάνει αριθμούς τηλεφώνου και διευθύνσεις email"; diff --git a/firefox-ios/Shared/Supporting Files/el.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/el.lproj/Toolbar.strings new file mode 100644 index 000000000000..2192fbfbfbba --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/el.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Νέα καρτέλα"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Κλείσιμο καρτέλας"; + diff --git a/firefox-ios/Shared/Supporting Files/en-CA.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/en-CA.lproj/EditAddress.strings index e931684b6cae..bc5d40c3374b 100644 --- a/firefox-ios/Shared/Supporting Files/en-CA.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/en-CA.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "Address Couldn’t Be Saved"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Address Couldn’t Be Removed"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Try again"; diff --git a/firefox-ios/Shared/Supporting Files/en-CA.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/en-CA.lproj/EnhancedTrackingProtection.strings index 327625e3ed14..927fcf187f5e 100644 --- a/firefox-ios/Shared/Supporting Files/en-CA.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/en-CA.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Connection not secure"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "No trackers found"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Cross-site tracking cookies: %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Social media trackers: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Trackers blocked: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,8 +46,7 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "You turned off protections"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "You’re protected. If we spot something, we’ll let you know."; /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ diff --git a/firefox-ios/Shared/Supporting Files/en-CA.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/en-CA.lproj/Microsurvey.strings index 9806c1dfb754..aa8a84e71e1f 100644 --- a/firefox-ios/Shared/Supporting Files/en-CA.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/en-CA.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Unselected"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Survey"; + diff --git a/firefox-ios/Shared/Supporting Files/en-CA.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/en-CA.lproj/Toolbar.strings new file mode 100644 index 000000000000..e3c35a24ad4b --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/en-CA.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "New Tab"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Close This Tab"; + diff --git a/firefox-ios/Shared/Supporting Files/en-GB.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/en-GB.lproj/EditAddress.strings index cd1ebd0e3e37..a3521a221373 100644 --- a/firefox-ios/Shared/Supporting Files/en-GB.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/en-GB.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "Address Couldn’t Be Saved"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Address Couldn’t Be Removed"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Try again"; diff --git a/firefox-ios/Shared/Supporting Files/en-GB.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/en-GB.lproj/EnhancedTrackingProtection.strings index 42cc67390eb1..ea0746ea56aa 100644 --- a/firefox-ios/Shared/Supporting Files/en-GB.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/en-GB.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Connection not secure"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "No trackers found"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Cross-site tracking cookies: %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Social media trackers: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Trackers blocked: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "You turned off protections"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "You’re protected. If we spot something, we’ll let you know."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "Your connection is not secure."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Be careful on this site"; diff --git a/firefox-ios/Shared/Supporting Files/en-GB.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/en-GB.lproj/Microsurvey.strings index 9806c1dfb754..aa8a84e71e1f 100644 --- a/firefox-ios/Shared/Supporting Files/en-GB.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/en-GB.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Unselected"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Survey"; + diff --git a/firefox-ios/Shared/Supporting Files/en-GB.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/en-GB.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..ae3df133d866 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/en-GB.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Reload"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "An SSL error has occurred and a secure connection to the server cannot be made."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "Be careful. Something doesn’t look right."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "Try connecting on a different device. Check your modem or router. Disconnect and reconnect to Wi-Fi."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "Looks like there’s a problem with your internet connection."; + diff --git a/firefox-ios/Shared/Supporting Files/en-GB.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/en-GB.lproj/Settings.strings index 6f4479457e46..cb1c111955d7 100644 --- a/firefox-ios/Shared/Supporting Files/en-GB.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/en-GB.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Manage addresses"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "Address for %@"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "SAVED ADDRESSES"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Save Addresses to %@"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "Securely save your information to get quick access to it later."; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "Includes phone numbers and email addresses"; diff --git a/firefox-ios/Shared/Supporting Files/en-GB.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/en-GB.lproj/Toolbar.strings new file mode 100644 index 000000000000..e3c35a24ad4b --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/en-GB.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "New Tab"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Close This Tab"; + diff --git a/firefox-ios/Shared/Supporting Files/en-US.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/en-US.lproj/EditAddress.strings index 1304bf15e803..02b1fa4defc1 100644 --- a/firefox-ios/Shared/Supporting Files/en-US.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/en-US.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "Address Couldn’t Be Saved"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Address Couldn’t Be Removed"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Try again"; diff --git a/firefox-ios/Shared/Supporting Files/en-US.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/en-US.lproj/EnhancedTrackingProtection.strings index 327625e3ed14..1d21d985912b 100644 --- a/firefox-ios/Shared/Supporting Files/en-US.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/en-US.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Connection not secure"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "No trackers found"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Cross-site tracking cookies: %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Social media trackers: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Trackers blocked: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "You turned off protections"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "You’re protected. If we spot something, we’ll let you know."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "Your connection is not secure."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Be careful on this site"; diff --git a/firefox-ios/Shared/Supporting Files/en-US.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/en-US.lproj/Microsurvey.strings index 9806c1dfb754..aa8a84e71e1f 100644 --- a/firefox-ios/Shared/Supporting Files/en-US.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/en-US.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Unselected"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Survey"; + diff --git a/firefox-ios/Shared/Supporting Files/en-US.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/en-US.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..ae3df133d866 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/en-US.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Reload"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "An SSL error has occurred and a secure connection to the server cannot be made."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "Be careful. Something doesn’t look right."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "Try connecting on a different device. Check your modem or router. Disconnect and reconnect to Wi-Fi."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "Looks like there’s a problem with your internet connection."; + diff --git a/firefox-ios/Shared/Supporting Files/en-US.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/en-US.lproj/Settings.strings index 893403cf54b1..9e6dd70facb6 100644 --- a/firefox-ios/Shared/Supporting Files/en-US.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/en-US.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Manage addresses"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "Address for %@"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "SAVED ADDRESSES"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Save Addresses to %@"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "Securely save your information to get quick access to it later."; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "Includes phone numbers and email addresses"; diff --git a/firefox-ios/Shared/Supporting Files/en-US.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/en-US.lproj/Toolbar.strings new file mode 100644 index 000000000000..e3c35a24ad4b --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/en-US.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "New Tab"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Close This Tab"; + diff --git a/firefox-ios/Shared/Supporting Files/eo.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/eo.lproj/EditAddress.strings new file mode 100644 index 000000000000..7e1ecbc2c07d --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/eo.lproj/EditAddress.strings @@ -0,0 +1,141 @@ +/* Title for the cancel button in the remove address alert. */ +"Addresses.EditAddress.Alert.CancelButton.v129" = "Nuligi"; + +/* Message explaining the consequences of removing an address from all synced devices. */ +"Addresses.EditAddress.Alert.Message.v129" = "La adreso estos forigita el ĉiuj viaj spegulitaj aparatoj."; + +/* Title for the remove button in the remove address alert. */ +"Addresses.EditAddress.Alert.RemoveButton.v129" = "Forigi"; + +/* Title for the alert indicating the action to remove an address. */ +"Addresses.EditAddress.Alert.Title.v129" = "Forigi adreson"; + +/* Title for the interface option where users can add a new address for autofill purposes. This facilitates quicker form completion by automatically filling in the user's address information. */ +"Addresses.EditAddress.AutofillAddAddressTitle.v129" = "Aldoni adreson"; + +/* Label for the area field, allowing users to specify a particular area within a city or region. This detail can improve the specificity and accuracy of autofilled addresses. */ +"Addresses.EditAddress.AutofillAddressArea.v129" = "Regiono"; + +/* Label for the field where users input the city part of their address. This information is crucial for mail delivery and service provision, ensuring accurate city identification in autofill settings. */ +"Addresses.EditAddress.AutofillAddressCity.v129" = "Urbo"; + +/* Label for the field where users can specify just the country, used in contexts where full address details are not required. Simplifies autofill when only country information is necessary. */ +"Addresses.EditAddress.AutofillAddressCountryOnly.v129" = "Lando"; + +/* Label for the country or region field in address forms, allowing users to specify their country or territorial region. This is fundamental for international mail and services, ensuring autofill accuracy across borders. */ +"Addresses.EditAddress.AutofillAddressCountryRegion.v129" = "Lando aŭ regiono"; + +/* Label for the county field, crucial for addressing in regions where county lines play a key role in postal services. Enhances autofill accuracy by including county information. */ +"Addresses.EditAddress.AutofillAddressCounty.v129" = "Municipo"; + +/* Label for the department field, used in countries like France and Colombia where departments are a key administrative division. Ensures correct departmental information is autofilled. */ +"Addresses.EditAddress.AutofillAddressDepartment.v129" = "Sekcio"; + +/* Label for the district field in the address form, allowing users to specify their district for more precise location identification. This aids in refining address details for accurate autofill. */ +"Addresses.EditAddress.AutofillAddressDistrict.v129" = "Distrikto"; + +/* Label for the Do/Si field, pertinent to addresses in South Korea. Do/Si refers to provincial level divisions, and specifying this enhances address accuracy in autofill settings. */ +"Addresses.EditAddress.AutofillAddressDoSi.v129" = "Do/Si"; + +/* Label for the Eircode field, specific to Ireland. It's a unique postal code system that helps in precise location identification, enhancing the effectiveness of autofill. */ +"Addresses.EditAddress.AutofillAddressEircode.v129" = "Koda poŝto (Eircode)"; + +/* Label for the email address field, where users input their email. Critical for digital communication and account verification, this ensures email addresses are autofilled accurately. */ +"Addresses.EditAddress.AutofillAddressEmail.v129" = "Retpoŝto"; + +/* Label for the emirate field, essential for addresses in the United Arab Emirates. Including emirate details ensures the autofill feature accurately represents user addresses. */ +"Addresses.EditAddress.AutofillAddressEmirate.v129" = "Emirlando"; + +/* Label for the field where users specify the name of an island, if applicable. Important for addresses in archipelagic regions, aiding in precise location identification during autofill. */ +"Addresses.EditAddress.AutofillAddressIsland.v129" = "Insulo"; + +/* Label for the field where the user inputs their full name as part of an address form. Essential for personalized form submissions and ensuring information accuracy in autofilled forms. */ +"Addresses.EditAddress.AutofillAddressName.v129" = "Nomo"; + +/* Label for the field where users can input the name of their neighborhood. This detail adds precision to addresses, especially in densely populated areas, improving the accuracy of autofill. */ +"Addresses.EditAddress.AutofillAddressNeighborhood.v129" = "Kvartalo"; + +/* Label for the oblast field, relevant for addresses in countries like Russia and Ukraine. Oblasts are a significant administrative division, and their specification aids in autofill accuracy. */ +"Addresses.EditAddress.AutofillAddressOblast.v129" = "Sendependa provinco"; + +/* Label for the input field designated for the organization's name related to the address. Helps in distinguishing addresses used for business or personal purposes in autofill settings. */ +"Addresses.EditAddress.AutofillAddressOrganization.v129" = "Organizo"; + +/* Label for the parish field, significant in places where parishes are used for local administration and addressing. Ensures users can specify parish details for better autofill accuracy. */ +"Addresses.EditAddress.AutofillAddressParish.v129" = "Paroĥo"; + +/* Label for the PIN (Postal Index Number) field, used in India. It's a code representing a specific area, crucial for accurate mail delivery and autofill functionality. */ +"Addresses.EditAddress.AutofillAddressPin.v129" = "Pin"; + +/* Label for the postal code field, universally used in address forms to specify the area code for mail sorting. Essential for autofill to ensure mail and services are accurately routed. */ +"Addresses.EditAddress.AutofillAddressPostalCode.v129" = "Poŝtkodo"; + +/* Label for the post town field, used primarily in the UK and some other regions for mail sorting. Essential for users in applicable areas to specify for correct mail delivery through autofill. */ +"Addresses.EditAddress.AutofillAddressPostTown.v129" = "Poŝta urbo"; + +/* Label for the prefecture field, essential for addresses in countries like Japan where prefectures are a major administrative division. Aids in precise location specification for autofill. */ +"Addresses.EditAddress.AutofillAddressPrefecture.v129" = "Prefektejo"; + +/* Label for the province field, required in countries where provinces are a primary administrative division. Helps in pinpointing the user's location more accurately for autofill purposes. */ +"Addresses.EditAddress.AutofillAddressProvince.v129" = "Provinco"; + +/* Label for the state field, a necessary component of addresses in many countries, especially the USA. It ensures that state-specific details are correctly filled in forms using autofill. */ +"Addresses.EditAddress.AutofillAddressState.v129" = "Ŝtato"; + +/* Label for the suburb field, enabling users to add suburb details to their address. This is important for accurate delivery and services in suburban areas, enhancing autofill functionality. */ +"Addresses.EditAddress.AutofillAddressSuburb.v129" = "Suburbo"; + +/* Label for the telephone number field, allowing users to input their contact number. This is essential for communication and service provision, ensuring contact details are autofilled correctly. */ +"Addresses.EditAddress.AutofillAddressTel.v129" = "Telefono"; + +/* Label for the input field for the townland, a specific type of land division used in rural areas. Enhances address detail for users in regions where townlands are a common addressing component. */ +"Addresses.EditAddress.AutofillAddressTownland.v129" = "Kampara regiono"; + +/* Label for the field to input the name of a village or township. This is crucial for addresses in rural areas, ensuring the autofill feature accurately captures all necessary geographical details. */ +"Addresses.EditAddress.AutofillAddressVillageTownship.v129" = "Vilaĝo aŭ municipo"; + +/* Label for the ZIP code field, primarily used in the United States for mail sorting. Key for autofill to accurately complete addresses for shipping, billing, and service provision. */ +"Addresses.EditAddress.AutofillAddressZip.v129" = "Kodo ZIP"; + +/* Label for the button to cancel the current autofill operation or exit the form without saving changes. Provides users with an option to back out of a process without making any modifications. */ +"Addresses.EditAddress.AutofillCancelButton.v129" = "Nuligi"; + +/* Title for the option allowing users to edit an existing saved address. This is used within the settings for autofill, enabling users to update their address details for accurate form autofilling. */ +"Addresses.EditAddress.AutofillEditAddressTitle.v129" = "Modifi adreson"; + +/* Title for the input field where users can enter their street address. This is used within the settings for autofill, allowing users to provide their street address for accurate form autofilling. */ +"Addresses.EditAddress.AutofillEditStreetAddressTitle.v129" = "Strata adreso"; + +/* Label for the button to save the current address details entered or edited by the user. This action confirms the user's changes and updates their autofill settings accordingly. */ +"Addresses.EditAddress.AutofillSaveButton.v129" = "Konservi"; + +/* Title for the option allowing users to view an existing saved address. This is used within the settings for autofill, enabling users to see their address details for accurate form autofilling. */ +"Addresses.EditAddress.AutofillViewAddressTitle.v129" = "Vidi adreson"; + +/* Button label for closing the view where user can view their address info. */ +"Addresses.EditAddress.CloseNavBarButtonLabel.v129" = "Fermi"; + +/* Button label for editing the address details shown in the form. */ +"Addresses.EditAddress.EditNavBarButtonLabel.v129" = "Modifi"; + +/* Title for button that offers the user the option to remove an address. */ +"Addresses.EditAddress.RemoveAddressButtonTitle.v129" = "Forigi adreson"; + +/* Toast message confirming that an address has been successfully removed. */ +"Addresses.Toast.AddressRemovedConfirmation.v129" = "Adreso forigita"; + +/* Toast message confirming that an address has been successfully saved. */ +"Addresses.Toast.AddressSavedConfirmation.v129" = "Adreso konservita"; + +/* Toast message indicating an error occurred while trying to save an address. */ +"Addresses.Toast.AddressSaveError.v129" = "Ne eblis konservi la adreson"; + +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Ne eblis forigi la adreson"; + +/* Suggestion to try again after an error occurred while trying to save an address. */ +"Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Klopodi denove"; + +/* Toast message confirming that an address has been successfully updated. */ +"Addresses.Toast.AddressUpdatedConfirmation.v129" = "Informo pri adreso ĝisdatigita"; + diff --git a/firefox-ios/Shared/Supporting Files/eo.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/eo.lproj/EnhancedTrackingProtection.strings index 682177812f82..3f6c80d8b2c3 100644 --- a/firefox-ios/Shared/Supporting Files/eo.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/eo.lproj/EnhancedTrackingProtection.strings @@ -22,8 +22,19 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Nesekura konekto"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Neniu spurilo trovita"; + +/* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Interretejaj spurilaj kuketoj: %@"; + +/* Text to let users know how many fingerprinters were blocked on the current website. The placeholder will show the number of fingerprinters detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.Fingerprinter.v129" = "Identigiloj de ciferecaj spuroj: %@"; + +/* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Sociretaj spuriloj: %@"; + +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Blokitaj spuriloj: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -35,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Vi malŝaltis protektojn"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Vi estas protektita. Se ni malkovras ion, ni sciigos vin."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "Via konekto ne estas sekura."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Estu singarda en tiu ĉi retejo"; @@ -54,6 +67,9 @@ /* A switch to disable enhanced tracking protection inside the menu. */ "Menu.EnhancedTrackingProtection.SwitchOff.Text.v128" = "Protektoj estas MALAKTIVAJ. Ni sugestas reaktivigi ilin."; +/* A switch to disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.SwitchOff.Text.v129" = "Protektoj estas MALAKTIVAJ. Ni sugestas reaktivigi ilin."; + /* A switch to disable enhanced tracking protection inside the menu. */ "Menu.EnhancedTrackingProtection.SwitchOn.Text.v128" = "Se io misfunkcias en tiu ĉi retejo, provu malŝalti ĝin."; diff --git a/firefox-ios/Shared/Supporting Files/eo.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/eo.lproj/Microsurvey.strings index 9f79f1280ae6..f53cc0c68444 100644 --- a/firefox-ios/Shared/Supporting Files/eo.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/eo.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Ne elektita"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Enekto"; + diff --git a/firefox-ios/Shared/Supporting Files/eo.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/eo.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..78a305e2f9e3 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/eo.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Reŝargi"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "Okazis eraro SSL kaj ne eblas sekure konektiĝi al la servilo."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "Estu singarda. Io ne ĝustas."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "Provu konektiĝi el alia aparato. Kontrolu vian modemon aŭ retalirilon. Malkonektiĝu kaj rekonektiĝu al sendrata reto (Wi-Fi)."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "Ŝajne estas problemo kun via retaliro."; + diff --git a/firefox-ios/Shared/Supporting Files/eo.lproj/PasswordAutofill.strings b/firefox-ios/Shared/Supporting Files/eo.lproj/PasswordAutofill.strings index 1e9e1034f5ab..b563511a9ba4 100644 --- a/firefox-ios/Shared/Supporting Files/eo.lproj/PasswordAutofill.strings +++ b/firefox-ios/Shared/Supporting Files/eo.lproj/PasswordAutofill.strings @@ -1,3 +1,6 @@ +/* This label is used in a cell found in the list of autofill login options in place of an actual username to denote that no username was saved for this login */ +"PasswordAutofill.LoginListCellNoUsername.v129" = "(sen nomo de uzanto)"; + /* This label is used for a button in the password list screen allowing users to manage their saved passwords. It's meant to direct users to where they can add, remove, or edit their saved passwords. */ "PasswordAutofill.ManagePasswordsButton.v124" = "Administri pasvortojn"; diff --git a/firefox-ios/Shared/Supporting Files/eo.lproj/ScanQRCode.strings b/firefox-ios/Shared/Supporting Files/eo.lproj/ScanQRCode.strings new file mode 100644 index 000000000000..9e3e3703ddec --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/eo.lproj/ScanQRCode.strings @@ -0,0 +1,9 @@ +/* Allow button to open URL from scanned QR Code */ +"ScanQRCode.ConfirmOpenURL.AllowButton.v129" = "Permesi"; + +/* Deny button to cancel opening URL from scanned QR Code */ +"ScanQRCode.ConfirmOpenURL.DenyButton.v129" = "Rifuzi"; + +/* Text of the prompt to ask user permission to open a URL from a scanned QR code. Placeholder is app name. */ +"ScanQRCode.ConfirmOpenURL.Message.v129" = "Ĉu permesi la malfermon de %@?"; + diff --git a/firefox-ios/Shared/Supporting Files/eo.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/eo.lproj/Settings.strings index 3809e47f6a73..2b08faf8dd7d 100644 --- a/firefox-ios/Shared/Supporting Files/eo.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/eo.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Administri adresojn"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "Adreso por %@"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "KONSERVITAJ ADRESOJN"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Konservi adresojn en %@"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "Sekure konservi viajn informojn por rapidi aliri ilin poste."; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "Telefonnumeroj kaj retpoŝtaj adresoj inkluzivitaj"; diff --git a/firefox-ios/Shared/Supporting Files/eo.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/eo.lproj/Toolbar.strings new file mode 100644 index 000000000000..a7ae34040b66 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/eo.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Nova langeto"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Fermi tiun ĉi langeton"; + diff --git a/firefox-ios/Shared/Supporting Files/es-AR.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/es-AR.lproj/EditAddress.strings index ee112aa7eb6e..cc42ad8969dc 100644 --- a/firefox-ios/Shared/Supporting Files/es-AR.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/es-AR.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "No se pudo guardar la dirección"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "No se pudo eliminar la dirección"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Probá de nuevo"; diff --git a/firefox-ios/Shared/Supporting Files/es-AR.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/es-AR.lproj/EnhancedTrackingProtection.strings index e85ec2637be5..04510ffdccbf 100644 --- a/firefox-ios/Shared/Supporting Files/es-AR.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/es-AR.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Conexión insegura"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "No se encontraron rastreadores"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Cookies de rastreo de sitios cruzados: %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Rastreadores de redes sociales: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Rastreadores bloqueados: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Desactivaste las protecciones"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Estás protegido. Si detectamos algo, te lo haremos saber."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "La conexión no es segura."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Tené cuidado en este sitio"; diff --git a/firefox-ios/Shared/Supporting Files/es-AR.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/es-AR.lproj/Microsurvey.strings index 12afef94e2ee..327fd9ee17ca 100644 --- a/firefox-ios/Shared/Supporting Files/es-AR.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/es-AR.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "No seleccionada"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Encuesta"; + diff --git a/firefox-ios/Shared/Supporting Files/es-AR.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/es-AR.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..f0956f82c0d2 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/es-AR.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Recargar"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "Se produjo un error de SSL y no se puede establecer una conexión segura con el servidor."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "Cuidado. Algo no parece estar bien."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "Probá conectarte a un dispositivo diferente. Verificá tu módem o router. Desconectá y volvé a conectarte al wifi."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "Parece que hay un problema con la conexión a Internet."; + diff --git a/firefox-ios/Shared/Supporting Files/es-AR.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/es-AR.lproj/Settings.strings index 2bf2c6a33a63..9ce09a106ffd 100644 --- a/firefox-ios/Shared/Supporting Files/es-AR.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/es-AR.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Administrar direcciones"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "Dirección para %@"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "DIRECCIONES GUARDADAS"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Guardar direcciones en %@"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "Guardá tu información de forma segura para acceder a ella más tarde."; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "Incluye números de teléfono y direcciones de correo electrónico"; diff --git a/firefox-ios/Shared/Supporting Files/es-AR.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/es-AR.lproj/Toolbar.strings new file mode 100644 index 000000000000..c0cd65c91f77 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/es-AR.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Nueva pestaña"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Cerrar esta pestaña"; + diff --git a/firefox-ios/Shared/Supporting Files/es-CL.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/es-CL.lproj/EditAddress.strings index a96c54388ebd..75e8944a71a0 100644 --- a/firefox-ios/Shared/Supporting Files/es-CL.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/es-CL.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "No se pudo guardar la dirección"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "No se pudo eliminar la dirección"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Volver a intentarlo"; diff --git a/firefox-ios/Shared/Supporting Files/es-CL.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/es-CL.lproj/EnhancedTrackingProtection.strings index 3753514e7f9e..51bbe314225f 100644 --- a/firefox-ios/Shared/Supporting Files/es-CL.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/es-CL.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Conexión no segura"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "No se encontraron rastreadores"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Cookies de rastreo de sitios cruzados: %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Rastreadores de redes sociales: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Rastreadores bloqueados: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Desactivaste las protecciones"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Estás bajo protección. Si detectamos algo, te lo haremos saber."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "Tu conexión no es segura."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Ten cuidado en este sitio"; diff --git a/firefox-ios/Shared/Supporting Files/es-CL.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/es-CL.lproj/Microsurvey.strings index 6109a1b108d4..dafd983ad832 100644 --- a/firefox-ios/Shared/Supporting Files/es-CL.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/es-CL.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Deseleccionado"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Encuesta"; + diff --git a/firefox-ios/Shared/Supporting Files/es-CL.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/es-CL.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..1844a8e51246 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/es-CL.lproj/NativeErrorPage.strings @@ -0,0 +1,9 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Recargar"; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "Ten cuidado. Algo no parece estar bien."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "Parece que hay un problema con tu conexión a Internet."; + diff --git a/firefox-ios/Shared/Supporting Files/es-CL.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/es-CL.lproj/Settings.strings index 7b23f4a84661..a129aedc8e73 100644 --- a/firefox-ios/Shared/Supporting Files/es-CL.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/es-CL.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Administrar direcciones"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "Direcciones para %@"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "DIRECCIONES GUARDADAS"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Guardar direcciones en %@"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "Guarda tu información de forma segura para acceder a ella rápidamente más tarde."; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "Incluye números de teléfono y direcciones de correo electrónico"; diff --git a/firefox-ios/Shared/Supporting Files/es-CL.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/es-CL.lproj/Toolbar.strings new file mode 100644 index 000000000000..c0cd65c91f77 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/es-CL.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Nueva pestaña"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Cerrar esta pestaña"; + diff --git a/firefox-ios/Shared/Supporting Files/es-MX.lproj/ActivityStream.strings b/firefox-ios/Shared/Supporting Files/es-MX.lproj/ActivityStream.strings new file mode 100644 index 000000000000..5ad67a81186b --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/es-MX.lproj/ActivityStream.strings @@ -0,0 +1,6 @@ +/* String used in the section title of the Bookmarks section on Home Screen. */ +"ActivityStream.Bookmarks.Title.v128" = "Marcadores"; + +/* Show all button text for Bookmarks items on the home page, which opens the Bookmarks panel when tapped. */ +"Bookmarks.Actions.More.v128" = "Mostrar todo"; + diff --git a/firefox-ios/Shared/Supporting Files/es-MX.lproj/Alerts.strings b/firefox-ios/Shared/Supporting Files/es-MX.lproj/Alerts.strings index 13d2d34182ae..f785b99e3198 100644 --- a/firefox-ios/Shared/Supporting Files/es-MX.lproj/Alerts.strings +++ b/firefox-ios/Shared/Supporting Files/es-MX.lproj/Alerts.strings @@ -1,3 +1,6 @@ +/* When tapping the fire icon in private mode, an alert comes up asking to confirm if you want to delete all browsing data and end your private session. This is the body text for the alert. */ +"Alerts.FeltDeletion.Body.v122" = "Cierra todas las pestañas privadas y elimina el historial, las cookies y todos los demás datos del sitio."; + /* When tapping the fire icon in private mode, an alert comes up asking to confirm if you want to delete all browsing data and end your private session. This is the cancel action for the alert, cancelling ending your session. */ "Alerts.FeltDeletion.Button.Cancel.v122" = "Cancelar"; diff --git a/firefox-ios/Shared/Supporting Files/es-MX.lproj/CustomizeFirefoxHome.strings b/firefox-ios/Shared/Supporting Files/es-MX.lproj/CustomizeFirefoxHome.strings index d76f70680ef0..ec19f4221d58 100644 --- a/firefox-ios/Shared/Supporting Files/es-MX.lproj/CustomizeFirefoxHome.strings +++ b/firefox-ios/Shared/Supporting Files/es-MX.lproj/CustomizeFirefoxHome.strings @@ -1,3 +1,6 @@ +/* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle Bookmarks section on the Firefox homepage on or off */ +"Settings.Home.Option.Bookmarks.v128" = "Marcadores"; + /* In the settings menu, in the Firefox homepage customization section, this is the subtitle for the option that allows users to turn the Pocket Recommendations section on the Firefox homepage on or off. The placeholder is the pocket app name. */ "Settings.Home.Option.ThoughtProvokingStories.subtitle.v116" = "Artículos desarrollados por %@"; diff --git a/firefox-ios/Shared/Supporting Files/es-MX.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/es-MX.lproj/EditAddress.strings new file mode 100644 index 000000000000..fd22c013175e --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/es-MX.lproj/EditAddress.strings @@ -0,0 +1,36 @@ +/* Title for the cancel button in the remove address alert. */ +"Addresses.EditAddress.Alert.CancelButton.v129" = "Cancelar"; + +/* Title for the interface option where users can add a new address for autofill purposes. This facilitates quicker form completion by automatically filling in the user's address information. */ +"Addresses.EditAddress.AutofillAddAddressTitle.v129" = "Agregar dirección"; + +/* Label for the field where users input the city part of their address. This information is crucial for mail delivery and service provision, ensuring accurate city identification in autofill settings. */ +"Addresses.EditAddress.AutofillAddressCity.v129" = "Ciudad"; + +/* Label for the field where users can specify just the country, used in contexts where full address details are not required. Simplifies autofill when only country information is necessary. */ +"Addresses.EditAddress.AutofillAddressCountryOnly.v129" = "País"; + +/* Label for the country or region field in address forms, allowing users to specify their country or territorial region. This is fundamental for international mail and services, ensuring autofill accuracy across borders. */ +"Addresses.EditAddress.AutofillAddressCountryRegion.v129" = "País o región"; + +/* Label for the county field, crucial for addressing in regions where county lines play a key role in postal services. Enhances autofill accuracy by including county information. */ +"Addresses.EditAddress.AutofillAddressCounty.v129" = "Municipio"; + +/* Label for the email address field, where users input their email. Critical for digital communication and account verification, this ensures email addresses are autofilled accurately. */ +"Addresses.EditAddress.AutofillAddressEmail.v129" = "Correo electrónico"; + +/* Label for the postal code field, universally used in address forms to specify the area code for mail sorting. Essential for autofill to ensure mail and services are accurately routed. */ +"Addresses.EditAddress.AutofillAddressPostalCode.v129" = "Código postal"; + +/* Label for the prefecture field, essential for addresses in countries like Japan where prefectures are a major administrative division. Aids in precise location specification for autofill. */ +"Addresses.EditAddress.AutofillAddressPrefecture.v129" = "Prefectura"; + +/* Label for the province field, required in countries where provinces are a primary administrative division. Helps in pinpointing the user's location more accurately for autofill purposes. */ +"Addresses.EditAddress.AutofillAddressProvince.v129" = "Provincia"; + +/* Label for the state field, a necessary component of addresses in many countries, especially the USA. It ensures that state-specific details are correctly filled in forms using autofill. */ +"Addresses.EditAddress.AutofillAddressState.v129" = "Estado"; + +/* Label for the button to cancel the current autofill operation or exit the form without saving changes. Provides users with an option to back out of a process without making any modifications. */ +"Addresses.EditAddress.AutofillCancelButton.v129" = "Cancelar"; + diff --git a/firefox-ios/Shared/Supporting Files/es-MX.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/es-MX.lproj/EnhancedTrackingProtection.strings new file mode 100644 index 000000000000..8ab36f8966a0 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/es-MX.lproj/EnhancedTrackingProtection.strings @@ -0,0 +1,3 @@ +/* The title for the privacy settings button inside the enhanced tracking protection screen. */ +"Menu.EnhancedTrackingProtection.PrivacySettings.Title.v128" = "Ajustes de privacidad"; + diff --git a/firefox-ios/Shared/Supporting Files/es-MX.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/es-MX.lproj/Microsurvey.strings new file mode 100644 index 000000000000..3b50a27cac72 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/es-MX.lproj/Microsurvey.strings @@ -0,0 +1,15 @@ +/* On top of the bottom toolbar, there can be a microsurvey prompt, this is the title for the button that appears on the prompt that allows the user to tap on and navigates them to the microsurvey to respond to. */ +"Microsurvey.Prompt.Button.v127" = "Continuar"; + +/* On top of the bottom toolbar, there can be a microsurvey prompt, this is the accessibility label for the close button that appears on the prompt that allows the user to dismiss the microsurvey prompt. */ +"Microsurvey.Prompt.Close.Button.AccessibilityLabel.v127" = "Cerrar"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this the title of button on the survey that a user can tap on to submit their responses. */ +"Microsurvey.Survey.Button.v127" = "Enviar"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label for close button that dismisses the sheet. */ +"Microsurvey.Survey.Close.Button.AccessibilityLabel.v127" = "Cerrar"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was selected. */ +"Microsurvey.Survey.RadioButton.Selected.AccessibilityLabel.v129" = "Seleccionado"; + diff --git a/firefox-ios/Shared/Supporting Files/es-MX.lproj/Onboarding.strings b/firefox-ios/Shared/Supporting Files/es-MX.lproj/Onboarding.strings index 996ab7eff2ed..6bc0de69383c 100644 --- a/firefox-ios/Shared/Supporting Files/es-MX.lproj/Onboarding.strings +++ b/firefox-ios/Shared/Supporting Files/es-MX.lproj/Onboarding.strings @@ -16,6 +16,15 @@ /* String used to describe the option to continue to the next onboarding card in Firefox Onboarding screens. Placeholder is for the app name. */ "Onboarding.Customization.Intro.Continue.Action.v123" = "Personalizar %@"; +/* String used to describe the option to skip the customization cards in Firefox Onboarding screens and start browsing. */ +"Onboarding.Customization.Intro.Skip.Action.v123" = "Empieza a navegar"; + +/* String used to describe the title of the customization onboarding page in our Onboarding screens. Placeholder is for the app name. */ +"Onboarding.Customization.Intro.Title.v123" = "%@ te da el control"; + +/* String used to describe the option to save the user setting and continue to the next onboarding in Firefox Onboarding screens. */ +"Onboarding.Customization.Theme.Continue.Action.v123" = "Guardar y Continuar"; + /* On the theme customization onboarding card, the string used to describe the option to set the theme to dark theme from the available choices. */ "Onboarding.Customization.Theme.Dark.Action.v123" = "Oscuro"; diff --git a/firefox-ios/Shared/Supporting Files/es-MX.lproj/PasswordAutofill.strings b/firefox-ios/Shared/Supporting Files/es-MX.lproj/PasswordAutofill.strings index 6ee8a5e356e4..f2287ef67da0 100644 --- a/firefox-ios/Shared/Supporting Files/es-MX.lproj/PasswordAutofill.strings +++ b/firefox-ios/Shared/Supporting Files/es-MX.lproj/PasswordAutofill.strings @@ -4,3 +4,6 @@ /* This label is used in the password list screen header as a question, prompting the user if they want to use a saved password for logging in. */ "PasswordAutofill.UseSavedPasswordFromHeader.v124" = "¿Usar contraseña guardada?"; +/* Displayed inside the keyboard hint when a user is entering their login credentials and has at least one saved password. Indicates that there are stored passwords available for use in filling out the login form. */ +"PasswordAutofill.UseSavedPasswordFromKeyboard.v124" = "Usar contraseña guardada"; + diff --git a/firefox-ios/Shared/Supporting Files/es-MX.lproj/RememberCard.strings b/firefox-ios/Shared/Supporting Files/es-MX.lproj/RememberCard.strings index 574f4a208148..0732cf6e799c 100644 --- a/firefox-ios/Shared/Supporting Files/es-MX.lproj/RememberCard.strings +++ b/firefox-ios/Shared/Supporting Files/es-MX.lproj/RememberCard.strings @@ -4,3 +4,9 @@ /* This value is used as the title for the remember credit card page */ "CreditCard.RememberCard.MainTitle.v122" = "¿Guardar esta tarjeta de forma segura?"; +/* This value is used as the title for the Not Now button in the remember credit card page */ +"CreditCard.RememberCard.SecondaryButtonTitle.v115" = "Ahora no"; + +/* This value is used as the toast message for the saving success alert in the remember credit card page */ +"CreditCard.RememberCard.SecondaryButtonTitle.v116" = "Nueva tarjeta guardada"; + diff --git a/firefox-ios/Shared/Supporting Files/es-MX.lproj/ScanQRCode.strings b/firefox-ios/Shared/Supporting Files/es-MX.lproj/ScanQRCode.strings new file mode 100644 index 000000000000..30a711e3be60 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/es-MX.lproj/ScanQRCode.strings @@ -0,0 +1,3 @@ +/* Allow button to open URL from scanned QR Code */ +"ScanQRCode.ConfirmOpenURL.AllowButton.v129" = "Permitir"; + diff --git a/firefox-ios/Shared/Supporting Files/es-MX.lproj/SelectCreditCard.strings b/firefox-ios/Shared/Supporting Files/es-MX.lproj/SelectCreditCard.strings new file mode 100644 index 000000000000..bc69098b0ea7 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/es-MX.lproj/SelectCreditCard.strings @@ -0,0 +1,3 @@ +/* This value is used as the title for the select a credit card from list of available cards. */ +"CreditCard.SelectCreditCard.MainTitle.v122" = "Usar tarjeta guardada"; + diff --git a/firefox-ios/Shared/Supporting Files/es-MX.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/es-MX.lproj/Settings.strings index dbf2d3095aca..dda9228169d0 100644 --- a/firefox-ios/Shared/Supporting Files/es-MX.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/es-MX.lproj/Settings.strings @@ -1,15 +1,48 @@ +/* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ +"Addresses.Settings.SavedAddressesSectionTitle.v124" = "DIRECCIONES GUARDADAS"; + +/* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ +"Addresses.Settings.Switch.Description.v124" = "Incluye números de teléfono y direcciones de correo electrónico"; + +/* Title label for user to use the toggle settings to allow saving and autofilling of addresses for webpages. */ +"Addresses.Settings.Switch.Title.v124" = "Guardar y completar direcciones"; + +/* Displayed inside the keyboard hint when a user is entering their address and has at least one saved address. Indicates that there are stored addresses available for use in filling out a form. */ +"Addresses.Settings.UseSavedAddressFromKeyboard.v124" = "Usar dirección guardada"; + +/* Accessibility label for the add button in autofill settings screen. Pressing this button presents a modal that allows users to add a card by entering the credit card information. */ +"CreditCard.Settings.AddCard.AccessibilityLabel.v121" = "Agregar tarjeta"; + /* Description label for when there are no credit cards shown in credit card list in autofill settings screen. */ "CreditCard.Settings.EmptyListDescription.v112" = "Guarda la información de tu tarjeta de forma segura para pagar más rápido la próxima vez."; +/* When a user is in the process or has finished making a purchase with a card not saved in Firefox's list of stored cards, we ask the user if they would like to save this card for future purchases. This string indicates to users that they can deny Firefox from remembering the card that is being used. */ +"CreditCard.Settings.NotNow.v122" = "Ahora no"; + +/* When a user is in the process or has finished making a purchase with a card not saved in Firefox's list of stored cards, we ask the user if they would like to save this card for future purchases. This string is a title string of the overall message that asks the user if they would like Firefox to remember the card that is being used. */ +"CreditCard.Settings.RememberThisCard.v122" = "¿Guardar esta tarjeta de forma segura?"; + +/* When a user is in the process or has finished making a purchase with a remembered card, and if the credit card information doesn't match the contents of the stored information of that card, we show this string. We ask this user if they would like Firefox update the staled information of that credit card. */ +"CreditCard.Settings.UpdateThisCard.v122" = "¿Actualizar tarjeta?"; + +/* When a user is in the process or has finished making a purchase with a card not saved in Firefox's list of stored cards, we ask the user if they would like to save this card for future purchases. This string asks users to confirm if they would like Firefox to remember the card that is being used. */ +"CreditCard.Settings.Yes.v122" = "Actualizar"; + /* When a user is in the process of making a purchase and has at least one saved credit card, a view above the keyboard shows actions a user can take. When tapping this label, the keyboard will dismiss from view. */ "CreditCards.Settings.Done.v114" = "Hecho"; /* When a user is in the process or has finished making a purchase, and has at least one card saved, we show this tappable string. This indicates to users that they can navigate to their list of stored credit cards in the app's credit card list screen. */ "CreditCards.Settings.ManageCards.v112" = "Administrar tarjetas"; +/* When a user is in the process of making a purchase, and has at least one saved card, we show this label used as a title. This indicates to the user that there are stored cards available for use on this pending purchase. */ +"CreditCards.Settings.UseASavedCard.v122" = "Usar tarjeta guardada"; + /* When a user is in the process of making a purchase, and has at least one saved card, we show this label inside the keyboard hint. This indicates to the user that there are stored cards available for use on this pending purchase. */ "CreditCards.Settings.UseSavedCardFromKeyboard.v112" = "Usar tarjeta guardada"; +/* Settings section title for the old Firefox account */ +"FxA.FirefoxAccount.v119" = "Cuenta"; + /* Label used as an item in Settings screen. When touched, it will take user to address autofill settings page to that will allow user to add or modify saved addresses to allow for autofill in a webpage. */ "Settings.AddressAutofill.Title.v126" = "Direcciones"; diff --git a/firefox-ios/Shared/Supporting Files/es-MX.lproj/Shopping.strings b/firefox-ios/Shared/Supporting Files/es-MX.lproj/Shopping.strings index 0f949afbd62b..353c2ae4fbfd 100644 --- a/firefox-ios/Shared/Supporting Files/es-MX.lproj/Shopping.strings +++ b/firefox-ios/Shared/Supporting Files/es-MX.lproj/Shopping.strings @@ -61,3 +61,18 @@ /* Title for info card when Fakespot cannot analyze reviews for a certain product type */ "Shopping.InfoCard.FakespotDoesNotAnalyzeReviews.Title.v120" = "No se pueden verificar estas reseñas"; +/* Primary action title for info card when the product needs analysis */ +"Shopping.InfoCard.NeedsAnalysis.PrimaryAction.v120" = "Comprobar ahora"; + +/* Text for the secondary button of the Shopping Experience Opt In onboarding Card (Fakespot) */ +"Shopping.OptInCard.SecondaryButton.Title.v120" = "Ahora no"; + +/* Show Firefox Browser Terms of Use page from the Privacy section in the Shopping Experience Opt In onboarding Card (Fakespot). See https://www.mozilla.org/privacy/firefox/ */ +"Shopping.OptInCard.TermsOfUse.Button.Title.v120" = "Términos de uso"; + +/* Show Fakespot Terms of Use page in the Shopping Experience Opt In onboarding Card (Fakespot). The parameter will be replace by the Fakespot name. */ +"Shopping.OptInCard.TermsOfUse.Button.Title.v123" = "Términos de uso de %@"; + +/* Accessibility label for the Grade labels used in 'How we determine review quality' card and 'How reliable are these reviews' card displayed in the shopping review quality bottom sheet. The placeholder will be replaced by a grade letter (e.g. A). The grading system contains letters from A-F. */ +"Shopping.ReliabilityScore.Grade.A11y.Label.v120" = "Calificación %@"; + diff --git a/firefox-ios/Shared/Supporting Files/es-MX.lproj/TabLocation.strings b/firefox-ios/Shared/Supporting Files/es-MX.lproj/TabLocation.strings new file mode 100644 index 000000000000..95b4ca951d46 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/es-MX.lproj/TabLocation.strings @@ -0,0 +1,15 @@ +/* Accessibility label for the security icon in url bar */ +"TabLocation.ETP.On.NotSecure.A11y.Label.v119" = "Conexión no segura"; + +/* Accessibility label for the security icon in url bar */ +"TabLocation.ETP.On.Secure.A11y.Label.v119" = "Conexión segura"; + +/* Accessibility label for the share button in url bar */ +"TabLocation.Share.A11y.Label.v119" = "Compartir esta página"; + +/* Large content title for the share button. This title is displayed when using accessible font sizes is enabled */ +"TabLocation.ShareButton.AccessibilityLabel.v122" = "Compartir"; + +/* Large content title for the button shown in editing mode to remove this site from the top sites panel. */ +"TopSites.RemoveButton.LargeContentTitle.v122" = "Eliminar página"; + diff --git a/firefox-ios/Shared/Supporting Files/es-MX.lproj/TabsTray.strings b/firefox-ios/Shared/Supporting Files/es-MX.lproj/TabsTray.strings index 7b4c0e2c280c..0c9e8adfe27e 100644 --- a/firefox-ios/Shared/Supporting Files/es-MX.lproj/TabsTray.strings +++ b/firefox-ios/Shared/Supporting Files/es-MX.lproj/TabsTray.strings @@ -7,3 +7,9 @@ /* When the user closes tabs in the tab tray, a popup will appear informing them how many tabs were closed. This is the text for the popup. The placeholder is the number of tabs */ "CloseTabsToast.Title.v113" = "Pestañas cerradas: %d"; +/* This is the swipe action title for closing an inactive tab by swiping, located in the Inactive Tabs section of the Tabs Tray */ +"InactiveTabs.TabTray.CloseSwipeActionTitle.v115" = "Cerrar"; + +/* Button label to sync tabs in your account */ +"TabsTray.SyncTabs.SyncTabsButton.Title.v119" = "Sincronizar pestañas"; + diff --git a/firefox-ios/Shared/Supporting Files/es-MX.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/es-MX.lproj/Toolbar.strings new file mode 100644 index 000000000000..f5b1dc1cc2e1 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/es-MX.lproj/Toolbar.strings @@ -0,0 +1,3 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Nueva pestaña"; + diff --git a/firefox-ios/Shared/Supporting Files/es-MX.lproj/UpdateCard.strings b/firefox-ios/Shared/Supporting Files/es-MX.lproj/UpdateCard.strings new file mode 100644 index 000000000000..1a617b11edff --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/es-MX.lproj/UpdateCard.strings @@ -0,0 +1,6 @@ +/* This value is used as the title for the Not Now button in the update credit card page */ +"CreditCard.UpdateCard.NotNowButtonTitle.v115" = "Ahora no"; + +/* This value is used as the title for the button in the update credit card page. It indicates the action to update the details f9 the card. */ +"CreditCard.UpdateCard.YesButtonTitle.v122" = "Actualizar"; + diff --git a/firefox-ios/Shared/Supporting Files/es.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/es.lproj/EnhancedTrackingProtection.strings index b2c1b44a8624..607a7ed303fa 100644 --- a/firefox-ios/Shared/Supporting Files/es.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/es.lproj/EnhancedTrackingProtection.strings @@ -31,8 +31,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Rastreadores de redes sociales: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Rastreadores bloqueados: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,8 +43,7 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Has desactivado las protecciones"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Estás protegido. Si detectamos algo, te lo haremos saber."; /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ diff --git a/firefox-ios/Shared/Supporting Files/eu.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/eu.lproj/EditAddress.strings index 8ff1be80188b..67d1009c9fde 100644 --- a/firefox-ios/Shared/Supporting Files/eu.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/eu.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "Ez da helbidea gorde"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Ezin da helbidea kendu"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Saiatu berriro"; diff --git a/firefox-ios/Shared/Supporting Files/eu.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/eu.lproj/EnhancedTrackingProtection.strings index 5aad23d451a1..47124f3ea71c 100644 --- a/firefox-ios/Shared/Supporting Files/eu.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/eu.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Konexio ez-segurua"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Ez da jarraipen-elementurik aurkitu"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Guneen arteko cookie jarraipen-egileak: %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Sare sozialetako jarraipen-elementuak: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Blokeatutako jarraipen-elementuak: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,8 +46,7 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Babesak desgaitu dituzu"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Babestuta zaude. Zerbait ikusten badugu, jakinaraziko dizugu."; /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ diff --git a/firefox-ios/Shared/Supporting Files/eu.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/eu.lproj/Microsurvey.strings index be49974ab42f..1f83c3cd1388 100644 --- a/firefox-ios/Shared/Supporting Files/eu.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/eu.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Desautatuta"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Inkesta"; + diff --git a/firefox-ios/Shared/Supporting Files/eu.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/eu.lproj/Toolbar.strings new file mode 100644 index 000000000000..b1faf54105ee --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/eu.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Fitxa berria"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Itxi fitxa"; + diff --git a/firefox-ios/Shared/Supporting Files/fi.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/fi.lproj/EditAddress.strings index 3f2b1bdd11a8..8e234f0a3997 100644 --- a/firefox-ios/Shared/Supporting Files/fi.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/fi.lproj/EditAddress.strings @@ -97,6 +97,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "Osoitetta ei voitu tallentaa"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Osoitetta ei voitu poistaa"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Yritä uudelleen"; diff --git a/firefox-ios/Shared/Supporting Files/fi.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/fi.lproj/EnhancedTrackingProtection.strings index 7b696a6129c5..e5cb919b106d 100644 --- a/firefox-ios/Shared/Supporting Files/fi.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/fi.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Yhteys ei ole suojattu"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Seuraimia ei löytynyt"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Sivustorajat ylittävät seurainevästeet: %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Sosiaalisen median seuraimet: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Seuraimia estetty: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Poistit suojaukset käytöstä"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Olet suojattu. Jos havaitsemme jotain, ilmoitamme sinulle."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "Yhteys ei ole suojattu."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Ole varovainen tällä sivustolla"; diff --git a/firefox-ios/Shared/Supporting Files/fi.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/fi.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..2f090f25b788 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/fi.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Päivitä"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "Tapahtui SSL-virhe, eikä suojattua yhteyttä palvelimeen voida muodostaa."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "Ole varovainen. Jokin ei vaikuta olevan oikein."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "Yritä muodostaa yhteys toisella laitteella. Tarkista modeemi tai reititin. Katkaise Wi-Fi-yhteys ja yhdistä se uudelleen."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "Vaikuttaa siltä, että Internet-yhteydessäsi on ongelma."; + diff --git a/firefox-ios/Shared/Supporting Files/fi.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/fi.lproj/Settings.strings index afbb09f2c004..44c456932d26 100644 --- a/firefox-ios/Shared/Supporting Files/fi.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/fi.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Hallitse osoitteita"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "Käyttäjän %@ osoite"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "TALLENNETUT OSOITTEET"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Tallenna osoitteet %@iin"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "Tallenna tietosi turvallisesti, jotta saat ne käyttöösi nopeasti myöhemmin."; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "Sisältää puhelinnumerot ja sähköpostiosoitteet"; diff --git a/firefox-ios/Shared/Supporting Files/fi.lproj/Shopping.strings b/firefox-ios/Shared/Supporting Files/fi.lproj/Shopping.strings index 188063865907..85a6733f9c35 100644 --- a/firefox-ios/Shared/Supporting Files/fi.lproj/Shopping.strings +++ b/firefox-ios/Shared/Supporting Files/fi.lproj/Shopping.strings @@ -163,6 +163,12 @@ /* Adjusted rating label from How we determine review quality card displayed in the shopping review quality bottom sheet. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ "Shopping.ReviewQualityCard.AdjustedRating.Label.v120" = "*Oikaistu arvosana* perustuu vain niihin arvosteluihin, jotka koemme luotettaviksi."; +/* Accessibility label for the up chevron, from 'How we determine review quality' card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.Collapse.AccessibilityLabel.v120" = "Supista \"Kuinka määritämme arvostelun laadun\"-kortti"; + +/* Accessibility label for the down chevron, from 'How we determine review quality' card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.Expand.AccessibilityLabel.v120" = "Laajenna \"Kuinka määritämme arvostelun laadun\"-kortti"; + /* Title of the 'How we determine review quality' card displayed in the shopping review quality bottom sheet. */ "Shopping.ReviewQualityCard.Label.Title.v120" = "Kuinka määritämme arvostelun laadun"; @@ -193,6 +199,9 @@ /* Accessibility label for the recommended products label and switch, grouped together. The first placeholder is for the recommended products label, and the second placeholder is for the state of the switch: On/Off. */ "Shopping.SettingsCard.Expand.GroupedRecommendedProductsAndSwitch.AccessibilityLabel.v123" = "%1$@, kytkinpainike, %2$@."; +/* Action title of the footer underneath the Settings Card displayed in the shopping review quality bottom sheet. The first parameter will be replaced by the Fakespot app name and the second parameter by the company name of Mozilla. */ +"Shopping.SettingsCard.Footer.Action.v120" = "Arvostelujen tarkistuksen tarjoaa %2$@n %1$@"; + /* Title of the settings card displayed in the shopping review quality bottom sheet. */ "Shopping.SettingsCard.Label.Title.v120" = "Asetukset"; diff --git a/firefox-ios/Shared/Supporting Files/fi.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/fi.lproj/Toolbar.strings new file mode 100644 index 000000000000..a2e3c28afc86 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/fi.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Uusi välilehti"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Sulje välilehti"; + diff --git a/firefox-ios/Shared/Supporting Files/fr.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/fr.lproj/EditAddress.strings index 175198fce209..366f9942c18b 100644 --- a/firefox-ios/Shared/Supporting Files/fr.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/fr.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "L’adresse n’a pas pu être enregistrée"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "L’adresse n’a pas pu être supprimée"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Réessayer"; diff --git a/firefox-ios/Shared/Supporting Files/fr.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/fr.lproj/EnhancedTrackingProtection.strings index 9632216e4bf8..05eef5c43a83 100644 --- a/firefox-ios/Shared/Supporting Files/fr.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/fr.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Connexion non sécurisée"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Aucun traqueur trouvé"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Cookies de pistage intersites : %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Traqueurs de réseaux sociaux : %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Traqueurs bloqués : %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Vous avez désactivé les protections"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Vous êtes protégé·e. Si nous remarquons quelque chose, nous vous le ferons savoir."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "La connexion n’est pas sécurisée."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Faites attention sur ce site"; diff --git a/firefox-ios/Shared/Supporting Files/fr.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/fr.lproj/Microsurvey.strings index 4f5902df0a75..12949ac75452 100644 --- a/firefox-ios/Shared/Supporting Files/fr.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/fr.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Désélectionné"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Enquête"; + diff --git a/firefox-ios/Shared/Supporting Files/fr.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/fr.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..6d00254ac71f --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/fr.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Actualiser"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "Une erreur SSL s’est produite et une connexion sécurisée avec le serveur ne peut pas être établie."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "Faites preuve de prudence, il semble y avoir un problème."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "Essayez de vous connecter sur un autre appareil. Vérifiez votre modem ou votre routeur. Déconnectez-vous puis reconnectez-vous au Wi-Fi."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "Il semble y avoir un problème avec votre connexion Internet."; + diff --git a/firefox-ios/Shared/Supporting Files/fr.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/fr.lproj/Settings.strings index 7cb747140475..cba0f4498912 100644 --- a/firefox-ios/Shared/Supporting Files/fr.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/fr.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Gérer les adresses"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "Adresse pour %@"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "ADRESSES ENREGISTRÉES"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Enregistrez les adresses dans %@"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "Enregistrez vos informations en toute sécurité pour y accéder rapidement plus tard."; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "Y compris les numéros de téléphone et les adresses e-mail"; diff --git a/firefox-ios/Shared/Supporting Files/fr.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/fr.lproj/Toolbar.strings new file mode 100644 index 000000000000..f61b85056401 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/fr.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Nouvel onglet"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Fermer cet onglet"; + diff --git a/firefox-ios/Shared/Supporting Files/he.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/he.lproj/EditAddress.strings index 013eaa1ccf22..036996a988b3 100644 --- a/firefox-ios/Shared/Supporting Files/he.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/he.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "לא ניתן היה לשמור את הכתובת"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "לא ניתן היה להסיר את הכתובת"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "ניסיון חוזר"; diff --git a/firefox-ios/Shared/Supporting Files/he.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/he.lproj/EnhancedTrackingProtection.strings index 51fdbbf14606..b3dc3347f448 100644 --- a/firefox-ios/Shared/Supporting Files/he.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/he.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "החיבור אינו מאובטח"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "לא נמצאו רכיבי מעקב"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "עוגיות מעקב חוצות אתרים: %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "רכיבי מעקב של מדיה חברתית: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "רכיבי מעקב שנחסמו: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "כיבית את ההגנות"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "ההגנה פעילה. אם נזהה משהו חריג, נודיע לך."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "החיבור שלך אינו מאובטח."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "יש להיזהר באתר זה"; diff --git a/firefox-ios/Shared/Supporting Files/he.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/he.lproj/Microsurvey.strings index 88c16a6ab7a5..61d47bc69d7e 100644 --- a/firefox-ios/Shared/Supporting Files/he.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/he.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "לא נבחר"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "סקר"; + diff --git a/firefox-ios/Shared/Supporting Files/he.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/he.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..4c6066f82612 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/he.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "טעינה מחדש"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "אירעה שגיאת SSL ולא ניתן ליצור חיבור מאובטח לשרת."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "נא להיזהר. משהו לא נראה תקין."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "נא לנסות להתחבר במכשיר אחר, לבדוק את המודם או הנתב שלך, ולהתנתק ולהתחבר מחדש ל־Wi-Fi."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "נראה שיש בעיה בחיבור האינטרנט שלך."; + diff --git a/firefox-ios/Shared/Supporting Files/he.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/he.lproj/Settings.strings index b0d3a1410b64..c629fb89c391 100644 --- a/firefox-ios/Shared/Supporting Files/he.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/he.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "ניהול כתובות"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "כתובת עבור %@"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "כתובות שמורות"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "שמירת כתובות ל־%@"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "שמירה של המידע שלך בצורה מאובטחת כדי לקבל גישה מהירה אליו מאוחר יותר."; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "כולל מספרי טלפון וכתובות דוא״ל"; diff --git a/firefox-ios/Shared/Supporting Files/he.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/he.lproj/Toolbar.strings new file mode 100644 index 000000000000..f36854ab5918 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/he.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "לשונית חדשה"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "סגירת לשונית זו"; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/ActivityStream.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/ActivityStream.strings new file mode 100644 index 000000000000..d64f37e12e1b --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/ActivityStream.strings @@ -0,0 +1,6 @@ +/* String used in the section title of the Bookmarks section on Home Screen. */ +"ActivityStream.Bookmarks.Title.v128" = "Zabilješke"; + +/* Show all button text for Bookmarks items on the home page, which opens the Bookmarks panel when tapped. */ +"Bookmarks.Actions.More.v128" = "Prikaži sve"; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/AddressToolbar.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/AddressToolbar.strings new file mode 100644 index 000000000000..5c80c5e72b6e --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/AddressToolbar.strings @@ -0,0 +1,12 @@ +/* Accessibility label for the address field in the address toolbar. */ +"AddressToolbar.Location.A11y.Label.v128" = "Traži ili upiši adresu"; + +/* Placeholder for the address field in the address toolbar. */ +"AddressToolbar.Location.Placeholder.v128" = "Traži ili upiši adresu"; + +/* Accessibility label for the lock icon button in the address field of the address toolbar, responsible with Privacy & Security Settings. */ +"AddressToolbar.PrivacyAndSecuriySettings.A11y.Label.v128" = "Postavke privatnosti i sigurnosti"; + +/* Accessibility label for the search engine icon in the address field of the address toolbar. The placeholder is getting replaced with the name of the search engine (e.g. Google). */ +"AddressToolbar.SearchEngine.A11y.Label.v128" = "Tražilica: %@"; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/Alert.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/Alert.strings index ea9a09cd56c3..84e05b0d6c47 100644 --- a/firefox-ios/Shared/Supporting Files/hr.lproj/Alert.strings +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/Alert.strings @@ -1,6 +1,12 @@ /* Button text to dismiss the dialog box that gets presented as a confirmation to remove card and cancel the operation. */ "CreditCard.SnackBar.CancelRemoveCardButton.v112" = "Odustani"; +/* Sub label for the dialog box that gets presented as a confirmation to ask user if they would like to remove the saved credit card from local as well as all their synced devices */ +"CreditCard.SnackBar.RemoveCardSublabel.v112" = "Ovo će ukloniti kreditnu karticu sa svih tvojih sinkroniziranih uređaja."; + +/* Title label for the dialog box that gets presented as a confirmation to ask user if they would like to remove the saved credit card */ +"CreditCard.SnackBar.RemoveCardTitle.v122" = "Ukloniti kreditnu karticu?"; + /* Button text to dismiss the dialog box that gets presented as a confirmation to remove card and perform the operation of removing the credit card. */ "CreditCard.SnackBar.RemovedCardButton.v112" = "Ukloni"; diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/Alerts.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/Alerts.strings index 5ad4c442a802..ba590442cd8a 100644 --- a/firefox-ios/Shared/Supporting Files/hr.lproj/Alerts.strings +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/Alerts.strings @@ -1,6 +1,24 @@ +/* When tapping the fire icon in private mode, an alert comes up asking to confirm if you want to delete all browsing data and end your private session. This is the body text for the alert. */ +"Alerts.FeltDeletion.Body.v122" = "Zatvori sve privatne kartice i izbriši povijest, kolačiće i sve ostale podatke web stranica."; + +/* When tapping the fire icon in private mode, an alert comes up asking to confirm if you want to delete all browsing data and end your private session. This is the cancel action for the alert, cancelling ending your session. */ +"Alerts.FeltDeletion.Button.Cancel.v122" = "Odustani"; + +/* When tapping the fire icon in private mode, an alert comes up asking to confirm if you want to delete all browsing data and end your private session. This is the affirmative action for the alert, confirming that you do want to do that. */ +"Alerts.FeltDeletion.Button.Confirm.v122" = "Izbriši podatke sesije"; + +/* When tapping the fire icon in private mode, an alert comes up asking to confirm if you want to delete all browsing data and end your private session. This is the title for the alert. */ +"Alerts.FeltDeletion.Title.v122" = "Završiti privatnu sesiju?"; + /* The title for the negative action of the restore tabs pop-up alert. This alert shows when opening up Firefox after it crashed, and will reject the action of restoring tabs. */ "Alerts.RestoreTabs.Button.No.v109" = "Ne"; /* The title for the affirmative action of the restore tabs pop-up alert. This alert shows when opening up Firefox after it crashed, and will restore existing tabs. */ "Alerts.RestoreTabs.Button.Yes.v109" = "Vrati kartice"; +/* The body of the restore tabs pop-up alert. This alert shows when opening up Firefox after it crashed. */ +"Alerts.RestoreTabs.Message.v109" = "Oprosti. Obnovi kartice da nastaviš tamo gdje si stao/la."; + +/* The title of the restore tabs pop-up alert. This alert shows when opening up Firefox after it crashed. The placeholder will be the Firefox name. */ +"Alerts.RestoreTabs.Title.v109.v2" = "%@ se srušio. Želiš li vratiti tvoje kartice?"; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/BiometricAuthentication.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/BiometricAuthentication.strings new file mode 100644 index 000000000000..304c5e27d8ce --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/BiometricAuthentication.strings @@ -0,0 +1,6 @@ +/* Biometric authentication is when the system prompts users for Face ID or fingerprint before accessing protected information. This string asks the user to enter their device passcode to access the protected screen. */ +"Biometry.Screen.UniversalAuthenticationReason.v115" = "Autentificiraj se za pristup lozinkama."; + +/* Biometric authentication is when the system prompts users for Face ID or fingerprint before accessing protected information. This string asks the user to enter their device passcode to access the protected screen for logins and encrypted cards. */ +"Biometry.Screen.UniversalAuthenticationReason.v122" = "Autentificiraj se za pristup spremljenim lozinkama i načinima plaćanja."; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/BottomSheet.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/BottomSheet.strings new file mode 100644 index 000000000000..9531cf325778 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/BottomSheet.strings @@ -0,0 +1,3 @@ +/* When a user is in the process of entering an address, a screen pops up prompting the user if they want to use a saved address. This string is used as the title label of the screen. */ +"Addresses.BottomSheet.UseSavedAddressBottomSheet.v124" = "Koristiti jednu spremljenu adresu?"; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/ContextualHints.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/ContextualHints.strings new file mode 100644 index 000000000000..002345733444 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/ContextualHints.strings @@ -0,0 +1,3 @@ +/* Contextual hints are little popups that appear for the users informing them of new features. This is a call to action for the popup that appears to educate users about what the fire button in the toolbar does, when in private mode. */ +"ContextualHints.FeltDeletion.Body.v122" = "Dodirni ovdje za početak nove privatne sesije. Izbriši svoju povijest, kolačiće – sve."; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/CredentialProvider.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/CredentialProvider.strings new file mode 100644 index 000000000000..a42e685930ed --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/CredentialProvider.strings @@ -0,0 +1,12 @@ +/* Label shown when there are no logins to list. The placeholder will be replaced with the app name. */ +"LoginsList.NoLoginsFound.Description.v122" = "Ovdje će se prikazati s aplikacijom %@ spremljene ili sinkronizirane lozinke. Sve lozinke koje spremiš se šifriraju."; + +/* Label shown when there are no logins saved in the passwords list */ +"LoginsList.NoLoginsFound.Title.v122" = "Nijedna lozinka nije spremljena"; + +/* Label displayed when a user searches for an item, and no matches can be found against the search query */ +"LoginsList.NoMatchingResult.Title.v122" = "Nijedna lozinka nije pronađena"; + +/* Placeholder text for search field in the credential provider list */ +"LoginsList.Search.Placeholder.v122" = "Traži lozinke"; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/Credentials.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/Credentials.strings new file mode 100644 index 000000000000..718e7831949f --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/Credentials.strings @@ -0,0 +1,6 @@ +/* Message shown when you enter Logins & Passwords without having a device passcode set. */ +"Logins.DevicePasscodeRequired.Message.v122" = "Za spremanje i automatsko ispunjavanje lozinki aktiviraj Face ID, Touch ID ili šifru uređaja."; + +/* Message shown when you enter Payment Methods without having a device passcode set. */ +"Logins.PaymentMethods.DevicePasscodeRequired.Message.v124.v2" = "Za spremanje i automatsko ispunjavanje kreditnih kartica aktiviraj Face ID, Touch ID ili šifru uređaja."; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/CustomizeFirefoxHome.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/CustomizeFirefoxHome.strings index 5f2fbb8b5e63..77314e0c58b5 100644 --- a/firefox-ios/Shared/Supporting Files/hr.lproj/CustomizeFirefoxHome.strings +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/CustomizeFirefoxHome.strings @@ -1,3 +1,9 @@ +/* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle Bookmarks section on the Firefox homepage on or off */ +"Settings.Home.Option.Bookmarks.v128" = "Zabilješke"; + +/* In the settings menu, in the Firefox homepage customization section, this is the subtitle for the option that allows users to turn the Pocket Recommendations section on the Firefox homepage on or off. The placeholder is the pocket app name. */ +"Settings.Home.Option.ThoughtProvokingStories.subtitle.v116" = "Članke pokreće %@"; + /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to turn the Pocket Recommendations section on the Firefox homepage on or off */ "Settings.Home.Option.ThoughtProvokingStories.v116" = "Priče koje potiču na razmišljanje"; diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/Edit Card.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/Edit Card.strings index 465f211b6974..ef692feaddfc 100644 --- a/firefox-ios/Shared/Supporting Files/hr.lproj/Edit Card.strings +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/Edit Card.strings @@ -1,3 +1,3 @@ /* Title label for the view where user can edit their credit card info */ -"CreditCard.EditCard.EditCreditCardTitle.v113" = "Uredi kreditnu karticu"; +"CreditCard.EditCard.EditCreditCardTitle.v122" = "Uredi kreditnu karticu"; diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/EditAddress.strings new file mode 100644 index 000000000000..f18b777bab33 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/EditAddress.strings @@ -0,0 +1,141 @@ +/* Title for the cancel button in the remove address alert. */ +"Addresses.EditAddress.Alert.CancelButton.v129" = "Odustani"; + +/* Message explaining the consequences of removing an address from all synced devices. */ +"Addresses.EditAddress.Alert.Message.v129" = "Adresa će se ukloniti sa svih tvojih sinkroniziranih uređaja."; + +/* Title for the remove button in the remove address alert. */ +"Addresses.EditAddress.Alert.RemoveButton.v129" = "Ukloni"; + +/* Title for the alert indicating the action to remove an address. */ +"Addresses.EditAddress.Alert.Title.v129" = "Ukloni adresu"; + +/* Title for the interface option where users can add a new address for autofill purposes. This facilitates quicker form completion by automatically filling in the user's address information. */ +"Addresses.EditAddress.AutofillAddAddressTitle.v129" = "Dodaj adresu"; + +/* Label for the area field, allowing users to specify a particular area within a city or region. This detail can improve the specificity and accuracy of autofilled addresses. */ +"Addresses.EditAddress.AutofillAddressArea.v129" = "Područje"; + +/* Label for the field where users input the city part of their address. This information is crucial for mail delivery and service provision, ensuring accurate city identification in autofill settings. */ +"Addresses.EditAddress.AutofillAddressCity.v129" = "Grad"; + +/* Label for the field where users can specify just the country, used in contexts where full address details are not required. Simplifies autofill when only country information is necessary. */ +"Addresses.EditAddress.AutofillAddressCountryOnly.v129" = "Zemlja"; + +/* Label for the country or region field in address forms, allowing users to specify their country or territorial region. This is fundamental for international mail and services, ensuring autofill accuracy across borders. */ +"Addresses.EditAddress.AutofillAddressCountryRegion.v129" = "Zemlja ili regija"; + +/* Label for the county field, crucial for addressing in regions where county lines play a key role in postal services. Enhances autofill accuracy by including county information. */ +"Addresses.EditAddress.AutofillAddressCounty.v129" = "Županija"; + +/* Label for the department field, used in countries like France and Colombia where departments are a key administrative division. Ensures correct departmental information is autofilled. */ +"Addresses.EditAddress.AutofillAddressDepartment.v129" = "Odjel"; + +/* Label for the district field in the address form, allowing users to specify their district for more precise location identification. This aids in refining address details for accurate autofill. */ +"Addresses.EditAddress.AutofillAddressDistrict.v129" = "Okrug"; + +/* Label for the Do/Si field, pertinent to addresses in South Korea. Do/Si refers to provincial level divisions, and specifying this enhances address accuracy in autofill settings. */ +"Addresses.EditAddress.AutofillAddressDoSi.v129" = "Do/Si"; + +/* Label for the Eircode field, specific to Ireland. It's a unique postal code system that helps in precise location identification, enhancing the effectiveness of autofill. */ +"Addresses.EditAddress.AutofillAddressEircode.v129" = "Eircode"; + +/* Label for the email address field, where users input their email. Critical for digital communication and account verification, this ensures email addresses are autofilled accurately. */ +"Addresses.EditAddress.AutofillAddressEmail.v129" = "E-mail adresa"; + +/* Label for the emirate field, essential for addresses in the United Arab Emirates. Including emirate details ensures the autofill feature accurately represents user addresses. */ +"Addresses.EditAddress.AutofillAddressEmirate.v129" = "Emirat"; + +/* Label for the field where users specify the name of an island, if applicable. Important for addresses in archipelagic regions, aiding in precise location identification during autofill. */ +"Addresses.EditAddress.AutofillAddressIsland.v129" = "Otok"; + +/* Label for the field where the user inputs their full name as part of an address form. Essential for personalized form submissions and ensuring information accuracy in autofilled forms. */ +"Addresses.EditAddress.AutofillAddressName.v129" = "Ime"; + +/* Label for the field where users can input the name of their neighborhood. This detail adds precision to addresses, especially in densely populated areas, improving the accuracy of autofill. */ +"Addresses.EditAddress.AutofillAddressNeighborhood.v129" = "Susjedstvo"; + +/* Label for the oblast field, relevant for addresses in countries like Russia and Ukraine. Oblasts are a significant administrative division, and their specification aids in autofill accuracy. */ +"Addresses.EditAddress.AutofillAddressOblast.v129" = "Oblast"; + +/* Label for the input field designated for the organization's name related to the address. Helps in distinguishing addresses used for business or personal purposes in autofill settings. */ +"Addresses.EditAddress.AutofillAddressOrganization.v129" = "Organizacija"; + +/* Label for the parish field, significant in places where parishes are used for local administration and addressing. Ensures users can specify parish details for better autofill accuracy. */ +"Addresses.EditAddress.AutofillAddressParish.v129" = "Župa"; + +/* Label for the PIN (Postal Index Number) field, used in India. It's a code representing a specific area, crucial for accurate mail delivery and autofill functionality. */ +"Addresses.EditAddress.AutofillAddressPin.v129" = "Pin"; + +/* Label for the postal code field, universally used in address forms to specify the area code for mail sorting. Essential for autofill to ensure mail and services are accurately routed. */ +"Addresses.EditAddress.AutofillAddressPostalCode.v129" = "Poštanski broj"; + +/* Label for the post town field, used primarily in the UK and some other regions for mail sorting. Essential for users in applicable areas to specify for correct mail delivery through autofill. */ +"Addresses.EditAddress.AutofillAddressPostTown.v129" = "Post town"; + +/* Label for the prefecture field, essential for addresses in countries like Japan where prefectures are a major administrative division. Aids in precise location specification for autofill. */ +"Addresses.EditAddress.AutofillAddressPrefecture.v129" = "Prefektura"; + +/* Label for the province field, required in countries where provinces are a primary administrative division. Helps in pinpointing the user's location more accurately for autofill purposes. */ +"Addresses.EditAddress.AutofillAddressProvince.v129" = "Provincija"; + +/* Label for the state field, a necessary component of addresses in many countries, especially the USA. It ensures that state-specific details are correctly filled in forms using autofill. */ +"Addresses.EditAddress.AutofillAddressState.v129" = "Država"; + +/* Label for the suburb field, enabling users to add suburb details to their address. This is important for accurate delivery and services in suburban areas, enhancing autofill functionality. */ +"Addresses.EditAddress.AutofillAddressSuburb.v129" = "Predgrađe"; + +/* Label for the telephone number field, allowing users to input their contact number. This is essential for communication and service provision, ensuring contact details are autofilled correctly. */ +"Addresses.EditAddress.AutofillAddressTel.v129" = "Telefon"; + +/* Label for the input field for the townland, a specific type of land division used in rural areas. Enhances address detail for users in regions where townlands are a common addressing component. */ +"Addresses.EditAddress.AutofillAddressTownland.v129" = "Townland"; + +/* Label for the field to input the name of a village or township. This is crucial for addresses in rural areas, ensuring the autofill feature accurately captures all necessary geographical details. */ +"Addresses.EditAddress.AutofillAddressVillageTownship.v129" = "Selo ili općina"; + +/* Label for the ZIP code field, primarily used in the United States for mail sorting. Key for autofill to accurately complete addresses for shipping, billing, and service provision. */ +"Addresses.EditAddress.AutofillAddressZip.v129" = "Poštanski broj"; + +/* Label for the button to cancel the current autofill operation or exit the form without saving changes. Provides users with an option to back out of a process without making any modifications. */ +"Addresses.EditAddress.AutofillCancelButton.v129" = "Odustani"; + +/* Title for the option allowing users to edit an existing saved address. This is used within the settings for autofill, enabling users to update their address details for accurate form autofilling. */ +"Addresses.EditAddress.AutofillEditAddressTitle.v129" = "Uredi adresu"; + +/* Title for the input field where users can enter their street address. This is used within the settings for autofill, allowing users to provide their street address for accurate form autofilling. */ +"Addresses.EditAddress.AutofillEditStreetAddressTitle.v129" = "Ulica"; + +/* Label for the button to save the current address details entered or edited by the user. This action confirms the user's changes and updates their autofill settings accordingly. */ +"Addresses.EditAddress.AutofillSaveButton.v129" = "Spremi"; + +/* Title for the option allowing users to view an existing saved address. This is used within the settings for autofill, enabling users to see their address details for accurate form autofilling. */ +"Addresses.EditAddress.AutofillViewAddressTitle.v129" = "Prikaži adresu"; + +/* Button label for closing the view where user can view their address info. */ +"Addresses.EditAddress.CloseNavBarButtonLabel.v129" = "Zatvori"; + +/* Button label for editing the address details shown in the form. */ +"Addresses.EditAddress.EditNavBarButtonLabel.v129" = "Uredi"; + +/* Title for button that offers the user the option to remove an address. */ +"Addresses.EditAddress.RemoveAddressButtonTitle.v129" = "Ukloni adresu"; + +/* Toast message confirming that an address has been successfully removed. */ +"Addresses.Toast.AddressRemovedConfirmation.v129" = "Adresa je uklonjena"; + +/* Toast message confirming that an address has been successfully saved. */ +"Addresses.Toast.AddressSavedConfirmation.v129" = "Adresa spremljena"; + +/* Toast message indicating an error occurred while trying to save an address. */ +"Addresses.Toast.AddressSaveError.v129" = "Adresa se nije mogla spremiti"; + +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Adresa se nije mogla ukloniti"; + +/* Suggestion to try again after an error occurred while trying to save an address. */ +"Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Pokušaj ponovo"; + +/* Toast message confirming that an address has been successfully updated. */ +"Addresses.Toast.AddressUpdatedConfirmation.v129" = "Podaci o adresi su aktulizirani"; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/EditCard.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/EditCard.strings index 65732fb945ba..0072ab1749ea 100644 --- a/firefox-ios/Shared/Supporting Files/hr.lproj/EditCard.strings +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/EditCard.strings @@ -1,3 +1,6 @@ +/* Title label for the view where user can add their credit card info */ +"CreditCard.EditCard.AddCreditCardTitle.v122" = "Dodaj kreditnu karticu"; + /* Button label for cancelling editing of the credit card details shown in the form */ "CreditCard.EditCard.CancelNavBarButtonLabel.v113" = "Odustani"; @@ -40,6 +43,9 @@ /* Button label for saving the credit card details user entered in the form */ "CreditCard.EditCard.SaveNavBarButtonLabel.v113" = "Spremi"; +/* Title label for user to use the toggle settings to allow saving and autofilling of credit cards for webpages. */ +"CreditCard.EditCard.ToggleToAllowAutofillTitle.v122" = "Spremi i ispuni načine plaćanja"; + /* Title label for the view where user can view their credit card info */ "CreditCard.EditCard.ViewCreditCardTitle.v116" = "Prikaži karticu"; diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/EngagementNotification.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/EngagementNotification.strings index 9d3a595c3cec..ca35deffa9b6 100644 --- a/firefox-ios/Shared/Supporting Files/hr.lproj/EngagementNotification.strings +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/EngagementNotification.strings @@ -4,9 +4,15 @@ /* Title of notification send to user after inactivity to encourage them to use the search feature. */ "Engagement.Notification.Title.v112" = "Započni svoje prvo pretraživanje"; +/* Body of notification send to user after inactivity to encourage them to use the private browsing feature. Placeholder is app name. */ +"Engagement.Notification.Treatment.A.Body.v114" = "Privatno pregledavanje u %@ ne sprema tvoje podatke i blokira skrivene programe za praćenje."; + /* Title of notification send to user after inactivity to encourage them to use the private browsing feature. */ "Engagement.Notification.Treatment.A.Title.v114" = "Pretražuj bez ostavljanja traga"; +/* Body of notification send to user after inactivity to encourage them to use the private browsing feature. Placeholder is the app name. */ +"Engagement.Notification.Treatment.B.Body.v114" = "Pregledavaj bez spremljenih kolačića ili povijesti u %@."; + /* Title of notification send to user after inactivity to encourage them to use the private browsing feature. */ "Engagement.Notification.Treatment.B.Title.v114" = "Isprobaj privatno pretraživanje"; diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/EnhancedTrackingProtection.strings new file mode 100644 index 000000000000..a079d8493d52 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/EnhancedTrackingProtection.strings @@ -0,0 +1,72 @@ +/* The text for the clear cookies and site data alert button inside the enhanced tracking protection screen. */ +"Menu.EnhancedTrackingProtection.ClearData.AlertCancelButton.v128" = "Odustani"; + +/* The text for the clear cookies and site data alert button inside the enhanced tracking protection screen. */ +"Menu.EnhancedTrackingProtection.ClearData.AlertOkButton.v128" = "Izbriši"; + +/* The text for the clear cookies and site data alert inside the enhanced tracking protection screen. The placeholder will be replaced with the user's currently visited website */ +"Menu.EnhancedTrackingProtection.ClearData.AlertText.v128" = "Uklanjanjem kolačića i podataka web stranice, %@ će te možda odjaviti s web stranice i isprazniti košaricu."; + +/* The title for the clear cookies and site data alert inside the enhanced tracking protection screen. */ +"Menu.EnhancedTrackingProtection.ClearData.AlertTitle.v128" = "Izbriši kolačiće i podatke stranice"; + +/* The title for the clear cookies and site data button inside the enhanced tracking protection screen. */ +"Menu.EnhancedTrackingProtection.ClearData.ButtonTitle.v128" = "Obriši kolačiće i podatke stranice"; + +/* The text for the clear cookies and site data toast that appears when the user selects to clear the cookies */ +"Menu.EnhancedTrackingProtection.ClearData.ToastMessage.v128" = "Kolačići i podaci stranice su uklonjeni"; + +/* Text to let users know that the current website is secure. */ +"Menu.EnhancedTrackingProtection.Details.ConnectionSecure.v128" = "Sigurna veza"; + +/* Text to let users know that the current website is not secure. */ +"Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Veza nije sigurna"; + +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Nema programa za praćenje"; + +/* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Kolačići za praćenje web stranica: %@"; + +/* Text to let users know how many fingerprinters were blocked on the current website. The placeholder will show the number of fingerprinters detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.Fingerprinter.v129" = "Broj prikupljača digitalnih otisaka: %@"; + +/* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Programi za praćenje društvenih mreža: %@"; + +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ +"Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Blokirani programi za praćenje: %@"; + +/* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ +"Menu.EnhancedTrackingProtection.Details.Verifier.v128" = "Ovjereno od %@"; + +/* Header for the enhanced tracking protection screen when the user has opted out of the feature. Placeholder will be replaced by the app name */ +"Menu.EnhancedTrackingProtection.Off.Header.v128" = "%@ je deaktiviran. Predlažemo ponovno uključivanje zaštite."; + +/* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ +"Menu.EnhancedTrackingProtection.Off.Title.v128" = "Isključio/la si zaštitu"; + +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ +"Menu.EnhancedTrackingProtection.On.Header.v128" = "Zaštićen/a si. Ako nešto uočimo, javit ćemo ti."; + +/* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Oprez na ovoj stranici"; + +/* Title for the enhanced tracking protection screen when the user has selected to be protected. The placeholder will have the value of the app name */ +"Menu.EnhancedTrackingProtection.On.Title.v128" = "%@ stražari"; + +/* The title for the privacy settings button inside the enhanced tracking protection screen. */ +"Menu.EnhancedTrackingProtection.PrivacySettings.Title.v128" = "Postavke privatnosti"; + +/* Title for the switch to enable/disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.Switch.Title.v128" = "Poboljšana zaštita od praćenja"; + +/* A switch to disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.SwitchOff.Text.v128" = "Zaštite su ISKLJUČENE. Predlažemo da ih ponovo uključiš."; + +/* A switch to disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.SwitchOff.Text.v129" = "Zaštite su ISKLJUČENE. Predlažemo da ih ponovo uključiš."; + +/* A switch to disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.SwitchOn.Text.v128" = "Ako na ovoj stranici nešto izgleda pokvareno, pokušaj isključiti zaštitu."; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/FirefoxHomepage.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/FirefoxHomepage.strings new file mode 100644 index 000000000000..c18da86f2c3e --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/FirefoxHomepage.strings @@ -0,0 +1,12 @@ +/* When the user ends their private session, they are returned to the private mode homepage, and a toastbar popups confirming that their data has been erased. This is the label for that toast. */ +"FirefoxHomepage.FeltDeletion.Link.v122" = "Podaci privatnog pregledavanja izbrisani"; + +/* The body of the message for the card that educates users about how private mode works. The card shows up on the homepage when in the new privacy mode. Placeholder refers to app name. */ +"FirefoxHomepage.FeltPrivacyUI.Body.v122" = "%@ briše tvoje kolačiće, povijest i podatke web stranica kada zatvoriš sve svoje privatne kartice."; + +/* The link for the card that educates users about how private mode works. The link redirects to an external site for more information. The card shows up on the homepage when in the new privacy mode. */ +"FirefoxHomepage.FeltPrivacyUI.Link.v122" = "Tko bi mogao vidjeti moju aktivnost?"; + +/* The title for the card that educates users about how private mode works. The card shows up on the homepage when in the new privacy mode. */ +"FirefoxHomepage.FeltPrivacyUI.Title.v122" = "Ne ostavljaj tragove na ovom uređaju"; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/FirefoxLogins.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/FirefoxLogins.strings new file mode 100644 index 000000000000..e8a59c6f315b --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/FirefoxLogins.strings @@ -0,0 +1,18 @@ +/* Prompt for saving the username in the Save Logins prompt. */ +"LoginsHelper.PromptSaveLogin.Title.v122" = "Spremiti korisničko ime?"; + +/* Prompt for saving a password in the Save Logins prompt. */ +"LoginsHelper.PromptSavePassword.Title.v122" = "Spremiti lozinku?"; + +/* Prompt for updating the password in the Update Password prompt. */ +"LoginsHelper.PromptUpdateLogin.Title.OneArg.v122" = "Aktualizirati lozinku?"; + +/* Prompt for updating a password in the Update Password prompt. */ +"LoginsHelper.PromptUpdateLogin.Title.TwoArg.v122" = "Aktualizirati lozinku?"; + +/* Placeholder text for search box in logins list view. */ +"LoginsList.LoginsListSearchPlaceholder.v122" = "Pretraži lozinke"; + +/* Title for the list of logins saved by the app */ +"LoginsList.Title.v122" = "SPREMLJENE LOZINKE"; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/FirefoxSync.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/FirefoxSync.strings index 527f0e697fb5..949cac42bc20 100644 --- a/firefox-ios/Shared/Supporting Files/hr.lproj/FirefoxSync.strings +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/FirefoxSync.strings @@ -1,3 +1,9 @@ +/* Toggle for address autofill syncing setting */ +"FirefoxSync.AddressAutofillEngine.v124" = "Adrese"; + /* Toggle for credit cards syncing setting */ -"FirefoxSync.CreditCardsEngine.v115" = "Kreditne kartice"; +"FirefoxSync.CreditCardsEngine.v122" = "Načini plaćanja"; + +/* Toggle passwords syncing setting, in the Settings > Sync Data menu of the app. */ +"Sync.LoginsEngine.Title.v122" = "Lozinke"; diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/Footer.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/Footer.strings index 54a5796f1aa1..b3d93b99e3bd 100644 --- a/firefox-ios/Shared/Supporting Files/hr.lproj/Footer.strings +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/Footer.strings @@ -1,3 +1,6 @@ /* This is the learn more text of the Pocket footer on Firefox Homepage. */ "FirefoxHomepage.Pocket.Footer.LearnMore.v115" = "Saznaj više"; +/* This is the title of the Pocket footer on Firefox Homepage. The first placeholder is for the Pocket app name and the second placeholder for the app name */ +"FirefoxHomepage.Pocket.Footer.Title.v116" = "Pokreće %1$@. Dio %2$@ obitelji."; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/JumpBackIn.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/JumpBackIn.strings new file mode 100644 index 000000000000..c7f462672901 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/JumpBackIn.strings @@ -0,0 +1,6 @@ +/* Contextual hints are little popups that appear for the users informing them of new features. This one talks about additions to the Firefox homepage regarding a more personalized experience. */ +"ContextualHints.FirefoxHomepage.JumpBackIn.PersonalizedHome" = "Upoznaj svoju personaliziranu početnu stranicu. Ovdje će se pojaviti nedavne kartice, zabilješke i rezultati pretraživanja."; + +/* Contextual hints are little popups that appear for the users informing them of new features. When a user is logged in and has a tab synced from desktop, this popup indicates which tab that is within the Jump Back In section. */ +"ContextualHints.FirefoxHomepage.JumpBackIn.SyncedTab.v106" = "Tvoje se kartice sinkroniziraju! Nastavi gdje si stao/la na svom drugom uređaju."; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/KeyboardAccessory.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/KeyboardAccessory.strings new file mode 100644 index 000000000000..89a274ef95ae --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/KeyboardAccessory.strings @@ -0,0 +1,6 @@ +/* Accessibility label for next button that is displayed above the keyboard when a form field on a website was tapped. */ +"KeyboardAccessory.NextButton.Accessibility.Label.v124" = "Sljedeće polje obrasca"; + +/* Accessibility label for previous button that is displayed above the keyboard when a form field on a website was tapped. */ +"KeyboardAccessory.PreviousButton.Accessibility.Label.v124" = "Prethodno polje obrasca"; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/LoginsHelper.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/LoginsHelper.strings new file mode 100644 index 000000000000..ae15b83d75d0 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/LoginsHelper.strings @@ -0,0 +1,9 @@ +/* Button to not save the user's password in the logins helper */ +"LoginsHelper.DontSave.Button.v122" = "Ne sada"; + +/* Button to not update the user's password in the logins helper */ +"LoginsHelper.DontUpdate.Button.v122" = "Ne sada"; + +/* Button to save the user's password */ +"LoginsHelper.SaveLogin.Button.v122" = "Spremi"; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/Microsurvey.strings new file mode 100644 index 000000000000..1b0d0ad7ef21 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/Microsurvey.strings @@ -0,0 +1,72 @@ +/* On top of the bottom toolbar, there can be a microsurvey prompt, this is the title for the button that appears on the prompt that allows the user to tap on and navigates them to the microsurvey to respond to. */ +"Microsurvey.Prompt.Button.v127" = "Nastavi"; + +/* On top of the bottom toolbar, there can be a microsurvey prompt, this is the accessibility label for the close button that appears on the prompt that allows the user to dismiss the microsurvey prompt. */ +"Microsurvey.Prompt.Close.AccessibilityLabel.v127" = "Zatvori upit ankete"; + +/* On top of the bottom toolbar, there can be a microsurvey prompt, this is the accessibility label for the close button that appears on the prompt that allows the user to dismiss the microsurvey prompt. */ +"Microsurvey.Prompt.Close.Button.AccessibilityLabel.v127" = "Zatvori"; + +/* On top of the bottom toolbar, there can be a microsurvey prompt, this is the logo image that appears on the prompt to inform the prompt is coming from the app specifically. Placeholder is for the app name. */ +"Microsurvey.Prompt.LogoImage.AccessibilityLabel.v129" = "%@ logotip"; + +/* On top of the bottom toolbar, there can be a microsurvey prompt, this is the title for the text that appears on the prompt to inform the user that this is a prompt to take a survey. Placeholder is for the app name. */ +"Microsurvey.Prompt.TitleLabel.v127" = "Pomogni nam poboljšati %@. Traje samo jednu minutu."; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this the title of button on the survey that a user can tap on to submit their responses. */ +"Microsurvey.Survey.Button.v127" = "Pošalji"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label for close button that dismisses the sheet. */ +"Microsurvey.Survey.Close.AccessibilityLabel.v127" = "Zatvori anketu"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label for close button that dismisses the sheet. */ +"Microsurvey.Survey.Close.Button.AccessibilityLabel.v127" = "Zatvori"; + +/* On the microsurvey, which is a bottom sheet that pops up with a survey question and options, this is the text shown on the confirmation page when the user has completed the survey. */ +"Microsurvey.Survey.ConfirmationPage.ConfirmationLabel.v127" = "Hvala ti povratnim informacijama!"; + +/* On the microsurvey, which is a bottom sheet that pops up with a survey question and options, this is the title for the header on the microsurvey when the user has completed the survey. */ +"Microsurvey.Survey.ConfirmationPage.HeaderLabel.v127" = "Anketa dovršena"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the title for the header on the screen. */ +"Microsurvey.Survey.HeaderLabel.v127" = "Ispuni ovu anketu"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the title for the header on the screen. */ +"Microsurvey.Survey.HeaderLabel.v129" = "Ispuni anketu"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the logo image that appears on the bottom sheet that informs the user that it is coming from the app specifically. Placeholder is for the app name. */ +"Microsurvey.Survey.LogoImage.AccessibilityLabel.v129" = "%@ logotip"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.LikertScaleOption1.v127" = "Vrlo zadovoljan/na"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.LikertScaleOption2.v127" = "Zadovoljan/na"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.LikertScaleOption3.v127" = "Neutralno"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.LikertScaleOption4.v127" = "Nezadovoljan/na"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ +"Microsurvey.Survey.Options.LikertScaleOption5.v127" = "Vrlo nezadovoljan/na"; + +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. It indicates that the user has not use the feature that the survey is inquiring about. */ +"Microsurvey.Survey.Options.LikertScaleOption6.v129" = "Ne koristim ju"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states the order the survey option in the list of options. First placeholder is the number the option is in the list and the second placeholder is the total number of options such as 1 out of 6. */ +"Microsurvey.Survey.OptionsOrder.AccessibilityLabel.v129" = "%1$@ od %2$@"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this the title of a link on the survey and allows the user to navigate to our privacy policy details. */ +"Microsurvey.Survey.PrivacyPolicyLink.v127" = "Napomena o privatnosti"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was selected. */ +"Microsurvey.Survey.RadioButton.Selected.AccessibilityLabel.v129" = "Odabrano"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ +"Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Neodabrano"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Anketa"; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/Notification.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/Notification.strings new file mode 100644 index 000000000000..9f4623fee9b1 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/Notification.strings @@ -0,0 +1,3 @@ +/* Fallback Title of notification if no notification title was configured. The notification is an advise to the user. The argument is the app name. */ +"Notification.Fallback.Title.v113" = "%@ savjet"; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/Onboarding.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/Onboarding.strings index 6939491be134..12d8df3b7e25 100644 --- a/firefox-ios/Shared/Supporting Files/hr.lproj/Onboarding.strings +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/Onboarding.strings @@ -1,18 +1,153 @@ +/* The title of the button on the Default Browser Popup, which is a card with instructions telling the user how to set Firefox as their default browser. */ +"DefaultBrowserPopup.ButtonTitle.v114" = "Idi na postavke"; + +/* The footer label on the Default Browser Popup, which is below all the instructions asking the users if their Firefox browser is the default browser. If it is then close this message and tap skip. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"DefaultBrowserPopup.DescriptionFooter.v124" = "*Je li %@ već tvoj standardni preglednik?* Zatvori ovu poruku i dodirni gumb „Preskoči”."; + +/* The first label on the Default Browser Popup, which is a card with instructions telling the user how to set Firefox as their default browser. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"DefaultBrowserPopup.FirstLabel.v114" = "1. Idi na *Postavke*"; + +/* The second label on the Default Browser Popup, which is a card with instructions telling the user how to set Firefox as their default browser. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"DefaultBrowserPopup.SecondLabel.v114" = "2. Dodirni *Standardni preglednik*"; + +/* The third label on the Default Browser Popup, which is a card with instructions telling the user how to set Firefox as their default browser. Placeholder is the app name. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"DefaultBrowserPopup.ThirdLabel.v114" = "3. Odaberi *%@*"; + +/* The title on the Default Browser Popup, which is a card with instructions telling the user how to set Firefox as their default browser. */ +"DefaultBrowserPopup.Title.v114" = "Promijeni standardni preglednik"; + +/* String used to describe the option to continue to the next onboarding card in Firefox Onboarding screens. Placeholder is for the app name. */ +"Onboarding.Customization.Intro.Continue.Action.v123" = "Prilagodi %@"; + +/* String used to describe the description label of the customization onboarding page in our Onboarding screens. */ +"Onboarding.Customization.Intro.Description.v123" = "Postavi temu i alatnu traku tako da odgovaraju tvom jedinstvenom stilu pregledavanja."; + +/* String used to describe the option to skip the customization cards in Firefox Onboarding screens and start browsing. */ +"Onboarding.Customization.Intro.Skip.Action.v123" = "Počni pregledavati"; + +/* String used to describe the title of the customization onboarding page in our Onboarding screens. Placeholder is for the app name. */ +"Onboarding.Customization.Intro.Title.v123" = "%@ ti daje kontrolu"; + +/* String used to describe the option to save the user setting and continue to the next onboarding in Firefox Onboarding screens. */ +"Onboarding.Customization.Theme.Continue.Action.v123" = "Spremi i nastavi"; + +/* On the theme customization onboarding card, the string used to describe the option to set the theme to dark theme from the available choices. */ +"Onboarding.Customization.Theme.Dark.Action.v123" = "Tamna"; + +/* String used to describe the description label of the theme customization onboarding page in our Onboarding screens. */ +"Onboarding.Customization.Theme.Description.v123" = "Pogledaj web u najboljem svjetlu."; + +/* On the theme customization onboarding card, the string used to describe the option to set the theme to light theme from the available choices. */ +"Onboarding.Customization.Theme.Light.Action.v123" = "Svijetla"; + +/* String used to describe the option to skip the theme customization in Firefox Onboarding screens. */ +"Onboarding.Customization.Theme.Skip.Action.v123" = "Preskoči"; + +/* On the theme customization onboarding card, the string used to describe the option to set the theme to system theme from the available choices. */ +"Onboarding.Customization.Theme.System.Action.v123" = "Automatski od sustava"; + +/* String used to describe the title of the theme customization onboarding page in our Onboarding screens. */ +"Onboarding.Customization.Theme.Title.v123" = "Odaberi temu"; + +/* On the toolbar customization onboarding card, the string used to describe the option to set the toolbar at the bottom of the screen. */ +"Onboarding.Customization.Toolbar.Bottom.Action.v123" = "Dolje"; + +/* String used to describe the option to save set preferences and leave onboarding to start browsing in the app. */ +"Onboarding.Customization.Toolbar.Continue.Action.v123" = "Spremi i počni pregledavati"; + +/* String used to describe the description label of the toolbar customization onboarding page in our Onboarding screens. */ +"Onboarding.Customization.Toolbar.Description.v123" = "Zadrži pretrage nadohvat ruke."; + +/* String used to describe the option to skip the toolbar customization in Firefox Onboarding screens and start browisg in the app. */ +"Onboarding.Customization.Toolbar.Skip.Action.v123" = "Preskoči"; + +/* String used to describe the title of the toolbar customization onboarding page in our Onboarding screens. */ +"Onboarding.Customization.Toolbar.Title.v123" = "Odaberi položaj alatne trake"; + +/* On the toolbar customization onboarding card, the string used to describe the option to set the toolbar at the top of the screen. */ +"Onboarding.Customization.Toolbar.Top.Action.v123" = "Gore"; + +/* String used to describes what Firefox is on the first onboarding page in our Onboarding screens. Indie means small independant. */ +"Onboarding.IntroDescriptionPart1.v114" = "Neovisna. Neprofitna organizacija. Zauvijek."; + +/* String used to describes what Firefox is on the first onboarding page in our Onboarding screens. */ +"Onboarding.IntroDescriptionPart2.v114" = "Predani obećanju za bolji internet za sve."; + /* Describes an action on some of the Onboarding screen, including the wallpaper onboarding screen. This string will be on a button so user can skip that onboarding page. */ "Onboarding.LaterAction.v115" = "Preskoči"; /* String used to describe the option to continue to ask for the notification permission in Firefox Onboarding screens. */ "Onboarding.Notification.Continue.Action.v114" = "Nastavi"; +/* String used to describe the description of the notification onboarding page in our Onboarding screens. Placeholder is for the app name. */ +"Onboarding.Notification.Description.v120" = "Šalji kartice između svojih uređaja na siguran način i otkrij druge %@ funkcije privatnosti."; + /* String used to describe the option to skip the notification permission in Firefox Onboarding screens. */ "Onboarding.Notification.Skip.Action.v115" = "Preskoči"; +/* String used to describe the title of the notification onboarding page in our Onboarding screens. Placeholder is for the app name. */ +"Onboarding.Notification.Title.v120" = "Obavijesti pomažu da ostaneš sigurniji uz %@"; + +/* String used to describe the option to continue to ask for the notification permission in Firefox Onboarding screens. */ +"Onboarding.Notification.TurnOnNotifications.Action.v114" = "Uključi obavijesti"; + +/* String used to describes the description of what Firefox is on the Sync onboarding page for current version in our Onboarding screens. Placeholder is for the app name. */ +"Onboarding.Sync.Description.v120" = "Prijava i sinkronizacija povećavaju tvoju sigurnost. %@ šifrira tvoje lozinke, zabilješke i još više."; + +/* String used to describes the description of what Firefox is on the Sync onboarding page for current version in our Onboarding screens. Placeholder is for the app name. */ +"Onboarding.Sync.Description.v123" = "%@ šifrira tvoje lozinke, zabilješke i još više kada si sinkroniziran/a."; + /* String used to describes the option to skip the Sync sign in during onboarding for the current version in Firefox Onboarding screens. */ "Onboarding.Sync.SignIn.Action.v114" = "Prijava"; /* String used to describes the option to skip the Sync sign in during onboarding for the current version in Firefox Onboarding screens. */ "Onboarding.Sync.Skip.Action.v114" = "Preskoči"; +/* String used to describes the title of what Firefox is on the Sync onboarding page for current version in our Onboarding screens. */ +"Onboarding.Sync.Title.v120" = "Šifriraj podatke kada radiš na više uređaja"; + +/* Accessibility label for the wallpaper onboarding modal displayed on top of the homepage. This describes to the user that which type of wallpaper they are seeing. */ +"Onboarding.Wallpaper.Accessibility.Classic.v114" = "Klasična pozadina"; + +/* Accessibility label for the wallpaper onboarding modal displayed on top of the homepage. This describes to the user that which type of wallpaper they are seeing. */ +"Onboarding.Wallpaper.Accessibility.LimitedEdition.v114" = "Pozadina ograničenog izdanja"; + +/* Description for the wallpaper onboarding page in our Onboarding screens. This describes to the user that they can set a wallpaper. */ +"Onboarding.Wallpaper.Action.v114" = "Postavi pozadinu"; + +/* Description for the wallpaper onboarding modal displayed on top of the homepage. This describes to the user that they can choose different wallpapers. */ +"Onboarding.Wallpaper.Description.v114" = "Odaberi pozadinu koja ti odgovara."; + +/* Title for the wallpaper onboarding modal displayed on top of the homepage. This describes to the user that they can choose different wallpapers. */ +"Onboarding.Wallpaper.SelectorTitle.v114" = "Isprobaj jednu obojanu pozadinu"; + +/* Title for the wallpaper onboarding page in our Onboarding screens. This describes to the user that they can choose different wallpapers. Placeholder is for app name. */ +"Onboarding.Wallpaper.Title.v114" = "Odaberi %@ pozadinu"; + +/* Describes the action on the first onboarding page in our Onboarding screen. This string will be on a button so user can continue the onboarding. */ +"Onboarding.Welcome.Action.v114" = "Započni"; + +/* Describes the action on the first onboarding page in our Onboarding screen. This indicates that the user will set their default browser to Firefox. */ +"Onboarding.Welcome.ActionTreatementA.v114" = "Postavi kao standardni preglednik"; + +/* Accessibility label for close button that dismisses the welcome onboarding screen. Placeholder is for the app name. */ +"Onboarding.Welcome.Close.AccessibilityLabel.v121" = "Zatvori i izađi iz početnog %@ ekrana"; + +/* String used to describes the description of what Firefox is on the welcome onboarding page for current version in our Onboarding screens. */ +"Onboarding.Welcome.Description.TreatementA.v120" = "Naš preglednik neprofitne organizacije pomaže spriječiti poduzeća da te potajno prate na webu."; + +/* String used to describes the description of what Firefox is on the welcome onboarding page for current version in our Onboarding screens. Placeholder is for the app name. */ +"Onboarding.Welcome.Description.v120" = "Naš preglednik neprofitne organizacije pomaže spriječiti poduzeća da te potajno prate na webu."; + +/* String used to describe the title of link button is on the welcome onboarding page for current version in our Onboarding screens. */ +"Onboarding.Welcome.Link.Action.v114" = "Saznaj više u našoj napomeni o privatnosti"; + /* Describes the action on the first onboarding page in our Onboarding screen. This string will be on a button so user can skip this onboarding card. */ "Onboarding.Welcome.Skip.v114" = "Preskoči"; +/* String used to describes the title of what Firefox is on the welcome onboarding page for current version in our Onboarding screens. */ +"Onboarding.Welcome.Title.TreatementA.v120" = "Volimo te štititi"; + +/* String used to describes the title of what Firefox is on the welcome onboarding page for current version in our Onboarding screens. */ +"Onboarding.Welcome.Title.v114" = "Dobro došao, dobro došla u neovisni internet"; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/PasswordAutofill.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/PasswordAutofill.strings new file mode 100644 index 000000000000..f4b36a88fb3c --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/PasswordAutofill.strings @@ -0,0 +1,15 @@ +/* This label is used in a cell found in the list of autofill login options in place of an actual username to denote that no username was saved for this login */ +"PasswordAutofill.LoginListCellNoUsername.v129" = "(nema korisničkog imena)"; + +/* This label is used for a button in the password list screen allowing users to manage their saved passwords. It's meant to direct users to where they can add, remove, or edit their saved passwords. */ +"PasswordAutofill.ManagePasswordsButton.v124" = "Upravljaj lozinkama"; + +/* This phrase is used as a subtitle in the header of password list screen, indicating to the user that they will be logging into a specific website (represented by %@) using a saved password. It's providing clarity on which website the saved credentials apply to. */ +"PasswordAutofill.SignInWithSavedPassword.v124" = "Prijavit ćeš se na %@"; + +/* This label is used in the password list screen header as a question, prompting the user if they want to use a saved password for logging in. */ +"PasswordAutofill.UseSavedPasswordFromHeader.v124" = "Koristiti spremljenu lozinku?"; + +/* Displayed inside the keyboard hint when a user is entering their login credentials and has at least one saved password. Indicates that there are stored passwords available for use in filling out the login form. */ +"PasswordAutofill.UseSavedPasswordFromKeyboard.v124" = "Koristi spremljenu lozinku"; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/QRCode.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/QRCode.strings new file mode 100644 index 000000000000..f8caaf3e52a7 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/QRCode.strings @@ -0,0 +1,3 @@ +/* Accessibility label of the QR code button in the toolbar */ +"QRCode.Toolbar.Button.A11y.Title.v128" = "Snimi QR kod"; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/RememberCard.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/RememberCard.strings index 8401ad68f1cf..9043de801c2f 100644 --- a/firefox-ios/Shared/Supporting Files/hr.lproj/RememberCard.strings +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/RememberCard.strings @@ -1,3 +1,12 @@ +/* This value is used as the header for the remember card page. The placeholder is for the app name. */ +"CreditCard.RememberCard.Header.v122" = "%@ šifrira broj tvoje kreditne kartice. Tvoj sigurnosni kod se neće spremiti."; + +/* This value is used as the title for the Yes button in the remember credit card page */ +"CreditCard.RememberCard.MainButtonTitle.v122" = "Spremi"; + +/* This value is used as the title for the remember credit card page */ +"CreditCard.RememberCard.MainTitle.v122" = "Spremiti ovu kreditnu karticu na siguran način?"; + /* This value is used as the title for the Not Now button in the remember credit card page */ "CreditCard.RememberCard.SecondaryButtonTitle.v115" = "Ne sada"; diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/ResearchSurface.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/ResearchSurface.strings new file mode 100644 index 000000000000..a61e358c15b6 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/ResearchSurface.strings @@ -0,0 +1,9 @@ +/* On the Research Survey popup, the text that explains what the screen is about. Placeholder is for the app name. */ +"Body.Text.v112" = "Pomogni poboljšati %@ ispunjavanjem kratke ankete."; + +/* On the Research Survey popup, the text for the button that, when tapped, will dismiss the popup and take the user to a survey. */ +"PrimaryButton.Label.v112" = "Ispuni anketu"; + +/* On the Research Survey popup, the text for the button that, when tapped, will dismiss this screen, and the user will not be taken to the survey. */ +"SecondaryButton.Label.v112" = "Ne hvala"; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/ScanQRCode.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/ScanQRCode.strings new file mode 100644 index 000000000000..fbbf003e4b14 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/ScanQRCode.strings @@ -0,0 +1,9 @@ +/* Allow button to open URL from scanned QR Code */ +"ScanQRCode.ConfirmOpenURL.AllowButton.v129" = "Dozvoli"; + +/* Deny button to cancel opening URL from scanned QR Code */ +"ScanQRCode.ConfirmOpenURL.DenyButton.v129" = "Odbij"; + +/* Text of the prompt to ask user permission to open a URL from a scanned QR code. Placeholder is app name. */ +"ScanQRCode.ConfirmOpenURL.Message.v129" = "Dozvoliti da %@ otvori URL?"; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/SearchHeaderTitle.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/SearchHeaderTitle.strings index 0824f5f8108b..0f59f091a06a 100644 --- a/firefox-ios/Shared/Supporting Files/hr.lproj/SearchHeaderTitle.strings +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/SearchHeaderTitle.strings @@ -1,3 +1,6 @@ +/* When making a new search from the awesome bar, search results appear as the user write new letters in their search. Different sections with results from the selected search engine will appear. This string will be used as a header to separate the selected engine search results from current search query. */ +"Search.EngineSection.Title.v108" = "%@ pretraga"; + /* When making a new search from the awesome bar, search results appear as the user write new letters in their search. This string will be used as a header for Google search results listed as suggestions. */ "Search.Google.Title.v108" = "Google pretraživanje"; diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/SelectCreditCard.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/SelectCreditCard.strings index 6b8d086319e7..d35decf3ba91 100644 --- a/firefox-ios/Shared/Supporting Files/hr.lproj/SelectCreditCard.strings +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/SelectCreditCard.strings @@ -1,3 +1,3 @@ /* This value is used as the title for the select a credit card from list of available cards. */ -"CreditCard.SelectCreditCard.MainTitle.v116" = "Koristiti spremljenu karticu?"; +"CreditCard.SelectCreditCard.MainTitle.v122" = "Koristi spremljenu kreditnu karticu"; diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/Settings.strings index 31f48a41af2c..727a5fd18574 100644 --- a/firefox-ios/Shared/Supporting Files/hr.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/Settings.strings @@ -1,15 +1,162 @@ +/* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ +"Addresses.Settings.SavedAddressesSectionTitle.v124" = "SPREMLJENE ADRESE"; + +/* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ +"Addresses.Settings.Switch.Description.v124" = "Uključuje telefonske brojeve i e-mail adrese"; + +/* Title label for user to use the toggle settings to allow saving and autofilling of addresses for webpages. */ +"Addresses.Settings.Switch.Title.v124" = "Spremi i ispuni adrese"; + +/* Displayed inside the keyboard hint when a user is entering their address and has at least one saved address. Indicates that there are stored addresses available for use in filling out a form. */ +"Addresses.Settings.UseSavedAddressFromKeyboard.v124" = "Koristi spremljenu adresu"; + +/* Accessibility label for the add button in autofill settings screen. Pressing this button presents a modal that allows users to add a card by entering the credit card information. */ +"CreditCard.Settings.AddCard.AccessibilityLabel.v121" = "Dodaj kreditnu karticu"; + +/* Description label for when there are no credit cards shown in credit card list in autofill settings screen. */ +"CreditCard.Settings.EmptyListDescription.v112" = "Spremi podatke kreditne kartice na siguran način za brže plaćanje u budućnosti."; + +/* Title label for when there are no credit cards shown in credit card list in autofill settings screen. %@ is the product name and should not be altered. */ +"CreditCard.Settings.EmptyListTitle.v122" = "Spremi kreditne kartice na %@"; + +/* Accessibility label for a credit card list item in autofill settings screen. The first parameter is the credit card issuer (e.g. Visa). The second parameter is the name of the credit card holder. The third parameter is the last 4 digits of the credit card. The fourth parameter is the card's expiration date. */ +"CreditCard.Settings.ListItemA11y.v118" = "%1$@, izdano za %2$@, završava %3$@, isteče %4$@"; + +/* When a user is in the process or has finished making a purchase with a card not saved in Firefox's list of stored cards, we ask the user if they would like to save this card for future purchases. This string indicates to users that they can deny Firefox from remembering the card that is being used. */ +"CreditCard.Settings.NotNow.v122" = "Ne sada"; + +/* When a user is in the process or has finished making a purchase with a card not saved in Firefox's list of stored cards, we ask the user if they would like to save this card for future purchases. This string is a title string of the overall message that asks the user if they would like Firefox to remember the card that is being used. */ +"CreditCard.Settings.RememberThisCard.v122" = "Sigurno spremiti ovu kreditnu karticu?"; + +/* When a user is in the process or has finished making a purchase with a remembered card, and if the credit card information doesn't match the contents of the stored information of that card, we show this string. We ask this user if they would like Firefox update the staled information of that credit card. */ +"CreditCard.Settings.UpdateThisCard.v122" = "Aktualizirati kreditnu karticu?"; + +/* When a user is in the process or has finished making a purchase with a card not saved in Firefox's list of stored cards, we ask the user if they would like to save this card for future purchases. This string asks users to confirm if they would like Firefox to remember the card that is being used. */ +"CreditCard.Settings.Yes.v122" = "Aktualiziraj"; + /* When a user is in the process of making a purchase and has at least one saved credit card, a view above the keyboard shows actions a user can take. When tapping this label, the keyboard will dismiss from view. */ "CreditCards.Settings.Done.v114" = "Gotovo"; /* When a user is in the process or has finished making a purchase, and has at least one card saved, we show this tappable string. This indicates to users that they can navigate to their list of stored credit cards in the app's credit card list screen. */ "CreditCards.Settings.ManageCards.v112" = "Upravljaj karticama"; +/* When a user is in the process of making a purchase, and has at least one saved card, we show this label used as a title. This indicates to the user that there are stored cards available for use on this pending purchase. */ +"CreditCards.Settings.UseASavedCard.v122" = "Koristi spremljenu kreditnu karticu"; + /* When a user is in the process of making a purchase, and has at least one saved card, we show this label inside the keyboard hint. This indicates to the user that there are stored cards available for use on this pending purchase. */ "CreditCards.Settings.UseSavedCardFromKeyboard.v112" = "Koristi spremljenu karticu"; +/* Settings section title for the old Firefox account */ +"FxA.FirefoxAccount.v119" = "Račun"; + +/* Label used as an item in Settings screen. When touched, it will take user to address autofill settings page to that will allow user to add or modify saved addresses to allow for autofill in a webpage. */ +"Settings.AddressAutofill.Title.v124" = "Automatsko ispunjavanje adresa"; + +/* Label used as an item in Settings screen. When touched, it will take user to address autofill settings page to that will allow user to add or modify saved addresses to allow for autofill in a webpage. */ +"Settings.AddressAutofill.Title.v126" = "Adrese"; + +/* Label used as an item in Settings screen. When touched, it will take user to credit card settings page to that will allows to add or modify saved credit cards to allow for autofill in a webpage. */ +"Settings.AutofillCreditCard.Title.v122" = "Načini plaćanja"; + +/* Title displayed in header of the FxA settings panel. */ +"Settings.FxA.Title.v119" = "Račun"; + +/* This is the description for the setting that toggles Sync related notifications in the settings menu under the Notifications section. */ +"Settings.Notifications.SyncNotificationsStatus.v112" = "Ovo mora biti uključeno za primanje kartica i obavijest kada se prijaviš na jednom drugom uređaju."; + +/* This is the title for the setting that toggles Sync related notifications in the settings menu under the Notifications section. */ +"Settings.Notifications.SyncNotificationsTitle.v112" = "Sinkronizacija"; + +/* This is the footer title informing the user needs to turn on notifications in iOS Settings. Both placeholders will be replaced with the app name. */ +"Settings.Notifications.SystemNotificationsDisabledMessage.v112" = "Isključene su sve %1$@ obavijesti. Uključi ih na uređaju u izborniku Postavke > Obavijesti > %2$@"; + +/* This is the description for the setting that toggles Tips and Features feature in the settings menu under the Notifications section. The placeholder will be replaced with the app name. */ +"Settings.Notifications.TipsAndFeaturesNotificationsStatus.v112" = "Saznaj više o korisnim funkcijama i kako najbolje iskoristiti %@."; + /* This is the title for the setting that toggles Tips and Features feature in the settings menu under the Notifications section. */ "Settings.Notifications.TipsAndFeaturesNotificationsTitle.v112" = "Savjeti i značajke"; /* In the settings menu, in the Privacy section, this is the title for Notifications customization section. */ "Settings.Notifications.Title.v112" = "Obavijesti"; +/* This is the title informing the user needs to turn on notifications in iOS Settings. The placeholder will be replaced with the app name. */ +"Settings.Notifications.TurnOnNotificationsMessage.v112" = "Idi na postavke uređaja za uključivanje obavijesti u %@"; + +/* This is the title informing the user needs to turn on notifications in iOS Settings. */ +"Settings.Notifications.TurnOnNotificationsTitle.v112" = "Uključi obavijesti"; + +/* Accessibility label for default search engine setting. */ +"Settings.Search.Accessibility.DefaultSearchEngine.v121" = "Standardna tražilica"; + +/* Accessibility label for Learn more about Firefox Suggest. */ +"Settings.Search.Accessibility.LearnAboutSuggestions.v124" = "Saznaj više o funkciji Firefox prijedlozi"; + +/* Title for alternate search engines settings section in the Search page in the Settings menu. */ +"Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Alternativne tražilice"; + +/* Footer for the `default search engine` settings section in the Search Settings page, which explains in more details what the `Show Search Suggestions` setting includes. */ +"Settings.Search.DefaultSearchEngine.Footer.v122" = "Rezultati pretraživanja, povijesti, zabilješki i više"; + +/* Title for the `default search engine` settings section in the Search page in the Settings menu. */ +"Settings.Search.DefaultSearchEngine.Title.v121" = "Standardna tražilica"; + +/* Title for the `Suggestions from Search Engines` settings section in the Search page in the Settings menu. */ +"Settings.Search.EnginesSuggestions.Title.v124" = "Prijedlozi tražilica"; + +/* Navigation title for search page in the Settings menu. */ +"Settings.Search.PageTitle.v121" = "Traži"; + +/* Description for `Show in Private Sessions` toggle, located in `Suggestions from Search Engines` section in the Search page in the Settings menu. */ +"Settings.Search.PrivateSession.Description.v125" = "Prikaži prijedloge tražilica u privatnim sesijama"; + +/* Label for toggle. Explains that in private browsing mode, the search suggestions which appears at the top of the search bar, can be toggled on or off. Located in the Private Session section in the Search page in the Settings menu. */ +"Settings.Search.PrivateSession.Setting.v122" = "Prikaži prijedloge u privatnom pregledavanju"; + +/* Label for toggle. Explains that in private browsing mode, the search suggestions which appears at the top of the search bar, can be toggled on or off. Located in `Suggestions from Search Engines` and `Address Bar - Firefox Suggest` sections in the Search page in the Settings menu. */ +"Settings.Search.PrivateSession.Setting.v124" = "Prikaži u privatnim sesijama"; + +/* Title for the `Private Browsing` settings section in the Search page in the Settings menu. */ +"Settings.Search.PrivateSession.Title.v122" = "Privatno pregledavanje"; + +/* Title for quick-search engines settings section in the Search page in the Settings menu. */ +"Settings.Search.QuickEnginesTitle.v121" = "Tražilice za brzo pretraživanje"; + +/* Label for the `show search suggestions` setting, in the Search Settings page. */ +"Settings.Search.ShowSuggestions.v121" = "Prikaži prijedloge pretraživanja"; + +/* In the Search page of the Settings menu, the title for the Firefox Suggest settings section. */ +"Settings.Search.Suggest.AddressBarSetting.Title.v124" = "Adresna traka – Firefox prijedlozi"; + +/* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ +"Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Saznaj više o funkciji Firefox prijedlozi"; + +/* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Prikaži prijedloge od Firefox prijedlozi u privatnim sesijama"; + +/* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ +"Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Pretraži povijest pregledavanja"; + +/* In the Search page of the Settings menu, the title for the setting to enable search bookmarks. */ +"Settings.Search.Suggest.SearchSearchBookmarks.Title.v124" = "Pretraži zabilješke"; + +/* In the Search page of the Settings menu, the title for the setting to enable synced tabs. */ +"Settings.Search.Suggest.SearchSyncedTabs.Title.v124" = "Pretraži sinkronizirane kartice"; + +/* In the Search page of the Settings menu, the description for the setting to enable Suggestions from Firefox. */ +"Settings.Search.Suggest.ShowNonSponsoredSuggestions.Description.v124" = "Dobij prijedloge s weba koji se odnose na tvoju pretragu"; + +/* In the Search page of the Settings menu, the description for the setting to enable Suggestions from Firefox. Placeholder is for the app name - Firefox. */ +"Settings.Search.Suggest.ShowNonSponsoredSuggestions.Description.v124.v2" = "Dobij prijedloge od %@a koji se odnose na tvoju pretragu"; + +/* In the Search page of the Settings menu, the title for setting to enable Suggestions from Firefox. Placeholder is for the app name - Firefox. */ +"Settings.Search.Suggest.ShowNonSponsoredSuggestions.Title.v124" = "Prijedlozi od %@"; + +/* In the Search page of the Settings menu, the title for setting to enable Suggestions from the web in Firefox. */ +"Settings.Search.Suggest.ShowNonSponsoredSuggestions.Title.v124.v2" = "Prijedlozi od weba"; + +/* In the Search page of the Settings menu, the description for the setting to enable Suggestions from sponsors. Placeholder is for the app name - Firefox. */ +"Settings.Search.Suggest.ShowSponsoredSuggestions.Description.v124" = "Podrži %@ s povremenim sponzoriranim prijedlozima"; + +/* In the Search page of the Settings menu, the title for the setting to enable Suggestions from sponsors. */ +"Settings.Search.Suggest.ShowSponsoredSuggestions.Title.v124" = "Prijedlozi od sponzora"; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/Share.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/Share.strings new file mode 100644 index 000000000000..fd441246a660 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/Share.strings @@ -0,0 +1,6 @@ +/* Error message shown in the remote tabs panel */ +"SendTo.NoDevicesFound.Message.v119" = "Nemaš nijedan drugi uređaj povezan s ovim računom koji je dostupan za sinkronizaciju."; + +/* This message appears when a user tries to use 'Send Link to Device' action while not logged in */ +"SendTo.NotSignedIn.Title.v119" = "Nisi prijavljen/a na svoj račun."; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/Shopping.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/Shopping.strings new file mode 100644 index 000000000000..5a32bbdcb960 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/Shopping.strings @@ -0,0 +1,255 @@ +/* Contextual hints are little popups that appear for the users informing them of new features. This one indicates that a user can tap on the shopping button to start using the Shopping feature. */ +"ContextualHints.Shopping.NotOptedIn.v120" = "Saznaj možeš li vjerovati recenzijama ovog proizvoda – prije kupnje."; + +/* Contextual hints are little popups that appear for the users informing them of new features. This one is a call to action for the popup describing the Shopping feature. It indicates that a user can go directly to the Shopping feature by tapping the text of the action. */ +"ContextualHints.Shopping.NotOptedInAction.v120" = "Probaj provjeru recenzija"; + +/* Contextual hints are little popups that appear for the users informing them of new features. This is a call to action for the popup that appears after the user has opted in for the Shopping feature. It indicates that a user can directly open the review checker by tapping the text of the action. */ +"ContextualHints.Shopping.OptedInAction.v120" = "Otvori provjeru recenzija"; + +/* Contextual hints are little popups that appear for the users informing them of new features. This one appears after the user has opted in and informs him if he wants use the review checker by tapping the Shopping button. */ +"ContextualHints.Shopping.OptedInBody.v120" = "Jesu li ove recenzije pouzdane? Provjeri sada za prikaz prilagođene ocjene."; + +/* Footer label from the Fakespot Ad card displayed for the related product we advertise. This is displayed below the ad card, suggested as an alternative to the product reviewed. The first parameter will be replaced by the Fakespot app name */ +"Shopping.AdCard.Footer.v121" = "Oglas od %@"; + +/* Title label for the Fakespot Ad card. This is displayed above a product image, suggested as an alternative to the product reviewed. */ +"Shopping.AdCard.Title.v121" = "Alternativni proizvodi"; + +/* Description adjusted of the rating card displayed in the shopping review quality bottom sheet. */ +"Shopping.AdjustedRating.Description.v121" = "Na temelju pouzdanih recenzija"; + +/* Accessibility label, associated to adjusted rating stars. %@ is a decimal value from 0 to 5 that will only use a tenth (example: 3.5). */ +"Shopping.AdjustedRating.StarsAccessibilityLabel.v120" = "%@ od 5 zvjezdica"; + +/* Title of the adjusted rating card displayed in the shopping review quality bottom sheet. */ +"Shopping.AdjustedRating.Title.v120" = "Prilagođena ocjena"; + +/* Button text of the confirmation displayed in the shopping review quality bottom sheet. */ +"Shopping.ConfirmationCard.Button.Text.v120" = "Razumijem"; + +/* Title of the confirmation displayed in the shopping review quality bottom sheet. */ +"Shopping.ConfirmationCard.Title.v120" = "Analiza je aktualna"; + +/* Section title of the review highlights displayed in the shopping review quality bottom sheet. */ +"Shopping.HighlightsCard.Competitiveness.Title.v120" = "Konkurentnost"; + +/* Title of the button that shows less reviews in the review highlights displayed in the shopping review quality bottom sheet. */ +"Shopping.HighlightsCard.LessButton.Title.v120" = "Prikaži manje"; + +/* Title of the button that shows more reviews in the review highlights displayed in the shopping review quality bottom sheet. */ +"Shopping.HighlightsCard.MoreButton.Title.v120" = "Prikaži više"; + +/* Section title of the review highlights displayed in the shopping review quality bottom sheet, specifically focusing on the quality, design, and condition of the product's packaging. This may include details about the box, protective materials, presentation, and overall packaging experience. */ +"Shopping.HighlightsCard.Packaging.Title.v120" = "Ambalaža"; + +/* Section title of the review highlights displayed in the shopping review quality bottom sheet. */ +"Shopping.HighlightsCard.Price.Title.v120" = "Cijena"; + +/* Section title of the review highlights displayed in the shopping review quality bottom sheet. */ +"Shopping.HighlightsCard.Quality.Title.v120" = "Kvaliteta"; + +/* Section title of the review highlights displayed in the shopping review quality bottom sheet. */ +"Shopping.HighlightsCard.Shipping.Title.v120" = "Dostava"; + +/* Title of the review highlights displayed in the shopping review quality bottom sheet. */ +"Shopping.HighlightsCard.Title.v120" = "Izdvajamo iz nedavnih recenzija"; + +/* Title for info card when Fakespot cannot analyze reviews for a certain product type */ +"Shopping.InfoCard.FakespotDoesNotAnalyzeReviews.Description.v120" = "Nažalost, ne možemo provjeriti kvalitetu recenzija za određene vrste proizvoda. Na primjer, poklon kartice i streaming videa, glazbe i igara."; + +/* Title for info card when Fakespot cannot analyze reviews for a certain product type */ +"Shopping.InfoCard.FakespotDoesNotAnalyzeReviews.Title.v120" = "Ne možemo provjeriti ove recenzije"; + +/* Description text for an information card used in the review checker section. This message is displayed when the reviews for a product are not yet available but are expected to be provided within the next 24 hours. It serves to inform users of the short wait for reviews and encourages them to return soon for the updated information. */ +"Shopping.InfoCard.InfoComingSoon.Description.v121" = "Trebali bismo imati informacije o recenzijama ovog proizvoda u roku od 24 sata. Navrati kasnije."; + +/* Title for an information card that is displayed in the review checker section when certain details about a product or feature are not currently available but are expected to be provided soon. The message should imply that the user can look forward to receiving more information shortly. */ +"Shopping.InfoCard.InfoComingSoon.Title.v121" = "Informacije stižu uskoro"; + +/* Primary action title for info card when the product needs analysis */ +"Shopping.InfoCard.NeedsAnalysis.PrimaryAction.v120" = "Provjeri sada"; + +/* Title for info card when the product needs analysis */ +"Shopping.InfoCard.NeedsAnalysis.Title.v120" = "Nove informacije za provjeru"; + +/* Description for info card when no information is available at the moment */ +"Shopping.InfoCard.NoInfoAvailableRightNow.Description.v120" = "Radimo na rješavanju ovog problema. Navrati malo kasnije."; + +/* Title for info card when no information is available at the moment */ +"Shopping.InfoCard.NoInfoAvailableRightNow.Title.v120" = "Trenutačno nema informacija"; + +/* Description for info card when there are not enough reviews for a product */ +"Shopping.InfoCard.NotEnoughReviews.Description.v120" = "Kad ovaj proizvod bude imao više recenzija, moći ćemo provjeriti njegovu kvalitetu."; + +/* Title for info card when there are not enough reviews for a product */ +"Shopping.InfoCard.NotEnoughReviews.Title.v120" = "Još nema dovoljno recenzija"; + +/* Description for the information card displayed by the review checker feature when the product the user is looking at is out of stock. This description is used for info card where the user can report if it's back in stock. */ +"Shopping.InfoCard.ProductNotInStock.Description.v121" = "Ako vidiš da je ovaj proizvod ponovo na zalihi, prijavi ga i mi ćemo provjeriti recenzije."; + +/* Primary action label for the information card displayed by the review checker feature when the product the user is looking at is out of stock. This primary action label is used for info card button where the user can report if it's back in stock. */ +"Shopping.InfoCard.ProductNotInStock.PrimaryAction.v121" = "Prijavi proizvod koji je ponovo na zalihi"; + +/* Title for the information card displayed by the review checker feature when the product the user is looking at is out of stock. This title is used for info card where the user can report if it's back in stock. */ +"Shopping.InfoCard.ProductNotInStock.Title.v121" = "Proizvod nije dostupan"; + +/* Description for info card when the product is in analysis mode */ +"Shopping.InfoCard.ProgressAnalysis.Description.v120" = "To bi moglo potrajati oko 60 sekundi."; + +/* Title for info card when the product is in analysis mode */ +"Shopping.InfoCard.ProgressAnalysis.Title.v120" = "Provjera kvalitete recenzije"; + +/* Title for info card when the product is in analysis mode. The placeholder represents the percentage of the analysis progress, ranging between 1 and 100. */ +"Shopping.InfoCard.ProgressAnalysis.Title.v123" = "Provjera kvalitete recenzije (%@)"; + +/* This description appears beneath the confirmation title on the information card to inform the user that their report regarding the product stock status has been received and is being processed. It serves to set the expectation that the review information will be updated within 24 hours and invites the user to revisit the product page for updates. */ +"Shopping.InfoCard.ReportSubmittedByCurrentUser.Description.v121" = "Trebali bismo imati informacije o recenzijama ovog proizvoda u roku od 24 sata. Navrati kasnije."; + +/* This title is displayed on the information card as a confirmation message after a user reports that a previously out-of-stock product is now available. It's meant to acknowledge the user's contribution and encourage community engagement by letting them know their report has been successfully submitted. */ +"Shopping.InfoCard.ReportSubmittedByCurrentUser.Title.v121" = "Hvala na prijavi!"; + +/* Text for the analyzer button displayed when an analysis can be updated for a product. */ +"Shopping.NoAnalysisCard.AnalyzerButton.Title.v120" = "Provjeri kvalitetu recenzija"; + +/* Text for the body label, to check the reliability of a product. */ +"Shopping.NoAnalysisCard.BodyLabel.Title.v120" = "Za saznavanje o pouzdanosti recenzija ovog proizvoda provjeri kvalitetu recenzije. Traje samo nekih 60 sekundi."; + +/* Title for card displayed when a shopping product has not been analysed yet. */ +"Shopping.NoAnalysisCard.HeadlineLabel.Title.v120" = "Još nema informacija o ovim recenzijama"; + +/* Text for the disclaimer that appears underneath the rating image of the Shopping Experience Opt In onboarding Card (Fakespot). The parameter will be replaced by the Fakespot app name. After the colon, what appears are two links, each on their own line. The first link is to a Privacy policy. The second link is to Terms of use. */ +"Shopping.OptInCard.Disclaimer.Text.v120" = "Odabirom „Da, isprobaj” pristaješ na sljedeće od %@:"; + +/* Text for the disclaimer that appears underneath the rating image of the Shopping Experience Opt In onboarding Card (Fakespot). After the colon, there will be two links, each on their own line. The first link is to a Privacy policy. The second link is to Terms of use. */ +"Shopping.OptInCard.Disclaimer.Text.v123" = "Odabirom „Da, isprobaj” pristaješ na:"; + +/* Label for the first paragraph of the Shopping Experience Opt In onboarding Card (Fakespot). The first parameter will be the website the user is coming from when viewing this screen (default Amazon). The second parameter will be replaced by the app name. This string is almost identical with 'Shopping.OptInCard.FirstParagraph.Description', but without Best Buy and Walmart websites, which are not available in many locales. */ +"Shopping.OptInCard.FirstParagraph.AmazonOnly.Description.v122" = "Prije kupovine pogledaj koliko su pouzdane recenzije proizvoda na %1$@. Provjera recenzija, eksperimentalna %2$@ funkcija, ugrađena direktno u preglednik."; + +/* Label for the first paragraph of the Shopping Experience Opt In onboarding Card (Fakespot). The first parameter will be the website the user is coming from when viewing this screen (default Amazon). The second parameter will be replaced by the app name. The third and fourth parameters will be the other two websites that are currently supported (Amazon, Best Buy or Walmart) besides the one used for the first parameter. */ +"Shopping.OptInCard.FirstParagraph.Description.v120" = "Prije kupovine pogledaj koliko su pouzdane recenzije proizvoda na %1$@. Provjera recenzija, eksperimentalna %2$@ funkcija, ugrađena direktno u preglednik. Radi i na %3$@ te %4$@ stranicama."; + +/* Label for the header of the Shopping Experience Opt In onboarding Card (Fakespot) */ +"Shopping.OptInCard.HeaderLabel.Title.v120" = "Isprobaj naš pouzdani vodič za recenzije proizvoda"; + +/* Label for the Learn more button in the Shopping Experience Opt In onboarding Card (Fakespot) */ +"Shopping.OptInCard.LearnMoreButtonTitle.Title.v120" = "Saznaj više"; + +/* Text for the main button of the Shopping Experience Opt In onboarding Card (Fakespot) */ +"Shopping.OptInCard.MainButton.Title.v120" = "Da, probaj"; + +/* Show Firefox Browser Privacy Policy page from the Privacy section in the Shopping Experience Opt In onboarding Card (Fakespot). See https://www.mozilla.org/privacy/firefox/ */ +"Shopping.OptInCard.PrivacyPolicy.Button.Title.v120" = "Politika privatnosti"; + +/* Show Firefox Browser Privacy Policy page from the Privacy section in the Shopping Experience Opt In onboarding Card (Fakespot). The parameter will be replace by the Firefox app name. */ +"Shopping.OptInCard.PrivacyPolicy.Button.Title.v123" = "%@ politika privatnosti"; + +/* Text for the secondary button of the Shopping Experience Opt In onboarding Card (Fakespot) */ +"Shopping.OptInCard.SecondaryButton.Title.v120" = "Ne sada"; + +/* Label for the second paragraph of the Shopping Experience Opt In onboarding Card (Fakespot). The first parameter will be replaced by the Fakespot app name. The second parameter will be replaced the company name of Mozilla. */ +"Shopping.OptInCard.SecondParagraph.Description.v120" = "Koristeći %1$@ moć od %2$@ pomažemo ti da izbjegneš pristrane i neautentične recenzije. Naš model UI-a se stalno poboljšava kako bi te zaštitio dok kupuješ."; + +/* Show Firefox Browser Terms of Use page from the Privacy section in the Shopping Experience Opt In onboarding Card (Fakespot). See https://www.mozilla.org/privacy/firefox/ */ +"Shopping.OptInCard.TermsOfUse.Button.Title.v120" = "Uvjeti korištenja"; + +/* Show Fakespot Terms of Use page in the Shopping Experience Opt In onboarding Card (Fakespot). The parameter will be replace by the Fakespot name. */ +"Shopping.OptInCard.TermsOfUse.Button.Title.v123" = "%@ uvjeti korištenja"; + +/* Accessibility label for the Grade labels used in 'How we determine review quality' card and 'How reliable are these reviews' card displayed in the shopping review quality bottom sheet. The placeholder will be replaced by a grade letter (e.g. A). The grading system contains letters from A-F. */ +"Shopping.ReliabilityScore.Grade.A11y.Label.v120" = "Ocjena %@"; + +/* Title of the reliability card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQuality.ReliabilityCardTitle.v120" = "Koliko su te recenzije pouzdane?"; + +/* Description of the reliability ratings for rating 'A' and 'B' displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQuality.ReliabilityRating.AB.Description.v120" = "Pouzdane recenzije"; + +/* Description of the reliability rating 'C' displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQuality.ReliabilityRating.C.Description.v120" = "Mješavina pouzdanih i nepouzdanih recenzija"; + +/* Description of the reliability ratings for rating 'D' and 'F' displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQuality.ReliabilityRating.DF.Description.v120" = "Nepouzdane recenzije"; + +/* Adjusted rating label from How we determine review quality card displayed in the shopping review quality bottom sheet. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"Shopping.ReviewQualityCard.AdjustedRating.Label.v120" = "*Prilagođena ocjena* se temelji samo na recenzijama za koje vjerujemo da su pouzdane."; + +/* Accessibility label for the up chevron, from 'How we determine review quality' card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.Collapse.AccessibilityLabel.v120" = "Sklopi karticu „Kako određujemo kvalitetu recenzija”"; + +/* Accessibility label for the down chevron, from 'How we determine review quality' card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.Expand.AccessibilityLabel.v120" = "Rasklopi karticu „Kako određujemo kvalitetu recenzija”"; + +/* Label of the headline from How we determine review quality card displayed in the shopping review quality bottom sheet. The first parameter will be replaced by the Fakespot app name and the second parameter by the company name of Mozilla. */ +"Shopping.ReviewQualityCard.Headline.Label.v120" = "Koristimo %1$@ UI tehnologiju od %2$@ za provjeru pouzdanosti recenzija proizvoda. To će ti pomoći procijeniti samo kvalitetu recenzija, ne kvalitetu proizvoda."; + +/* Highlights label from How we determine review quality card displayed in the shopping review quality bottom sheet. The parameter substitutes the partner website the user is coming from. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"Shopping.ReviewQualityCard.Highlights.Label.v120" = "*Istaknuto* rezultira od %1@ recenzija u zadnjih 80 dana za koje vjerujemo da su pouzdane."; + +/* Highlights label from How we determine review quality card displayed in the shopping review quality bottom sheet. The parameter substitutes the partner website the user is coming from. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"Shopping.ReviewQualityCard.Highlights.Label.v126" = "*Istaknuto* rezultira od %@ recenzija u zadnjih 80 dana za koje vjerujemo da su pouzdane."; + +/* Title of the 'How we determine review quality' card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.Label.Title.v120" = "Kako određujemo kvalitetu recenzija"; + +/* The title of the learn more button from How we determine review quality card displayed in the shopping review quality bottom sheet. The placeholder will be replaced with the Fakespot app name. */ +"Shopping.ReviewQualityCard.LearnMoreButton.Title.v120" = "Saznaj više o tome kako %@ određuje kvalitetu recenzija"; + +/* Mixed reviews label from How we determine review quality card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.MixedReviews.Label.v120" = "Vjerujemo da postoji mješavina pouzdanih i nepouzdanih recenzija"; + +/* Reliable reviews label from How we determine review quality card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.ReliableReviews.Label.v120" = "Pouzdane recenzije. Vjerujemo da su recenzije vjerojatno od stvarnih kupaca koji su ostavili iskrene, nepristrane recenzije."; + +/* Label of the sub headline from How we determine review quality card displayed in the shopping review quality bottom sheet. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"Shopping.ReviewQualityCard.SubHeadline.Label.v120" = "Svakoj recenziji proizvoda dodjeljujemo *slovnu ocjenu* od A do F."; + +/* Unreliable reviews label from How we determine review quality card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.UnreliableReviews.Label.v120" = "Nepouzdane recenzije. Vjerujemo da su recenzije vjerojatno lažne ili od pristranih recenzenata."; + +/* Accessibility label for the up chevron icon used to collapse or minimize the Settings Card within the shopping product review bottom sheet. */ +"Shopping.SettingsCard.Collapse.AccessibilityLabel.v120" = "Sklopi karticu postavki"; + +/* Accessibility label for the down chevron icon used to expand or show the details of the Settings Card within the shopping product review bottom sheet. */ +"Shopping.SettingsCard.Expand.AccessibilityLabel.v120" = "Rasklopi karticu postavki"; + +/* Accessibility hint for the recommended products label and switch, grouped together. When the group is selected in VoiceOver mode, the hint is read to help the user understand what action can be performed. */ +"Shopping.SettingsCard.Expand.GroupedRecommendedProductsAndSwitch.AccessibilityHint.v123" = "Dodirni dvaput za mijenjanje postavke."; + +/* Accessibility label for the recommended products label and switch, grouped together. The first placeholder is for the recommended products label, and the second placeholder is for the state of the switch: On/Off. */ +"Shopping.SettingsCard.Expand.GroupedRecommendedProductsAndSwitch.AccessibilityLabel.v123" = "%1$@, gumb za prebacivanje %2$@."; + +/* Action title of the footer underneath the Settings Card displayed in the shopping review quality bottom sheet. The first parameter will be replaced by the Fakespot app name and the second parameter by the company name of Mozilla. */ +"Shopping.SettingsCard.Footer.Action.v120" = "Provjeru recenzija pokreće %1$@ od %2$@"; + +/* Title of the settings card displayed in the shopping review quality bottom sheet. */ +"Shopping.SettingsCard.Label.Title.v120" = "Postavke"; + +/* Label of the switch from settings card displayed in the shopping review quality bottom sheet. The placeholder will be replaced with the app name. */ +"Shopping.SettingsCard.RecommendedProducts.Label.v120" = "Prikaži proizvode koje preporučuje %@"; + +/* Toggled Off accessibility switch value from Settings Card within the shopping product review bottom sheet. */ +"Shopping.SettingsCard.SwitchValueOff.AccessibilityLabel.v123" = "Isključeno"; + +/* Toggled On accessibility value, from Settings Card within the shopping product review bottom sheet. */ +"Shopping.SettingsCard.SwitchValueOn.AccessibilityLabel.v123" = "Uključeno"; + +/* Label of the button from settings card displayed in the shopping review quality bottom sheet. */ +"Shopping.SettingsCard.TurnOffButton.Title.v120" = "Isključi provjeru recenzija"; + +/* Beta label for the header of the Shopping Experience (Fakespot) sheet */ +"Shopping.Sheet.Beta.Title.v120" = "BETA"; + +/* Accessibility label for close button that dismisses the Shopping Experience (Fakespot) sheet. */ +"Shopping.Sheet.Close.AccessibilityLabel.v121" = "Zatvori provjeru recenzija"; + +/* Label for the header of the Shopping Experience (Fakespot) sheet */ +"Shopping.Sheet.Title.v120" = "Provjera recenzija"; + +/* Text for body of error card displayed to the user when the device is disconnected from the network. */ +"Shopping.WarningCard.CheckNoConnection.Description.v120" = "Provjeri svoju mrežnu vezu i pokušaj ponovo učitati stranicu."; + +/* Title for error card displayed to the user when the device is disconnected from the network. */ +"Shopping.WarningCard.CheckNoConnection.Title.v120" = "Nema mrežne veze"; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/SnackBar.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/SnackBar.strings index 50dc8d4ab2d6..26d62b8858c7 100644 --- a/firefox-ios/Shared/Supporting Files/hr.lproj/SnackBar.strings +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/SnackBar.strings @@ -4,3 +4,6 @@ /* Label text that gets presented as a confirmation at the bottom of screen when credit card information gets saved successfully */ "CreditCard.SnackBar.SavedCardLabel.v112" = "Nova kartica spremljena"; +/* Label text that gets presented as a confirmation at the bottom of screen when credit card information gets updated successfully */ +"CreditCard.SnackBar.UpdatedCardLabel.v122" = "Podaci kreditne kartice su aktulizirani"; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/TabLocation.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/TabLocation.strings new file mode 100644 index 000000000000..6d59a3af9a10 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/TabLocation.strings @@ -0,0 +1,36 @@ +/* Accessibility hint for the reload button */ +"Address.Bar.Reload.A11y.Hint.v124" = "Dodirni dvaput i drži pritisnuto za više opcija"; + +/* Accessibility label for the security icon in url bar */ +"TabLocation.ETP.Off.NotSecure.A11y.Label.v119" = "Veza nije sigurna. Poboljšana zaštita od praćenja je isključena."; + +/* Accessibility label for the security icon in url bar */ +"TabLocation.ETP.Off.Secure.A11y.Label.v119" = "Veza nije sigurna. Poboljšana zaštita od praćenja je isključena."; + +/* Accessibility label for the security icon in url bar */ +"TabLocation.ETP.On.NotSecure.A11y.Label.v119" = "Veza nije sigurna"; + +/* Accessibility label for the security icon in url bar */ +"TabLocation.ETP.On.Secure.A11y.Label.v119" = "Sigurna veza"; + +/* Accessibility label for the lock / tracking protection button on the URL bar */ +"TabLocation.LockButton.AccessibilityLabel.v122" = "Zaštita od praćenja"; + +/* Large content title for the lock button. This title is displayed when accessible font sizes are enabled */ +"TabLocation.LockButton.LargeContentTitle.v122" = "Zaštita od praćenja"; + +/* Accessibility label for the share button in url bar */ +"TabLocation.Share.A11y.Label.v119" = "Dijeli ovu stranicu"; + +/* Large content title for the share button. This title is displayed when using accessible font sizes is enabled */ +"TabLocation.ShareButton.AccessibilityLabel.v122" = "Dijeli"; + +/* Accessibility label for the shopping button in url bar */ +"TabLocation.Shopping.A11y.Label.v120" = "Provjera recenzija"; + +/* Large content title for the tabs button. The argument is the number of open tabs or an infinity symbol. This title is displayed when using accessible font sizes is enabled. */ +"TabsButton.Accessibility.LargeContentTitle.v122" = "Prikaži kartice: %@"; + +/* Large content title for the button shown in editing mode to remove this site from the top sites panel. */ +"TopSites.RemoveButton.LargeContentTitle.v122" = "Ukloni stranicu"; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/TabToolbar.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/TabToolbar.strings new file mode 100644 index 000000000000..d49b69be89be --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/TabToolbar.strings @@ -0,0 +1,3 @@ +/* Accessibility label for the tab toolbar fire button in private mode, used to provide users a way to end and delete their private session data. */ +"TabToolbar.Accessibility.DataClearance.v122" = "Brisanje podataka"; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/TabsTray.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/TabsTray.strings index 68ddcb222ea4..d5f08de5dbf3 100644 --- a/firefox-ios/Shared/Supporting Files/hr.lproj/TabsTray.strings +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/TabsTray.strings @@ -4,6 +4,15 @@ /* When the user closes an individual tab in the tab tray, a popup will appear informing them the tab was closed. This is the text for the popup. */ "CloseTabsToast.SingleTabTitle.v113" = "Kartica zatvorena"; +/* When the user closes tabs in the tab tray, a popup will appear informing them how many tabs were closed. This is the text for the popup. The placeholder is the number of tabs */ +"CloseTabsToast.Title.v113" = "Zatvorene kartice: %d"; + /* This is the swipe action title for closing an inactive tab by swiping, located in the Inactive Tabs section of the Tabs Tray */ "InactiveTabs.TabTray.CloseSwipeActionTitle.v115" = "Zatvori"; +/* Users can disable syncing tabs from other devices. In the Sync Tabs panel of the Tab Tray, we inform the user tab syncing can be switched back on to view those tabs. */ +"TabsTray.Sync.SyncTabsDisabled.v116" = "Uključi sinkronizaciju kartica za pregled popisa kartica s tvojih drugih uređaja."; + +/* Button label to sync tabs in your account */ +"TabsTray.SyncTabs.SyncTabsButton.Title.v119" = "Sinkroniziraj kartice"; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/Toolbar.strings new file mode 100644 index 000000000000..82bfe2ebacda --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Nova kartica"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Zatvori ovu karticu"; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/ToolbarLocation.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/ToolbarLocation.strings new file mode 100644 index 000000000000..e3d1e903eca8 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/ToolbarLocation.strings @@ -0,0 +1,6 @@ +/* Contextual hints are little popups that appear for the users informing them of new features. This one indicates a user can navigate to the Settings page to move the search bar to the top. */ +"ContextualHints.Toolbar.Bottom.Description.v107" = "Ako želiš, pomakni alatnu traku gore."; + +/* Contextual hints are little popups that appear for the users informing them of new features. This one indicates a user can navigate to the Settings page to move the search bar to the bottom. */ +"ContextualHints.Toolbar.Top.Description.v107" = "Ako želiš, pomakni alatnu traku dolje."; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/UpdateCard.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/UpdateCard.strings index 3983b3a77c6c..da633dbbfb1b 100644 --- a/firefox-ios/Shared/Supporting Files/hr.lproj/UpdateCard.strings +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/UpdateCard.strings @@ -1,6 +1,15 @@ +/* This value is used as the toast message for the saving success alert in the remember credit card page */ +"CreditCard.RememberCard.SecondaryButtonTitle.v116" = "Podaci kreditne kartice su aktulizirani"; + +/* This value is used as the title for the update card page */ +"CreditCard.UpdateCard.MainTitle.v122" = "Aktualizirati kreditnu karticu?"; + /* This value is used as the title for the Manage Cards button from the update credit card page */ "CreditCard.UpdateCard.ManageCardsButtonTitle.v115" = "Upravljaj karticama"; /* This value is used as the title for the Not Now button in the update credit card page */ "CreditCard.UpdateCard.NotNowButtonTitle.v115" = "Ne sada"; +/* This value is used as the title for the button in the update credit card page. It indicates the action to update the details f9 the card. */ +"CreditCard.UpdateCard.YesButtonTitle.v122" = "Aktualiziraj"; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/Upgrade.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/Upgrade.strings index 834c9e1e9820..d52febc21c54 100644 --- a/firefox-ios/Shared/Supporting Files/hr.lproj/Upgrade.strings +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/Upgrade.strings @@ -1,6 +1,18 @@ /* Describes an action on the sync upgrade page in our Upgrade screens. This string will be on a button so user can sign up or login directly in the upgrade. */ "Upgrade.SyncSign.Action.v114" = "Prijavi se"; +/* Description string used to sign in to sync in the Upgrade screens. This screen is shown after user upgrades Firefox version. */ +"Upgrade.SyncSign.Description.v114" = "Nastavi gdje si stao/la s karticama s drugih uređaja na tvojoj početnoj stranici."; + +/* Title string used to sign in to sync in the Upgrade screens. This screen is shown after user upgrades Firefox version. */ +"Upgrade.SyncSign.Title.v114" = "Mijenjanje ekrana nikada nije bilo lakše"; + /* Describes the action on the first upgrade page in the Upgrade screen. This string will be on a button so user can continue the Upgrade. */ "Upgrade.Welcome.Action.v114" = "Postavi kao zadani preglednik"; +/* Description string used to welcome back users in the Upgrade screens. This screen is shown after user upgrades Firefox version. */ +"Upgrade.Welcome.Description.v114" = "Nove boje. Nova pogodnost. Ista predanost ljudima prije profita."; + +/* Title string used to welcome back users in the Upgrade screens. This screen is shown after user upgrades Firefox version. */ +"Upgrade.Welcome.Title.v114" = "Dobro došao, dobro došla u osobniji internet"; + diff --git a/firefox-ios/Shared/Supporting Files/hr.lproj/ZoomPageBar.strings b/firefox-ios/Shared/Supporting Files/hr.lproj/ZoomPageBar.strings new file mode 100644 index 000000000000..6f570b6cf2cc --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hr.lproj/ZoomPageBar.strings @@ -0,0 +1,12 @@ +/* Accessibility label for closing the zoom panel in Zoom Page Bar */ +"Menu.ZoomPage.Close.AccessibilityLabel.v113" = "Zatvori ploču zumiranja"; + +/* Accessibility label for current zoom level in Zoom Page Bar. The placeholder represents the zoom level */ +"Menu.ZoomPage.CurrentZoomLevel.AccessibilityLabel.v113" = "Trenutačna razina zumiranja: %@"; + +/* Accessibility label for decreasing the zoom level in Zoom Page Bar */ +"Menu.ZoomPage.DecreaseZoom.AccessibilityLabel.v113" = "Smanji razinu zumiranja"; + +/* Accessibility label for increasing the zoom level in Zoom Page Bar */ +"Menu.ZoomPage.IncreaseZoom.AccessibilityLabel.v113" = "Povećaj razinu zumiranja"; + diff --git a/firefox-ios/Shared/Supporting Files/hsb.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/hsb.lproj/EditAddress.strings index 10831add7c37..6be3e86aa9bf 100644 --- a/firefox-ios/Shared/Supporting Files/hsb.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/hsb.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "Adresa njeda so składować"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Adresa njeda so wotstronić"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Hišće raz spytać"; diff --git a/firefox-ios/Shared/Supporting Files/hsb.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/hsb.lproj/EnhancedTrackingProtection.strings index 2143fd9c986d..0ffc291916f7 100644 --- a/firefox-ios/Shared/Supporting Files/hsb.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/hsb.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Zwisk njewěsty"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Žane přesćěhowaki namakane"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Sydła přesahowace slědowace placki: %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Přesćěhowaki socialnych medijow: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Přesćěhowaki zablokowane: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Sće škity znjemóžnił"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Sće škitany. Jeli něšto namakamy, zdźělimy wam to."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "Waš zwisk wěsty njeje."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Budźće kedźbliwy na tutym sydle"; diff --git a/firefox-ios/Shared/Supporting Files/hsb.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/hsb.lproj/Microsurvey.strings index 7ae91fd64dbd..43913174b85f 100644 --- a/firefox-ios/Shared/Supporting Files/hsb.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/hsb.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Njewubrany"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Naprašowanje"; + diff --git a/firefox-ios/Shared/Supporting Files/hsb.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/hsb.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..b9783fa96963 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hsb.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Znowa začitać"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "SSL-zmylk je nastał a wěsty zwisk ze serwerom njeda so wutworić."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "Budźće kedźbliwy. Něšto w porjadku njeje."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "Spytajće z druhim gratom zwjazać. Přepruwujće swój modem abo router. Dźělće zwisk WLAN a zwjazajće znowa."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "Zda so, zo je problem z wašim internetnym zwiskom."; + diff --git a/firefox-ios/Shared/Supporting Files/hsb.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/hsb.lproj/Settings.strings index 49f003b531d2..e7b8adebf8b3 100644 --- a/firefox-ios/Shared/Supporting Files/hsb.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/hsb.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Adresy rjadować"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "Adresa za %@"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "SKŁADOWANE ADRESY"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Adresy do %@ składować"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "Składujće swoje informacije wěsć, zo byšće pozdźišo spěšny přistup k njemu dóstał."; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "Wobsahuje telefonowe čisła a e-mejlowe adresy"; diff --git a/firefox-ios/Shared/Supporting Files/hsb.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/hsb.lproj/Toolbar.strings new file mode 100644 index 000000000000..602ce3e2508d --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hsb.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Nowy rajtark"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Tutón rajtark začinić"; + diff --git a/firefox-ios/Shared/Supporting Files/hu.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/hu.lproj/EditAddress.strings index 822affa971cc..15cc957b8489 100644 --- a/firefox-ios/Shared/Supporting Files/hu.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/hu.lproj/EditAddress.strings @@ -88,6 +88,9 @@ /* Label for the telephone number field, allowing users to input their contact number. This is essential for communication and service provision, ensuring contact details are autofilled correctly. */ "Addresses.EditAddress.AutofillAddressTel.v129" = "Telefon"; +/* Label for the input field for the townland, a specific type of land division used in rural areas. Enhances address detail for users in regions where townlands are a common addressing component. */ +"Addresses.EditAddress.AutofillAddressTownland.v129" = "Townland"; + /* Label for the field to input the name of a village or township. This is crucial for addresses in rural areas, ensuring the autofill feature accurately captures all necessary geographical details. */ "Addresses.EditAddress.AutofillAddressVillageTownship.v129" = "Falu vagy község"; @@ -127,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "A cím nem menthető"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "A cím nem távolítható el"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Próbálja újra"; diff --git a/firefox-ios/Shared/Supporting Files/hu.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/hu.lproj/EnhancedTrackingProtection.strings index 89cf1d1d148a..e8e9c44ddff9 100644 --- a/firefox-ios/Shared/Supporting Files/hu.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/hu.lproj/EnhancedTrackingProtection.strings @@ -4,6 +4,9 @@ /* The text for the clear cookies and site data alert button inside the enhanced tracking protection screen. */ "Menu.EnhancedTrackingProtection.ClearData.AlertOkButton.v128" = "Törlés"; +/* The text for the clear cookies and site data alert inside the enhanced tracking protection screen. The placeholder will be replaced with the user's currently visited website */ +"Menu.EnhancedTrackingProtection.ClearData.AlertText.v128" = "A sütik és oldaladatok eltávolítása a(z) %@ webhelyről kijelentkeztetheti a webhelyekről, és törölheti a kosarait."; + /* The title for the clear cookies and site data alert inside the enhanced tracking protection screen. */ "Menu.EnhancedTrackingProtection.ClearData.AlertTitle.v128" = "Sütik és oldaladatok törlése"; @@ -19,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "A kapcsolat nem biztonságos"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Nem találhatóak nyomkövetők"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Webhelyek közötti nyomkövető sütik: %@"; @@ -28,22 +34,39 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Közösségimédia-követők: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Blokkolt követők: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ "Menu.EnhancedTrackingProtection.Details.Verifier.v128" = "Ellenőrizte: %@"; +/* Header for the enhanced tracking protection screen when the user has opted out of the feature. Placeholder will be replaced by the app name */ +"Menu.EnhancedTrackingProtection.Off.Header.v128" = "A %@ szolgálaton kívül van. Javasoljuk a védelmek ismételt bekapcsolását."; + /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Kikapcsolta a védelmeket"; +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ +"Menu.EnhancedTrackingProtection.On.Header.v128" = "Védve van. Ha látunk valamit, értesíteni fogjuk."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Legyen óvatos ezen az oldalon"; +/* Title for the enhanced tracking protection screen when the user has selected to be protected. The placeholder will have the value of the app name */ +"Menu.EnhancedTrackingProtection.On.Title.v128" = "A %@ résen van"; + /* The title for the privacy settings button inside the enhanced tracking protection screen. */ "Menu.EnhancedTrackingProtection.PrivacySettings.Title.v128" = "Adatvédelmi beállítások"; /* Title for the switch to enable/disable enhanced tracking protection inside the menu. */ "Menu.EnhancedTrackingProtection.Switch.Title.v128" = "Fokozott követés elleni védelem"; +/* A switch to disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.SwitchOff.Text.v128" = "A védelem KI van kapcsolva. Javasoljuk, hogy kapcsolja be újra."; + +/* A switch to disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.SwitchOff.Text.v129" = "A védelem KI van kapcsolva. Javasoljuk, hogy kapcsolja be újra."; + +/* A switch to disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.SwitchOn.Text.v128" = "Ha valami nem működik ezen az oldalon, próbálja meg kikapcsolni."; + diff --git a/firefox-ios/Shared/Supporting Files/hu.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/hu.lproj/Microsurvey.strings index e43450218ae9..f34e1c32505a 100644 --- a/firefox-ios/Shared/Supporting Files/hu.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/hu.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Nincs kiválasztva"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Felmérés"; + diff --git a/firefox-ios/Shared/Supporting Files/hu.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/hu.lproj/Toolbar.strings new file mode 100644 index 000000000000..923b48d39b62 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hu.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Új lap"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Lap bezárása"; + diff --git a/firefox-ios/Shared/Supporting Files/hy-AM.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/hy-AM.lproj/EditAddress.strings index f4a7de7e203c..6b6df38a981f 100644 --- a/firefox-ios/Shared/Supporting Files/hy-AM.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/hy-AM.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "Հասցեն չհաջողվեց պահել"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Հասցեն չհաջողվեց հեռացնել"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Կրկին փորձել"; diff --git a/firefox-ios/Shared/Supporting Files/hy-AM.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/hy-AM.lproj/EnhancedTrackingProtection.strings index d1464ab41b3e..306576b16613 100644 --- a/firefox-ios/Shared/Supporting Files/hy-AM.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/hy-AM.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Կապակցումն անվտանգ չէ"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Հետագծողներ չեն գտնվել"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Միջակայքային հետևող նշոցիկներ` %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Սոցիալական մեդիայի հետագծումներ` %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Հետագծողներն արգելափակված են՝ %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,8 +46,7 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Դուք անջատեցիք պաշտպանությունը"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Դուք պաշտպանված եք: Եթե ինչ-որ բան նկատենք, ձեզ կտեղեկացնենք։"; /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ diff --git a/firefox-ios/Shared/Supporting Files/hy-AM.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/hy-AM.lproj/Microsurvey.strings index 040a2d1b2d3d..51ff09165334 100644 --- a/firefox-ios/Shared/Supporting Files/hy-AM.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/hy-AM.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Ապանշված"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Հարցում"; + diff --git a/firefox-ios/Shared/Supporting Files/hy-AM.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/hy-AM.lproj/Toolbar.strings new file mode 100644 index 000000000000..7dba3131d55d --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/hy-AM.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Նոր ներդիր"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Փակել այս ներդիրը"; + diff --git a/firefox-ios/Shared/Supporting Files/ia.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/ia.lproj/EditAddress.strings index 49f01ede7105..2f248c55e4d4 100644 --- a/firefox-ios/Shared/Supporting Files/ia.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/ia.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "Le adresse non pote esser salvate"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Le adresse non pote esser removite"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Retentar"; diff --git a/firefox-ios/Shared/Supporting Files/ia.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/ia.lproj/EnhancedTrackingProtection.strings index bc20e4c2aede..68d14a28b811 100644 --- a/firefox-ios/Shared/Supporting Files/ia.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/ia.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Connexion non secur"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Nulle traciatores trovate"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Cookies de traciamento inter sitos: %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Traciatores de retes social: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Traciatores blocate: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Tu disactivava le protectiones"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Tu es protegite. Si nos discoperi alco, lo facera saper vos."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "Tu connexion non es secur."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Sia attente in iste sito"; diff --git a/firefox-ios/Shared/Supporting Files/ia.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/ia.lproj/Microsurvey.strings index 4dd776757c8f..02c40bd361db 100644 --- a/firefox-ios/Shared/Supporting Files/ia.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/ia.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "De-seligite"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Questionario"; + diff --git a/firefox-ios/Shared/Supporting Files/ia.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/ia.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..7d900804b56b --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ia.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Recargar"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "Un error SSL ha occurrite e un connexion secur al servitor non pote esser facite."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "Attention! Alco non pare correcte."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "Prova a connecter te ab un altere apparato. Controla le modem o le router. Disactiva e reactiva le connexion Wi-Fi."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "Il pare que il ha un problema con tu connexion internet."; + diff --git a/firefox-ios/Shared/Supporting Files/ia.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/ia.lproj/Settings.strings index eb7129a33bf7..bbf259b21582 100644 --- a/firefox-ios/Shared/Supporting Files/ia.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/ia.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Gerer adresses"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "Adresse pro %@"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "ADRESSES SALVATE"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Salvar adresses a %@"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "Reserva tu informationes con securitate pro postea haber accesso facile a illos."; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "Include numeros de telephono e adresses email"; diff --git a/firefox-ios/Shared/Supporting Files/ia.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/ia.lproj/Toolbar.strings new file mode 100644 index 000000000000..34a5654643ab --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ia.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Nove scheda"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Clauder iste scheda"; + diff --git a/firefox-ios/Shared/Supporting Files/is.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/is.lproj/EditAddress.strings index 438cb7743db7..6ce85425d408 100644 --- a/firefox-ios/Shared/Supporting Files/is.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/is.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "Ekki tókst að vista heimilisfang"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Ekki tókst að fjarlægja heimilisfang"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Reyna aftur"; diff --git a/firefox-ios/Shared/Supporting Files/is.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/is.lproj/EnhancedTrackingProtection.strings index bdf235da3b7d..324377d19a3d 100644 --- a/firefox-ios/Shared/Supporting Files/is.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/is.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Tenging er ekki örugg"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Engir rekjarar fundust"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Rakningarkökur milli vefsvæða: %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Samfélagsmiðlarekjarar: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Lokað á rekjara: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Þú slökktir á vörnum"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Þú ert varin/n. Ef við komum auga á eitthvað látum við þig vita."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "Tengingin þín er ekki örugg."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Farðu varlega á þessum vef"; diff --git a/firefox-ios/Shared/Supporting Files/is.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/is.lproj/Microsurvey.strings index 757f6f1429a1..698760dc35a8 100644 --- a/firefox-ios/Shared/Supporting Files/is.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/is.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Óvalið"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Könnun"; + diff --git a/firefox-ios/Shared/Supporting Files/is.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/is.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..c459d22d20ab --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/is.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Endurhlaða"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "SSL-villa kom upp og ekki er hægt að koma á öruggri tengingu við netþjóninn."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "Farðu varlega. Eitthvað lítur ekki út fyrir að vera rétt."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "Prófaðu að tengjast í öðru tæki. Athugaðu mótaldið þitt eða beininn. Aftengdu og tengdu aftur við Wi-Fi."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "Það virðist vera vandamál með nettenginguna þína."; + diff --git a/firefox-ios/Shared/Supporting Files/is.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/is.lproj/Settings.strings index b181fa12c35a..eaf0c6c7e89e 100644 --- a/firefox-ios/Shared/Supporting Files/is.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/is.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Sýsla með tölvupóstföng"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "Póstfang fyrir %@"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "VISTAÐAR SLÓÐIR"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Vista póstföng í %@"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "Vistaðu upplýsingarnar þínar á öruggan hátt til að fá skjótan aðgang að þeim síðar."; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "Þar með talin símanúmer og tölvupóstföng"; diff --git a/firefox-ios/Shared/Supporting Files/is.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/is.lproj/Toolbar.strings new file mode 100644 index 000000000000..042daa71f003 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/is.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Nýr flipi"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Loka þessum flipa"; + diff --git a/firefox-ios/Shared/Supporting Files/it.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/it.lproj/EditAddress.strings index f0dcab6067be..b5edad48f5a9 100644 --- a/firefox-ios/Shared/Supporting Files/it.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/it.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "Impossibile salvare l’indirizzo"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Impossibile rimuovere l’indirizzo"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Riprova"; diff --git a/firefox-ios/Shared/Supporting Files/it.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/it.lproj/EnhancedTrackingProtection.strings index f5d80561b253..a1922e9e1798 100644 --- a/firefox-ios/Shared/Supporting Files/it.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/it.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Connessione non sicura"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Nessun elemento tracciante rilevato"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Cookie traccianti intersito: %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Traccianti dei social media: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Elementi traccianti bloccati: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Hai disattivato le protezioni"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Sei protetto. Se rileviamo qualcosa, te lo faremo sapere."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "Questa connessione non è sicura."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Fai attenzione su questo sito"; diff --git a/firefox-ios/Shared/Supporting Files/it.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/it.lproj/Microsurvey.strings index 240e565da150..10a2f5ed6a5c 100644 --- a/firefox-ios/Shared/Supporting Files/it.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/it.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Non selezionato"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Sondaggio"; + diff --git a/firefox-ios/Shared/Supporting Files/it.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/it.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..b55e3ec7517e --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/it.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Ricarica"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "Si è verificato un errore SSL e non è possibile stabilire una connessione sicura al server."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "Fai attenzione. Sembra che ci sia un problema."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "Prova a connetterti da un altro dispositivo, controlla il modem o il router, disattiva e riattiva la connessione Wi-Fi."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "Sembra che ci sia un problema con la connessione a Internet."; + diff --git a/firefox-ios/Shared/Supporting Files/it.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/it.lproj/Settings.strings index 8a61e5698fc3..720869182667 100644 --- a/firefox-ios/Shared/Supporting Files/it.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/it.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Gestisci indirizzi"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "Indirizzo per %@"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "INDIRIZZI SALVATI"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Salva indirizzi in %@"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "Salva le tue informazioni in modo sicuro per accedervi rapidamente in seguito."; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "Include numeri di telefono e indirizzi email"; diff --git a/firefox-ios/Shared/Supporting Files/it.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/it.lproj/Toolbar.strings new file mode 100644 index 000000000000..62b31c38dac6 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/it.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Nuova scheda"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Chiudi questa scheda"; + diff --git a/firefox-ios/Shared/Supporting Files/ja.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/ja.lproj/EditAddress.strings new file mode 100644 index 000000000000..c704d5ba9cb9 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ja.lproj/EditAddress.strings @@ -0,0 +1,141 @@ +/* Title for the cancel button in the remove address alert. */ +"Addresses.EditAddress.Alert.CancelButton.v129" = "キャンセル"; + +/* Message explaining the consequences of removing an address from all synced devices. */ +"Addresses.EditAddress.Alert.Message.v129" = "同期しているすべての端末から住所が削除されます。"; + +/* Title for the remove button in the remove address alert. */ +"Addresses.EditAddress.Alert.RemoveButton.v129" = "削除"; + +/* Title for the alert indicating the action to remove an address. */ +"Addresses.EditAddress.Alert.Title.v129" = "住所を削除"; + +/* Title for the interface option where users can add a new address for autofill purposes. This facilitates quicker form completion by automatically filling in the user's address information. */ +"Addresses.EditAddress.AutofillAddAddressTitle.v129" = "住所を追加"; + +/* Label for the area field, allowing users to specify a particular area within a city or region. This detail can improve the specificity and accuracy of autofilled addresses. */ +"Addresses.EditAddress.AutofillAddressArea.v129" = "地区"; + +/* Label for the field where users input the city part of their address. This information is crucial for mail delivery and service provision, ensuring accurate city identification in autofill settings. */ +"Addresses.EditAddress.AutofillAddressCity.v129" = "市"; + +/* Label for the field where users can specify just the country, used in contexts where full address details are not required. Simplifies autofill when only country information is necessary. */ +"Addresses.EditAddress.AutofillAddressCountryOnly.v129" = "国"; + +/* Label for the country or region field in address forms, allowing users to specify their country or territorial region. This is fundamental for international mail and services, ensuring autofill accuracy across borders. */ +"Addresses.EditAddress.AutofillAddressCountryRegion.v129" = "国または地域"; + +/* Label for the county field, crucial for addressing in regions where county lines play a key role in postal services. Enhances autofill accuracy by including county information. */ +"Addresses.EditAddress.AutofillAddressCounty.v129" = "郡"; + +/* Label for the department field, used in countries like France and Colombia where departments are a key administrative division. Ensures correct departmental information is autofilled. */ +"Addresses.EditAddress.AutofillAddressDepartment.v129" = "県"; + +/* Label for the district field in the address form, allowing users to specify their district for more precise location identification. This aids in refining address details for accurate autofill. */ +"Addresses.EditAddress.AutofillAddressDistrict.v129" = "区"; + +/* Label for the Do/Si field, pertinent to addresses in South Korea. Do/Si refers to provincial level divisions, and specifying this enhances address accuracy in autofill settings. */ +"Addresses.EditAddress.AutofillAddressDoSi.v129" = "道/市"; + +/* Label for the Eircode field, specific to Ireland. It's a unique postal code system that helps in precise location identification, enhancing the effectiveness of autofill. */ +"Addresses.EditAddress.AutofillAddressEircode.v129" = "Eircode"; + +/* Label for the email address field, where users input their email. Critical for digital communication and account verification, this ensures email addresses are autofilled accurately. */ +"Addresses.EditAddress.AutofillAddressEmail.v129" = "メールアドレス"; + +/* Label for the emirate field, essential for addresses in the United Arab Emirates. Including emirate details ensures the autofill feature accurately represents user addresses. */ +"Addresses.EditAddress.AutofillAddressEmirate.v129" = "首長国"; + +/* Label for the field where users specify the name of an island, if applicable. Important for addresses in archipelagic regions, aiding in precise location identification during autofill. */ +"Addresses.EditAddress.AutofillAddressIsland.v129" = "島"; + +/* Label for the field where the user inputs their full name as part of an address form. Essential for personalized form submissions and ensuring information accuracy in autofilled forms. */ +"Addresses.EditAddress.AutofillAddressName.v129" = "氏名"; + +/* Label for the field where users can input the name of their neighborhood. This detail adds precision to addresses, especially in densely populated areas, improving the accuracy of autofill. */ +"Addresses.EditAddress.AutofillAddressNeighborhood.v129" = "地域"; + +/* Label for the oblast field, relevant for addresses in countries like Russia and Ukraine. Oblasts are a significant administrative division, and their specification aids in autofill accuracy. */ +"Addresses.EditAddress.AutofillAddressOblast.v129" = "州"; + +/* Label for the input field designated for the organization's name related to the address. Helps in distinguishing addresses used for business or personal purposes in autofill settings. */ +"Addresses.EditAddress.AutofillAddressOrganization.v129" = "組織名"; + +/* Label for the parish field, significant in places where parishes are used for local administration and addressing. Ensures users can specify parish details for better autofill accuracy. */ +"Addresses.EditAddress.AutofillAddressParish.v129" = "教区"; + +/* Label for the PIN (Postal Index Number) field, used in India. It's a code representing a specific area, crucial for accurate mail delivery and autofill functionality. */ +"Addresses.EditAddress.AutofillAddressPin.v129" = "PIN"; + +/* Label for the postal code field, universally used in address forms to specify the area code for mail sorting. Essential for autofill to ensure mail and services are accurately routed. */ +"Addresses.EditAddress.AutofillAddressPostalCode.v129" = "郵便番号"; + +/* Label for the post town field, used primarily in the UK and some other regions for mail sorting. Essential for users in applicable areas to specify for correct mail delivery through autofill. */ +"Addresses.EditAddress.AutofillAddressPostTown.v129" = "ポストタウン"; + +/* Label for the prefecture field, essential for addresses in countries like Japan where prefectures are a major administrative division. Aids in precise location specification for autofill. */ +"Addresses.EditAddress.AutofillAddressPrefecture.v129" = "都道府県"; + +/* Label for the province field, required in countries where provinces are a primary administrative division. Helps in pinpointing the user's location more accurately for autofill purposes. */ +"Addresses.EditAddress.AutofillAddressProvince.v129" = "州/省/県"; + +/* Label for the state field, a necessary component of addresses in many countries, especially the USA. It ensures that state-specific details are correctly filled in forms using autofill. */ +"Addresses.EditAddress.AutofillAddressState.v129" = "州"; + +/* Label for the suburb field, enabling users to add suburb details to their address. This is important for accurate delivery and services in suburban areas, enhancing autofill functionality. */ +"Addresses.EditAddress.AutofillAddressSuburb.v129" = "地区"; + +/* Label for the telephone number field, allowing users to input their contact number. This is essential for communication and service provision, ensuring contact details are autofilled correctly. */ +"Addresses.EditAddress.AutofillAddressTel.v129" = "電話番号"; + +/* Label for the input field for the townland, a specific type of land division used in rural areas. Enhances address detail for users in regions where townlands are a common addressing component. */ +"Addresses.EditAddress.AutofillAddressTownland.v129" = "タウンランド"; + +/* Label for the field to input the name of a village or township. This is crucial for addresses in rural areas, ensuring the autofill feature accurately captures all necessary geographical details. */ +"Addresses.EditAddress.AutofillAddressVillageTownship.v129" = "村または郡区"; + +/* Label for the ZIP code field, primarily used in the United States for mail sorting. Key for autofill to accurately complete addresses for shipping, billing, and service provision. */ +"Addresses.EditAddress.AutofillAddressZip.v129" = "ZIP Code"; + +/* Label for the button to cancel the current autofill operation or exit the form without saving changes. Provides users with an option to back out of a process without making any modifications. */ +"Addresses.EditAddress.AutofillCancelButton.v129" = "キャンセル"; + +/* Title for the option allowing users to edit an existing saved address. This is used within the settings for autofill, enabling users to update their address details for accurate form autofilling. */ +"Addresses.EditAddress.AutofillEditAddressTitle.v129" = "住所の編集"; + +/* Title for the input field where users can enter their street address. This is used within the settings for autofill, allowing users to provide their street address for accurate form autofilling. */ +"Addresses.EditAddress.AutofillEditStreetAddressTitle.v129" = "番地・通り名"; + +/* Label for the button to save the current address details entered or edited by the user. This action confirms the user's changes and updates their autofill settings accordingly. */ +"Addresses.EditAddress.AutofillSaveButton.v129" = "保存"; + +/* Title for the option allowing users to view an existing saved address. This is used within the settings for autofill, enabling users to see their address details for accurate form autofilling. */ +"Addresses.EditAddress.AutofillViewAddressTitle.v129" = "住所を表示"; + +/* Button label for closing the view where user can view their address info. */ +"Addresses.EditAddress.CloseNavBarButtonLabel.v129" = "閉じる"; + +/* Button label for editing the address details shown in the form. */ +"Addresses.EditAddress.EditNavBarButtonLabel.v129" = "編集"; + +/* Title for button that offers the user the option to remove an address. */ +"Addresses.EditAddress.RemoveAddressButtonTitle.v129" = "住所を削除"; + +/* Toast message confirming that an address has been successfully removed. */ +"Addresses.Toast.AddressRemovedConfirmation.v129" = "住所を削除しました"; + +/* Toast message confirming that an address has been successfully saved. */ +"Addresses.Toast.AddressSavedConfirmation.v129" = "住所を保存しました"; + +/* Toast message indicating an error occurred while trying to save an address. */ +"Addresses.Toast.AddressSaveError.v129" = "住所を保存できませんでした"; + +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "住所を削除できませんでした"; + +/* Suggestion to try again after an error occurred while trying to save an address. */ +"Addresses.Toast.AddressSaveRetrySuggestion.v129" = "再試行"; + +/* Toast message confirming that an address has been successfully updated. */ +"Addresses.Toast.AddressUpdatedConfirmation.v129" = "住所を更新しました"; + diff --git a/firefox-ios/Shared/Supporting Files/ja.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/ja.lproj/EnhancedTrackingProtection.strings index 0c59e2f5c514..1eb11225f5cc 100644 --- a/firefox-ios/Shared/Supporting Files/ja.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/ja.lproj/EnhancedTrackingProtection.strings @@ -22,8 +22,19 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "安全でない接続"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "トラッカーが見つかりませんでした"; + +/* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "クロスサイトトラッキング Cookie: %@"; + +/* Text to let users know how many fingerprinters were blocked on the current website. The placeholder will show the number of fingerprinters detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.Fingerprinter.v129" = "フィンガープリント: %@"; + +/* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "ソーシャルメディアトラッカー: %@"; + +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "ブロックされた追跡: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -35,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "保護をオフにしました"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "現在保護されています。何か問題があればお知らせします。"; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "接続が安全ではありません。"; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "このサイトでは注意してください"; @@ -54,6 +67,9 @@ /* A switch to disable enhanced tracking protection inside the menu. */ "Menu.EnhancedTrackingProtection.SwitchOff.Text.v128" = "保護機能がオフになっています。再度オンにすることをおすすめします。"; +/* A switch to disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.SwitchOff.Text.v129" = "保護機能がオフになっています。再度オンにすることをおすすめします。"; + /* A switch to disable enhanced tracking protection inside the menu. */ "Menu.EnhancedTrackingProtection.SwitchOn.Text.v128" = "このサイトでの動作がおかしい場合は、オフにしてみてください。"; diff --git a/firefox-ios/Shared/Supporting Files/ja.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/ja.lproj/Microsurvey.strings index e27c4c36936a..19955a0a862c 100644 --- a/firefox-ios/Shared/Supporting Files/ja.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/ja.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "未選択"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "アンケート"; + diff --git a/firefox-ios/Shared/Supporting Files/ja.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/ja.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..f0771b4878cf --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ja.lproj/NativeErrorPage.strings @@ -0,0 +1,6 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "再読み込み"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "SSL エラーが発生したため、サーバーへの安全な接続を確立できません。"; + diff --git a/firefox-ios/Shared/Supporting Files/ja.lproj/PasswordAutofill.strings b/firefox-ios/Shared/Supporting Files/ja.lproj/PasswordAutofill.strings index c756d6cfa2a5..7ea005aecc7b 100644 --- a/firefox-ios/Shared/Supporting Files/ja.lproj/PasswordAutofill.strings +++ b/firefox-ios/Shared/Supporting Files/ja.lproj/PasswordAutofill.strings @@ -1,3 +1,6 @@ +/* This label is used in a cell found in the list of autofill login options in place of an actual username to denote that no username was saved for this login */ +"PasswordAutofill.LoginListCellNoUsername.v129" = "(ユーザー名なし)"; + /* This label is used for a button in the password list screen allowing users to manage their saved passwords. It's meant to direct users to where they can add, remove, or edit their saved passwords. */ "PasswordAutofill.ManagePasswordsButton.v124" = "パスワードを管理"; diff --git a/firefox-ios/Shared/Supporting Files/ja.lproj/ScanQRCode.strings b/firefox-ios/Shared/Supporting Files/ja.lproj/ScanQRCode.strings new file mode 100644 index 000000000000..bc8c1ef8b3ad --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ja.lproj/ScanQRCode.strings @@ -0,0 +1,9 @@ +/* Allow button to open URL from scanned QR Code */ +"ScanQRCode.ConfirmOpenURL.AllowButton.v129" = "許可"; + +/* Deny button to cancel opening URL from scanned QR Code */ +"ScanQRCode.ConfirmOpenURL.DenyButton.v129" = "拒否"; + +/* Text of the prompt to ask user permission to open a URL from a scanned QR code. Placeholder is app name. */ +"ScanQRCode.ConfirmOpenURL.Message.v129" = "%@ で開くことを許可しますか?"; + diff --git a/firefox-ios/Shared/Supporting Files/ja.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/ja.lproj/Toolbar.strings new file mode 100644 index 000000000000..aee165744cf6 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ja.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "新規タブ"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "このタブを閉じる"; + diff --git a/firefox-ios/Shared/Supporting Files/kab.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/kab.lproj/EditAddress.strings index fece81c5d278..cd476812f298 100644 --- a/firefox-ios/Shared/Supporting Files/kab.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/kab.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "Tansa ur tezmir ara ad tettwasekles"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Tansa ur tezmir ara ad tettwasekles"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Ɛreḍ tikkelt niḍen"; diff --git a/firefox-ios/Shared/Supporting Files/kab.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/kab.lproj/EnhancedTrackingProtection.strings index f073887a1914..6d74b8d750ed 100644 --- a/firefox-ios/Shared/Supporting Files/kab.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/kab.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Tuqqna taraɣelsant"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Ulac ineḍfaren i yettwafen"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Inagan n tuqqna i uḍfaṛ gar yismal: %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Ineḍfaṛen n iẓeḍwa inmettiyen: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Ineḍfaren ttusweḥlen: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Tsenseḍ ammesten"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Tettummestneḍ. Ma yella ncukk deg kra, ad ak-d-nini."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "Tuqqna-inek d taraɣelsant."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Ḥader asmel-a"; diff --git a/firefox-ios/Shared/Supporting Files/kab.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/kab.lproj/Microsurvey.strings index 1acd77db5c44..2c54c3fac8e7 100644 --- a/firefox-ios/Shared/Supporting Files/kab.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/kab.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Ur yettwafran ara"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Aḥedqis"; + diff --git a/firefox-ios/Shared/Supporting Files/kab.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/kab.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..88bf07e9b5af --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/kab.lproj/NativeErrorPage.strings @@ -0,0 +1,9 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Smiren"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "Teḍra-d tuccḍa SSL, tuqqna taɣellsant akked uqeddac tegguma ad teqεed."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "Ḥader. Yella wayen ur nteddu ara akken ilaq."; + diff --git a/firefox-ios/Shared/Supporting Files/kab.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/kab.lproj/Settings.strings index 3178d09c1af2..cdd6453b832b 100644 --- a/firefox-ios/Shared/Supporting Files/kab.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/kab.lproj/Settings.strings @@ -1,6 +1,15 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Sefrek tansiwin"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "Tansa i %@"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "TANSIWIN I YETTWASKELSEN"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Sekles tansiwin i %@"; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "Seddu ula uṭṭunen n tiliɣri d tansiwin n yimayl"; diff --git a/firefox-ios/Shared/Supporting Files/kab.lproj/Shopping.strings b/firefox-ios/Shared/Supporting Files/kab.lproj/Shopping.strings index 1aeb6be6ca7c..0a22e218d43b 100644 --- a/firefox-ios/Shared/Supporting Files/kab.lproj/Shopping.strings +++ b/firefox-ios/Shared/Supporting Files/kab.lproj/Shopping.strings @@ -55,6 +55,9 @@ /* Title of the review highlights displayed in the shopping review quality bottom sheet. */ "Shopping.HighlightsCard.Title.v120" = "Tamuɣli s wazal-is seg yilɣa imaynuten"; +/* Title for info card when Fakespot cannot analyze reviews for a certain product type */ +"Shopping.InfoCard.FakespotDoesNotAnalyzeReviews.Description.v120" = "Nesḥasef, ur nezmir ara ad nessefqed taɣara n ucegger i kra n wanawen n yifarisen. Amedya, tikarḍiwin n yisefkan d yisuddam n tvidyutin, aẓawan d wuraren."; + /* Title for info card when Fakespot cannot analyze reviews for a certain product type */ "Shopping.InfoCard.FakespotDoesNotAnalyzeReviews.Title.v120" = "Ur nezmir ara ad nsenqed tamawin-a"; @@ -82,6 +85,9 @@ /* Title for info card when there are not enough reviews for a product */ "Shopping.InfoCard.NotEnoughReviews.Title.v120" = "Ulac ddeqs n tamiwin akka tura"; +/* Description for the information card displayed by the review checker feature when the product the user is looking at is out of stock. This description is used for info card where the user can report if it's back in stock. */ +"Shopping.InfoCard.ProductNotInStock.Description.v121" = "Ma twalaḍ afaris-a yuɣal-d deg taggazt, ini-aɣ-t-id, ad nexdem akken ara nessefqed iceggiren."; + /* Primary action label for the information card displayed by the review checker feature when the product the user is looking at is out of stock. This primary action label is used for info card button where the user can report if it's back in stock. */ "Shopping.InfoCard.ProductNotInStock.PrimaryAction.v121" = "Mmel tuɣalin n ufaris deg tawsa"; @@ -118,6 +124,12 @@ /* Text for the disclaimer that appears underneath the rating image of the Shopping Experience Opt In onboarding Card (Fakespot). After the colon, there will be two links, each on their own line. The first link is to a Privacy policy. The second link is to Terms of use. */ "Shopping.OptInCard.Disclaimer.Text.v123" = "S ufran n “Ih, ɛreḍ-it” ad tqebleḍ iferdisen-a:"; +/* Label for the first paragraph of the Shopping Experience Opt In onboarding Card (Fakespot). The first parameter will be the website the user is coming from when viewing this screen (default Amazon). The second parameter will be replaced by the app name. This string is almost identical with 'Shopping.OptInCard.FirstParagraph.Description', but without Best Buy and Walmart websites, which are not available in many locales. */ +"Shopping.OptInCard.FirstParagraph.AmazonOnly.Description.v122" = "Wali taneflest n yiceggiren ɣef ufaris deg %1$@ send ad taɣeḍ. Amcegger, tamahilt tarmitant seg %2$@, tebna akken iwata deg yiminig."; + +/* Label for the first paragraph of the Shopping Experience Opt In onboarding Card (Fakespot). The first parameter will be the website the user is coming from when viewing this screen (default Amazon). The second parameter will be replaced by the app name. The third and fourth parameters will be the other two websites that are currently supported (Amazon, Best Buy or Walmart) besides the one used for the first parameter. */ +"Shopping.OptInCard.FirstParagraph.Description.v120" = "Wali taneflest n yiceggiren ɣef ufaris deg %1$@ send ad taɣeḍ. Amcegger, tamahilt tarmitant seg %2$@, tebna akken iwata deg yiminig. Tetteddu ɣef %3$@ akked %4$@."; + /* Label for the header of the Shopping Experience Opt In onboarding Card (Fakespot) */ "Shopping.OptInCard.HeaderLabel.Title.v120" = "Ɛreḍ amnir-nneɣ yettwamanen i timawin n ufaris"; @@ -160,12 +172,24 @@ /* Description of the reliability ratings for rating 'D' and 'F' displayed in the shopping review quality bottom sheet. */ "Shopping.ReviewQuality.ReliabilityRating.DF.Description.v120" = "Ilɣa yirinaflasen"; +/* Adjusted rating label from How we determine review quality card displayed in the shopping review quality bottom sheet. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"Shopping.ReviewQualityCard.AdjustedRating.Label.v120" = "Alɣu n *amwati* yebna kan ɣef yiceggiren i nettwali deg-sen taneflest."; + /* Accessibility label for the up chevron, from 'How we determine review quality' card displayed in the shopping review quality bottom sheet. */ "Shopping.ReviewQualityCard.Collapse.AccessibilityLabel.v120" = "Fneẓ takarḍa n wamek ara nettguccul alɣu n tɣara"; /* Accessibility label for the down chevron, from 'How we determine review quality' card displayed in the shopping review quality bottom sheet. */ "Shopping.ReviewQualityCard.Expand.AccessibilityLabel.v120" = "Snefli takarḍa n wamek ara nettguccul alɣu n tɣara"; +/* Label of the headline from How we determine review quality card displayed in the shopping review quality bottom sheet. The first parameter will be replaced by the Fakespot app name and the second parameter by the company name of Mozilla. */ +"Shopping.ReviewQualityCard.Headline.Label.v120" = "Nesseqdac tatiknulujit AI seg %1$@ s %2$@ akken ad nessefqed arkad n yiceggiren n ufaris. Aya ad k-iεawen kan ad tektaleḍ taɣara n uceggir, mačči taɣara n ufaris."; + +/* Highlights label from How we determine review quality card displayed in the shopping review quality bottom sheet. The parameter substitutes the partner website the user is coming from. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"Shopping.ReviewQualityCard.Highlights.Label.v120" = "*Asebruraq* seg %1@ n yiceggiren deg 80 n wussan-a ineggura i nettwali yella lettkal fell-asen."; + +/* Highlights label from How we determine review quality card displayed in the shopping review quality bottom sheet. The parameter substitutes the partner website the user is coming from. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"Shopping.ReviewQualityCard.Highlights.Label.v126" = "*Asebruraq* seg %@ n yiceggiren deg 80 n wussan-a ineggura i nettwali yella lettkal fell-asen."; + /* Title of the 'How we determine review quality' card displayed in the shopping review quality bottom sheet. */ "Shopping.ReviewQualityCard.Label.Title.v120" = "Amek ara nettguccul alɣu n tɣara"; @@ -175,6 +199,15 @@ /* Mixed reviews label from How we determine review quality card displayed in the shopping review quality bottom sheet. */ "Shopping.ReviewQualityCard.MixedReviews.Label.v120" = "Nettwali iwellihen sdukklen iwellihen inaflasen d yiwellihen arinaflasen"; +/* Reliable reviews label from How we determine review quality card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.ReliableReviews.Label.v120" = "Iceggiren igerrzen. Nettwali iceggiren zemren ad d-ilin seg yimsaɣen n tidet i d-yeǧǧan iceggiren imeɣtiyen, ur nebni ɣef tḥila."; + +/* Label of the sub headline from How we determine review quality card displayed in the shopping review quality bottom sheet. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"Shopping.ReviewQualityCard.SubHeadline.Label.v120" = "Ad nmudd *asekkil n ugemmay* i yiceggiren ɣef yal afaris, seg A ɣer F."; + +/* Unreliable reviews label from How we determine review quality card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.UnreliableReviews.Label.v120" = "Iceggiren ur yettwamanen ara. Nettwali iceggiren zemren ad ilin d tikellax neɣ kkan-d seg yimciggar ur nesεi azal."; + /* Accessibility label for the up chevron icon used to collapse or minimize the Settings Card within the shopping product review bottom sheet. */ "Shopping.SettingsCard.Collapse.AccessibilityLabel.v120" = "Fneẓ takarḍa n yiɣewwaren"; diff --git a/firefox-ios/Shared/Supporting Files/kab.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/kab.lproj/Toolbar.strings new file mode 100644 index 000000000000..753a6ffd5cde --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/kab.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Iccer amaynut"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Mdel iccer-a"; + diff --git a/firefox-ios/Shared/Supporting Files/kk.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/kk.lproj/EditAddress.strings index 677f6a149c85..c1ebd42982b8 100644 --- a/firefox-ios/Shared/Supporting Files/kk.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/kk.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "Адресті сақтау мүмкін емес"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Адресті өшіру мүмкін емес"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Қайтадан көру"; diff --git a/firefox-ios/Shared/Supporting Files/kk.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/kk.lproj/EnhancedTrackingProtection.strings index 8a1f1ac4c6c7..153d270337f4 100644 --- a/firefox-ios/Shared/Supporting Files/kk.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/kk.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Байланыс қауіпсіз емес"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Трекерлер табылмады"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Сайтаралық бақылайтын cookie файлдары: %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Әлеуметтік желілер трекерлері: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Блокталған трекерлер: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Сіз қорғаныстарды өшірдіңіз"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Сіз қорғалғансыз. Бірдеңені байқасақ, сізге хабарлаймыз."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "Сіздің байланысыңыз қауіпсіз емес."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Бұл сайтта абай болыңыз"; diff --git a/firefox-ios/Shared/Supporting Files/kk.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/kk.lproj/Microsurvey.strings index d236b2bfb09b..53d6d71fe159 100644 --- a/firefox-ios/Shared/Supporting Files/kk.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/kk.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Таңдаудан алынды"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Сауалнама"; + diff --git a/firefox-ios/Shared/Supporting Files/kk.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/kk.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..56829ca548af --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/kk.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Қайта жүктеу"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "SSL қатесі орын алды және серверге қауіпсіз байланысу мүмкін емес."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "Сақ болыңыз. Бірдеңе дұрыс емес сияқты."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "Басқа құрылғыда қосылып көріңіз. Модемді немесе маршрутизаторды тексеріңіз. Wi-Fi желісіне байланысты ажыратыңыз және қайта қосылыңыз."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "Интернет байланысыңызда мәселе бар сияқты."; + diff --git a/firefox-ios/Shared/Supporting Files/kk.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/kk.lproj/Settings.strings index 7a8cebdc02d9..e503f9211ee2 100644 --- a/firefox-ios/Shared/Supporting Files/kk.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/kk.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Адрестерді басқару"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "%@ үшін адрес"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "САҚТАЛҒАН АДРЕСТЕР"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Адрестерді %@ ішіне сақтау"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "Кейінірек жылдам қол жеткізу үшін ақпаратыңызды қауіпсіз сақтаңыз."; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "Телефон нөмірлері мен электрондық пошта адрестерін қамтиды"; diff --git a/firefox-ios/Shared/Supporting Files/kk.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/kk.lproj/Toolbar.strings new file mode 100644 index 000000000000..fde9c6709a66 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/kk.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Жаңа бет"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Бұл бетті жабу"; + diff --git a/firefox-ios/Shared/Supporting Files/ko.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/ko.lproj/EditAddress.strings index fda2b3fadbbb..f6339420f87c 100644 --- a/firefox-ios/Shared/Supporting Files/ko.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/ko.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "주소를 저장할 수 없습니다."; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "주소를 삭제할 수 없습니다."; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "다시 시도"; diff --git a/firefox-ios/Shared/Supporting Files/ko.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/ko.lproj/EnhancedTrackingProtection.strings index 90a233c938ca..4fe6162b4c28 100644 --- a/firefox-ios/Shared/Supporting Files/ko.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/ko.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "안전하지 않은 연결"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "추적기 없음"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "교차 사이트 추적 쿠키: %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "소셜 미디어 추적자: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "차단된 추적기: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,8 +46,7 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "보호 기능을 사용 중지했습니다."; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "보호받고 있습니다. 뭔가를 발견하면 알려드리겠습니다."; /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ diff --git a/firefox-ios/Shared/Supporting Files/ko.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/ko.lproj/Microsurvey.strings index 5c590bf04a75..34eb095ffc34 100644 --- a/firefox-ios/Shared/Supporting Files/ko.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/ko.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "선택 해제됨"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "조사"; + diff --git a/firefox-ios/Shared/Supporting Files/ko.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/ko.lproj/Toolbar.strings new file mode 100644 index 000000000000..6d712f4c53bb --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ko.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "새 탭"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "이 탭 닫기"; + diff --git a/firefox-ios/Shared/Supporting Files/nb.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/nb.lproj/EditAddress.strings new file mode 100644 index 000000000000..5c4ce4c0665f --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/nb.lproj/EditAddress.strings @@ -0,0 +1,141 @@ +/* Title for the cancel button in the remove address alert. */ +"Addresses.EditAddress.Alert.CancelButton.v129" = "Avbryt"; + +/* Message explaining the consequences of removing an address from all synced devices. */ +"Addresses.EditAddress.Alert.Message.v129" = "Adressen vil bli fjernet fra alle de synkroniserte enhetene dine."; + +/* Title for the remove button in the remove address alert. */ +"Addresses.EditAddress.Alert.RemoveButton.v129" = "Fjern"; + +/* Title for the alert indicating the action to remove an address. */ +"Addresses.EditAddress.Alert.Title.v129" = "Fjern adresse"; + +/* Title for the interface option where users can add a new address for autofill purposes. This facilitates quicker form completion by automatically filling in the user's address information. */ +"Addresses.EditAddress.AutofillAddAddressTitle.v129" = "Legg til adresse"; + +/* Label for the area field, allowing users to specify a particular area within a city or region. This detail can improve the specificity and accuracy of autofilled addresses. */ +"Addresses.EditAddress.AutofillAddressArea.v129" = "Område"; + +/* Label for the field where users input the city part of their address. This information is crucial for mail delivery and service provision, ensuring accurate city identification in autofill settings. */ +"Addresses.EditAddress.AutofillAddressCity.v129" = "Sted"; + +/* Label for the field where users can specify just the country, used in contexts where full address details are not required. Simplifies autofill when only country information is necessary. */ +"Addresses.EditAddress.AutofillAddressCountryOnly.v129" = "Land"; + +/* Label for the country or region field in address forms, allowing users to specify their country or territorial region. This is fundamental for international mail and services, ensuring autofill accuracy across borders. */ +"Addresses.EditAddress.AutofillAddressCountryRegion.v129" = "Land eller region"; + +/* Label for the county field, crucial for addressing in regions where county lines play a key role in postal services. Enhances autofill accuracy by including county information. */ +"Addresses.EditAddress.AutofillAddressCounty.v129" = "Land"; + +/* Label for the department field, used in countries like France and Colombia where departments are a key administrative division. Ensures correct departmental information is autofilled. */ +"Addresses.EditAddress.AutofillAddressDepartment.v129" = "Avdeling"; + +/* Label for the district field in the address form, allowing users to specify their district for more precise location identification. This aids in refining address details for accurate autofill. */ +"Addresses.EditAddress.AutofillAddressDistrict.v129" = "Distrikt"; + +/* Label for the Do/Si field, pertinent to addresses in South Korea. Do/Si refers to provincial level divisions, and specifying this enhances address accuracy in autofill settings. */ +"Addresses.EditAddress.AutofillAddressDoSi.v129" = "Do/Si"; + +/* Label for the Eircode field, specific to Ireland. It's a unique postal code system that helps in precise location identification, enhancing the effectiveness of autofill. */ +"Addresses.EditAddress.AutofillAddressEircode.v129" = "Eircode"; + +/* Label for the email address field, where users input their email. Critical for digital communication and account verification, this ensures email addresses are autofilled accurately. */ +"Addresses.EditAddress.AutofillAddressEmail.v129" = "E-post"; + +/* Label for the emirate field, essential for addresses in the United Arab Emirates. Including emirate details ensures the autofill feature accurately represents user addresses. */ +"Addresses.EditAddress.AutofillAddressEmirate.v129" = "Emirat"; + +/* Label for the field where users specify the name of an island, if applicable. Important for addresses in archipelagic regions, aiding in precise location identification during autofill. */ +"Addresses.EditAddress.AutofillAddressIsland.v129" = "Øy"; + +/* Label for the field where the user inputs their full name as part of an address form. Essential for personalized form submissions and ensuring information accuracy in autofilled forms. */ +"Addresses.EditAddress.AutofillAddressName.v129" = "Navn"; + +/* Label for the field where users can input the name of their neighborhood. This detail adds precision to addresses, especially in densely populated areas, improving the accuracy of autofill. */ +"Addresses.EditAddress.AutofillAddressNeighborhood.v129" = "Nabolag"; + +/* Label for the oblast field, relevant for addresses in countries like Russia and Ukraine. Oblasts are a significant administrative division, and their specification aids in autofill accuracy. */ +"Addresses.EditAddress.AutofillAddressOblast.v129" = "Oblast"; + +/* Label for the input field designated for the organization's name related to the address. Helps in distinguishing addresses used for business or personal purposes in autofill settings. */ +"Addresses.EditAddress.AutofillAddressOrganization.v129" = "Organisasjon"; + +/* Label for the parish field, significant in places where parishes are used for local administration and addressing. Ensures users can specify parish details for better autofill accuracy. */ +"Addresses.EditAddress.AutofillAddressParish.v129" = "Sogn"; + +/* Label for the PIN (Postal Index Number) field, used in India. It's a code representing a specific area, crucial for accurate mail delivery and autofill functionality. */ +"Addresses.EditAddress.AutofillAddressPin.v129" = "Pin"; + +/* Label for the postal code field, universally used in address forms to specify the area code for mail sorting. Essential for autofill to ensure mail and services are accurately routed. */ +"Addresses.EditAddress.AutofillAddressPostalCode.v129" = "Postnummer"; + +/* Label for the post town field, used primarily in the UK and some other regions for mail sorting. Essential for users in applicable areas to specify for correct mail delivery through autofill. */ +"Addresses.EditAddress.AutofillAddressPostTown.v129" = "Poststed"; + +/* Label for the prefecture field, essential for addresses in countries like Japan where prefectures are a major administrative division. Aids in precise location specification for autofill. */ +"Addresses.EditAddress.AutofillAddressPrefecture.v129" = "Prefektur"; + +/* Label for the province field, required in countries where provinces are a primary administrative division. Helps in pinpointing the user's location more accurately for autofill purposes. */ +"Addresses.EditAddress.AutofillAddressProvince.v129" = "Provins"; + +/* Label for the state field, a necessary component of addresses in many countries, especially the USA. It ensures that state-specific details are correctly filled in forms using autofill. */ +"Addresses.EditAddress.AutofillAddressState.v129" = "Stat"; + +/* Label for the suburb field, enabling users to add suburb details to their address. This is important for accurate delivery and services in suburban areas, enhancing autofill functionality. */ +"Addresses.EditAddress.AutofillAddressSuburb.v129" = "Forstad"; + +/* Label for the telephone number field, allowing users to input their contact number. This is essential for communication and service provision, ensuring contact details are autofilled correctly. */ +"Addresses.EditAddress.AutofillAddressTel.v129" = "Telefon"; + +/* Label for the input field for the townland, a specific type of land division used in rural areas. Enhances address detail for users in regions where townlands are a common addressing component. */ +"Addresses.EditAddress.AutofillAddressTownland.v129" = "Tettsted"; + +/* Label for the field to input the name of a village or township. This is crucial for addresses in rural areas, ensuring the autofill feature accurately captures all necessary geographical details. */ +"Addresses.EditAddress.AutofillAddressVillageTownship.v129" = "Tettsted eller liten by"; + +/* Label for the ZIP code field, primarily used in the United States for mail sorting. Key for autofill to accurately complete addresses for shipping, billing, and service provision. */ +"Addresses.EditAddress.AutofillAddressZip.v129" = "Postnummer"; + +/* Label for the button to cancel the current autofill operation or exit the form without saving changes. Provides users with an option to back out of a process without making any modifications. */ +"Addresses.EditAddress.AutofillCancelButton.v129" = "Avbryt"; + +/* Title for the option allowing users to edit an existing saved address. This is used within the settings for autofill, enabling users to update their address details for accurate form autofilling. */ +"Addresses.EditAddress.AutofillEditAddressTitle.v129" = "Rediger adresse"; + +/* Title for the input field where users can enter their street address. This is used within the settings for autofill, allowing users to provide their street address for accurate form autofilling. */ +"Addresses.EditAddress.AutofillEditStreetAddressTitle.v129" = "Gateadresse"; + +/* Label for the button to save the current address details entered or edited by the user. This action confirms the user's changes and updates their autofill settings accordingly. */ +"Addresses.EditAddress.AutofillSaveButton.v129" = "Lagre"; + +/* Title for the option allowing users to view an existing saved address. This is used within the settings for autofill, enabling users to see their address details for accurate form autofilling. */ +"Addresses.EditAddress.AutofillViewAddressTitle.v129" = "Vis adresse"; + +/* Button label for closing the view where user can view their address info. */ +"Addresses.EditAddress.CloseNavBarButtonLabel.v129" = "Lukk"; + +/* Button label for editing the address details shown in the form. */ +"Addresses.EditAddress.EditNavBarButtonLabel.v129" = "Rediger"; + +/* Title for button that offers the user the option to remove an address. */ +"Addresses.EditAddress.RemoveAddressButtonTitle.v129" = "Fjern adresse"; + +/* Toast message confirming that an address has been successfully removed. */ +"Addresses.Toast.AddressRemovedConfirmation.v129" = "Adresse fjernet"; + +/* Toast message confirming that an address has been successfully saved. */ +"Addresses.Toast.AddressSavedConfirmation.v129" = "Adresse lagret"; + +/* Toast message indicating an error occurred while trying to save an address. */ +"Addresses.Toast.AddressSaveError.v129" = "Klarte ikke å lagre adressen"; + +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Klarte ikke å fjerne adressen"; + +/* Suggestion to try again after an error occurred while trying to save an address. */ +"Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Prøv igjen"; + +/* Toast message confirming that an address has been successfully updated. */ +"Addresses.Toast.AddressUpdatedConfirmation.v129" = "Adresseinformasjon oppdatert"; + diff --git a/firefox-ios/Shared/Supporting Files/nb.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/nb.lproj/EnhancedTrackingProtection.strings index 9f4574838281..c25c87785d64 100644 --- a/firefox-ios/Shared/Supporting Files/nb.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/nb.lproj/EnhancedTrackingProtection.strings @@ -22,8 +22,19 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Tilkoblingen er ikke sikker"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Fant ingen sporere"; + +/* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Sporingsinfokapsler på tvers av nettsteder: %@"; + +/* Text to let users know how many fingerprinters were blocked on the current website. The placeholder will show the number of fingerprinters detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.Fingerprinter.v129" = "Nettleseravtrykksporere: %@"; + +/* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Sporing via sosiale medier: %@"; + +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Sporere blokkert: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -35,8 +46,7 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Du har slått av beskyttelse"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Du er beskyttet. Om vi oppdager noe sier vi ifra."; /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ @@ -54,6 +64,9 @@ /* A switch to disable enhanced tracking protection inside the menu. */ "Menu.EnhancedTrackingProtection.SwitchOff.Text.v128" = "Beskyttelser er AV. Vi foreslår at du slår den på igjen."; +/* A switch to disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.SwitchOff.Text.v129" = "Beskyttelser er AV. Vi foreslår at du slår dem på igjen."; + /* A switch to disable enhanced tracking protection inside the menu. */ "Menu.EnhancedTrackingProtection.SwitchOn.Text.v128" = "Hvis noe ser ødelagt ut på dette nettstedet, prøv å slå det av."; diff --git a/firefox-ios/Shared/Supporting Files/nb.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/nb.lproj/Microsurvey.strings index fa157b1dfcb8..453a11b431d5 100644 --- a/firefox-ios/Shared/Supporting Files/nb.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/nb.lproj/Microsurvey.strings @@ -31,6 +31,9 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the title for the header on the screen. */ "Microsurvey.Survey.HeaderLabel.v127" = "Gjennomfør denne undersøkelsen"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the title for the header on the screen. */ +"Microsurvey.Survey.HeaderLabel.v129" = "Fyll ut spørreundersøkelsen"; + /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the logo image that appears on the bottom sheet that informs the user that it is coming from the app specifically. Placeholder is for the app name. */ "Microsurvey.Survey.LogoImage.AccessibilityLabel.v129" = "%@-logo"; @@ -49,6 +52,9 @@ /* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ "Microsurvey.Survey.Options.LikertScaleOption5.v127" = "Veldig misfornøyd"; +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. It indicates that the user has not use the feature that the survey is inquiring about. */ +"Microsurvey.Survey.Options.LikertScaleOption6.v129" = "Jeg bruker den ikke"; + /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states the order the survey option in the list of options. First placeholder is the number the option is in the list and the second placeholder is the total number of options such as 1 out of 6. */ "Microsurvey.Survey.OptionsOrder.AccessibilityLabel.v129" = "%1$@ av %2$@"; @@ -61,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Ikke valgt"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Spørreundersøkelse"; + diff --git a/firefox-ios/Shared/Supporting Files/nb.lproj/PasswordAutofill.strings b/firefox-ios/Shared/Supporting Files/nb.lproj/PasswordAutofill.strings index 155ef39c7dae..e47aafd49c12 100644 --- a/firefox-ios/Shared/Supporting Files/nb.lproj/PasswordAutofill.strings +++ b/firefox-ios/Shared/Supporting Files/nb.lproj/PasswordAutofill.strings @@ -1,3 +1,6 @@ +/* This label is used in a cell found in the list of autofill login options in place of an actual username to denote that no username was saved for this login */ +"PasswordAutofill.LoginListCellNoUsername.v129" = "(ingen brukernavn)"; + /* This label is used for a button in the password list screen allowing users to manage their saved passwords. It's meant to direct users to where they can add, remove, or edit their saved passwords. */ "PasswordAutofill.ManagePasswordsButton.v124" = "Behandle passord"; diff --git a/firefox-ios/Shared/Supporting Files/nb.lproj/ScanQRCode.strings b/firefox-ios/Shared/Supporting Files/nb.lproj/ScanQRCode.strings new file mode 100644 index 000000000000..d943b5da03d2 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/nb.lproj/ScanQRCode.strings @@ -0,0 +1,9 @@ +/* Allow button to open URL from scanned QR Code */ +"ScanQRCode.ConfirmOpenURL.AllowButton.v129" = "Tillat"; + +/* Deny button to cancel opening URL from scanned QR Code */ +"ScanQRCode.ConfirmOpenURL.DenyButton.v129" = "Avvis"; + +/* Text of the prompt to ask user permission to open a URL from a scanned QR code. Placeholder is app name. */ +"ScanQRCode.ConfirmOpenURL.Message.v129" = "Tillate %@ å åpne?"; + diff --git a/firefox-ios/Shared/Supporting Files/nb.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/nb.lproj/Toolbar.strings new file mode 100644 index 000000000000..785b809932a2 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/nb.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Ny fane"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Lukk denne fanen"; + diff --git a/firefox-ios/Shared/Supporting Files/nl.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/nl.lproj/EditAddress.strings index 3af5da40d614..f6fa6fa8dddf 100644 --- a/firefox-ios/Shared/Supporting Files/nl.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/nl.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "Adres kan niet worden opgeslagen"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Adres kan niet worden verwijderd"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Opnieuw proberen"; diff --git a/firefox-ios/Shared/Supporting Files/nl.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/nl.lproj/EnhancedTrackingProtection.strings index dac209b9223b..f8e494d2d8c5 100644 --- a/firefox-ios/Shared/Supporting Files/nl.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/nl.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Verbinding niet beveiligd"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Geen trackers gevonden"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Cross-site-trackingcookies: %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Sociale-mediatrackers: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Trackers geblokkeerd: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,8 +46,7 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "U hebt beschermingen uitgeschakeld"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "U bent beschermd. Als we iets ontdekken, laten we u dat weten."; /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ diff --git a/firefox-ios/Shared/Supporting Files/nl.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/nl.lproj/Microsurvey.strings index 688108f1f609..65abf1d6bdbf 100644 --- a/firefox-ios/Shared/Supporting Files/nl.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/nl.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Selectie opgeheven"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Enquête"; + diff --git a/firefox-ios/Shared/Supporting Files/nl.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/nl.lproj/Toolbar.strings new file mode 100644 index 000000000000..06fd629912eb --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/nl.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Nieuw tabblad"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Dit tabblad sluiten"; + diff --git a/firefox-ios/Shared/Supporting Files/nn.lproj/Alerts.strings b/firefox-ios/Shared/Supporting Files/nn.lproj/Alerts.strings index bf5909aa60ad..91bebea6030f 100644 --- a/firefox-ios/Shared/Supporting Files/nn.lproj/Alerts.strings +++ b/firefox-ios/Shared/Supporting Files/nn.lproj/Alerts.strings @@ -17,7 +17,7 @@ "Alerts.RestoreTabs.Button.Yes.v109" = "Gjenopprett faner"; /* The body of the restore tabs pop-up alert. This alert shows when opening up Firefox after it crashed. */ -"Alerts.RestoreTabs.Message.v109" = "Beklagar det. Gjenopprett faner for å fortsetje der du slutta."; +"Alerts.RestoreTabs.Message.v109" = "Beklagar det. Gjenopprett faner for å halde fram der du slutta."; /* The title of the restore tabs pop-up alert. This alert shows when opening up Firefox after it crashed. The placeholder will be the Firefox name. */ "Alerts.RestoreTabs.Title.v109.v2" = "%@ krasja. Vil du gjenopprette fanene dine?"; diff --git a/firefox-ios/Shared/Supporting Files/nn.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/nn.lproj/EditAddress.strings index 1b1ab6afdbb9..dea32d1bb048 100644 --- a/firefox-ios/Shared/Supporting Files/nn.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/nn.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "Klarte ikkje å lagre adresse"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Klarte ikkje å fjerne adresse"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Prøv igjen"; diff --git a/firefox-ios/Shared/Supporting Files/nn.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/nn.lproj/EnhancedTrackingProtection.strings index 72ade1e691cb..729c4301c0b1 100644 --- a/firefox-ios/Shared/Supporting Files/nn.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/nn.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Tilkoplinga er ikkje trygg"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Fann ingen sporarar"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Sporingsinfokapslar på tvers av nettstadar: %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Sosiale mediesporarar: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Sporarar blokkerte: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Du har slått av vern"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Du er verna. Om vi oppdagar noko seier vi ifrå."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "Tilkoplinga di er ikkje trygg."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Ver forsiktig på denne nettstaden"; diff --git a/firefox-ios/Shared/Supporting Files/nn.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/nn.lproj/Microsurvey.strings index f60d820cea11..34df20a30b6b 100644 --- a/firefox-ios/Shared/Supporting Files/nn.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/nn.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Ikkje valt"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Undersøking"; + diff --git a/firefox-ios/Shared/Supporting Files/nn.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/nn.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..c3ecadb4df60 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/nn.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Oppdater"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "Det har oppstått ein SSL-feil og ei sikker tilkopling til sørvaren kan ikkje opprettast."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "Ver varsam. Noko ser ikkje rett ut."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "Prøv å kople til på ei anna eining. Sjekk modemet eller ruteren. Kople frå og kople til Wi-fi igjen."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "Det ser ut til at det er eit problem med internett-tilkoplinga di."; + diff --git a/firefox-ios/Shared/Supporting Files/nn.lproj/ScanQRCode.strings b/firefox-ios/Shared/Supporting Files/nn.lproj/ScanQRCode.strings index 293c423bdaf6..ed4d7c224809 100644 --- a/firefox-ios/Shared/Supporting Files/nn.lproj/ScanQRCode.strings +++ b/firefox-ios/Shared/Supporting Files/nn.lproj/ScanQRCode.strings @@ -5,5 +5,5 @@ "ScanQRCode.ConfirmOpenURL.DenyButton.v129" = "Avvis"; /* Text of the prompt to ask user permission to open a URL from a scanned QR code. Placeholder is app name. */ -"ScanQRCode.ConfirmOpenURL.Message.v129" = "Tillate %@ tå opne?"; +"ScanQRCode.ConfirmOpenURL.Message.v129" = "Tillate %@ å opne?"; diff --git a/firefox-ios/Shared/Supporting Files/nn.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/nn.lproj/Settings.strings index 48feb496d21a..650ace073f0a 100644 --- a/firefox-ios/Shared/Supporting Files/nn.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/nn.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Handsam adresser"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "Adresse for %@"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "LAGRA ADRESSER"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Lagre adresser ti %@"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "Lagre informasjonen din på ein sikker måte for å få rask tilgang til den seinare."; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "Inkluderer telefonnummer og e-postadresser"; diff --git a/firefox-ios/Shared/Supporting Files/nn.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/nn.lproj/Toolbar.strings new file mode 100644 index 000000000000..b354df12f664 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/nn.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Ny fane"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Lat att denne fana"; + diff --git a/firefox-ios/Shared/Supporting Files/pa-IN.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/pa-IN.lproj/EditAddress.strings index 846414681194..c8c9218badad 100644 --- a/firefox-ios/Shared/Supporting Files/pa-IN.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/pa-IN.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "ਸਿਰਨਾਵੇਂ ਨੂੰ ਸੰਭਾਲਿਆ ਨਹੀਂ ਜਾ ਸਕਿਆ"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "ਸਿਰਨਾਵੇਂ ਨੂੰ ਹਟਾਇਆ ਨਹੀਂ ਜਾ ਸਕਿਆ"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "ਫੇਰ ਕੋਸ਼ਿਸ਼ ਕਰੋ"; diff --git a/firefox-ios/Shared/Supporting Files/pa-IN.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/pa-IN.lproj/EnhancedTrackingProtection.strings index c0c93e657881..b57bab1a1968 100644 --- a/firefox-ios/Shared/Supporting Files/pa-IN.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/pa-IN.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "ਕਨੈਕਸ਼ਨ ਸੁਰੱਖਿਅਤ ਨਹੀਂ ਹੈ"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "ਕੋਈ ਟਰੈਕਰ ਨਹੀਂ ਲੱਭੇ"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "ਅੰਤਰ-ਸਾਈਟ ਟਰੈਕਿੰਗ ਕੂਕੀਜ਼: %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "ਸਮਾਜਿਕ ਮੀਡਿਆ ਟਰੈਕਰ: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "ਪਾਬੰਦੀ ਲਾਏ ਟਰੈਕਰ: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "ਤੁਸੀਂ ਸੁਰੱਖਿਆਵਾਂ ਨੂੰ ਬੰਦ ਕੀਤਾ ਹੈ"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "ਤੁਸੀਂ ਸੁਰੱਖਿਅਤ ਹੋ। ਜੇ ਸਾਨੂੰ ਕੁਝ ਲੱਭਿਆ ਤਾਂ ਅਸੀਂ ਤੁਹਾਨੂੰ ਦੱਸਾਂਗੇ।"; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "ਤੁਹਾਡਾ ਕਨੈਕਸ਼ਨ ਸੁਰੱਖਿਅਤ ਨਹੀਂ ਹੈ।"; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "ਇਸ ਸਾਈਟ ਤੋਂ ਸਾਵਧਾਨ ਰਹੋ"; diff --git a/firefox-ios/Shared/Supporting Files/pa-IN.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/pa-IN.lproj/Microsurvey.strings index f1b1ad1dc079..8743760f68ad 100644 --- a/firefox-ios/Shared/Supporting Files/pa-IN.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/pa-IN.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "ਨਾ-ਚੁਣਿਆ"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "ਸਰਵੇਖਣ"; + diff --git a/firefox-ios/Shared/Supporting Files/pa-IN.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/pa-IN.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..0a1ae13d967b --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/pa-IN.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "ਮੁੜ-ਲੋਡ"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "ਇੱਕ SSL ਗਲਤੀ ਆਈ ਹੈ ਅਤੇ ਸਰਵਰ ਨਾਲ ਸੁਰੱਖਿਅਤ ਕਨੈਕਸ਼ਨ ਨਹੀਂ ਬਣਾਇਆ ਜਾ ਸਕਦਾ ਹੈ।"; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "ਧਿਆਨ ਰੱਖੋ। ਕੁਝ ਸਹੀ ਨਹੀਂ ਜਾਪਦਾ ਹੈ।"; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "ਵੱਖਰੇ ਡਿਵਾਈਸ ਨਾਲ ਕਨੈਕਟ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰੋ। ਆਪਣੇ ਮਾਡਮ ਜਾਂ ਰਾਊਟਰ ਦੀ ਜਾਂਚ ਕਰੋ। Wi-Fi ਤੋਂ ਡਿਸ-ਕਨੈਕਟ ਕਰਕੇ ਫੇਰ ਕਨੈਕਟ ਕਰੋ।"; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "ਤੁਹਾਡੇ ਇੰਟਰਨੈੱਟ ਕਨੈਕਸ਼ਨ ਨਾਲ ਸਮੱਸਿਆ ਜਾਪਦੀ ਹੈ।"; + diff --git a/firefox-ios/Shared/Supporting Files/pa-IN.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/pa-IN.lproj/Settings.strings index 891bd0b305fb..1846d5c389b5 100644 --- a/firefox-ios/Shared/Supporting Files/pa-IN.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/pa-IN.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "ਸਿਰਨਾਵਿਆਂ ਦਾ ਇੰਤਜ਼ਾਮ ਕਰੋ"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "%@ ਲਈ ਸਿਰਨਾਵਾਂ"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "ਸੰਭਾਲੇ ਹੋਏ ਸਿਰਨਾਵੇਂ"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "%@ ਲਈ ਸਿਰਨਾਵੇਂ ਨੂੰ ਸੰਭਾਲੋ"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "ਆਪਣੀ ਜਾਣਕਾਰੀ ਨੂੰ ਮੁੜ ਕੇ ਫ਼ੌਰਨ ਵਰਤਣ ਲਈ ਆਪਣੀ ਜਾਣਕਾਰੀ ਨੂੰ ਸੁਰੱਖਿਅਤ ਢੰਗ ਨਾਲ ਸੰਭਾਲੋ।"; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "ਫ਼ੋਨ ਨੰਬਰਾਂ ਅਤੇ ਈਮੇਲ ਸਿਰਨਾਵਿਆਂ ਸਮੇਤ"; diff --git a/firefox-ios/Shared/Supporting Files/pa-IN.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/pa-IN.lproj/Toolbar.strings new file mode 100644 index 000000000000..f5b3a5a7dbae --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/pa-IN.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "ਨਵੀਂ ਟੈਬ"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "ਇਸ ਟੈਬ ਨੂੰ ਬੰਦ ਕਰੋ"; + diff --git a/firefox-ios/Shared/Supporting Files/pl.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/pl.lproj/EditAddress.strings new file mode 100644 index 000000000000..7b5da32cf52e --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/pl.lproj/EditAddress.strings @@ -0,0 +1,141 @@ +/* Title for the cancel button in the remove address alert. */ +"Addresses.EditAddress.Alert.CancelButton.v129" = "Anuluj"; + +/* Message explaining the consequences of removing an address from all synced devices. */ +"Addresses.EditAddress.Alert.Message.v129" = "Adres zostanie usunięty ze wszystkich synchronizowanych urządzeń."; + +/* Title for the remove button in the remove address alert. */ +"Addresses.EditAddress.Alert.RemoveButton.v129" = "Usuń"; + +/* Title for the alert indicating the action to remove an address. */ +"Addresses.EditAddress.Alert.Title.v129" = "Usuń adres"; + +/* Title for the interface option where users can add a new address for autofill purposes. This facilitates quicker form completion by automatically filling in the user's address information. */ +"Addresses.EditAddress.AutofillAddAddressTitle.v129" = "Dodaj adres"; + +/* Label for the area field, allowing users to specify a particular area within a city or region. This detail can improve the specificity and accuracy of autofilled addresses. */ +"Addresses.EditAddress.AutofillAddressArea.v129" = "Obszar"; + +/* Label for the field where users input the city part of their address. This information is crucial for mail delivery and service provision, ensuring accurate city identification in autofill settings. */ +"Addresses.EditAddress.AutofillAddressCity.v129" = "Miasto"; + +/* Label for the field where users can specify just the country, used in contexts where full address details are not required. Simplifies autofill when only country information is necessary. */ +"Addresses.EditAddress.AutofillAddressCountryOnly.v129" = "Państwo"; + +/* Label for the country or region field in address forms, allowing users to specify their country or territorial region. This is fundamental for international mail and services, ensuring autofill accuracy across borders. */ +"Addresses.EditAddress.AutofillAddressCountryRegion.v129" = "Państwo lub region"; + +/* Label for the county field, crucial for addressing in regions where county lines play a key role in postal services. Enhances autofill accuracy by including county information. */ +"Addresses.EditAddress.AutofillAddressCounty.v129" = "Hrabstwo"; + +/* Label for the department field, used in countries like France and Colombia where departments are a key administrative division. Ensures correct departmental information is autofilled. */ +"Addresses.EditAddress.AutofillAddressDepartment.v129" = "Departament"; + +/* Label for the district field in the address form, allowing users to specify their district for more precise location identification. This aids in refining address details for accurate autofill. */ +"Addresses.EditAddress.AutofillAddressDistrict.v129" = "Dystrykt"; + +/* Label for the Do/Si field, pertinent to addresses in South Korea. Do/Si refers to provincial level divisions, and specifying this enhances address accuracy in autofill settings. */ +"Addresses.EditAddress.AutofillAddressDoSi.v129" = "Do/Si"; + +/* Label for the Eircode field, specific to Ireland. It's a unique postal code system that helps in precise location identification, enhancing the effectiveness of autofill. */ +"Addresses.EditAddress.AutofillAddressEircode.v129" = "Eircode"; + +/* Label for the email address field, where users input their email. Critical for digital communication and account verification, this ensures email addresses are autofilled accurately. */ +"Addresses.EditAddress.AutofillAddressEmail.v129" = "E-mail"; + +/* Label for the emirate field, essential for addresses in the United Arab Emirates. Including emirate details ensures the autofill feature accurately represents user addresses. */ +"Addresses.EditAddress.AutofillAddressEmirate.v129" = "Emirat"; + +/* Label for the field where users specify the name of an island, if applicable. Important for addresses in archipelagic regions, aiding in precise location identification during autofill. */ +"Addresses.EditAddress.AutofillAddressIsland.v129" = "Wyspa"; + +/* Label for the field where the user inputs their full name as part of an address form. Essential for personalized form submissions and ensuring information accuracy in autofilled forms. */ +"Addresses.EditAddress.AutofillAddressName.v129" = "Imię i nazwisko"; + +/* Label for the field where users can input the name of their neighborhood. This detail adds precision to addresses, especially in densely populated areas, improving the accuracy of autofill. */ +"Addresses.EditAddress.AutofillAddressNeighborhood.v129" = "Neighborhood"; + +/* Label for the oblast field, relevant for addresses in countries like Russia and Ukraine. Oblasts are a significant administrative division, and their specification aids in autofill accuracy. */ +"Addresses.EditAddress.AutofillAddressOblast.v129" = "Obwód"; + +/* Label for the input field designated for the organization's name related to the address. Helps in distinguishing addresses used for business or personal purposes in autofill settings. */ +"Addresses.EditAddress.AutofillAddressOrganization.v129" = "Organizacja"; + +/* Label for the parish field, significant in places where parishes are used for local administration and addressing. Ensures users can specify parish details for better autofill accuracy. */ +"Addresses.EditAddress.AutofillAddressParish.v129" = "Parish"; + +/* Label for the PIN (Postal Index Number) field, used in India. It's a code representing a specific area, crucial for accurate mail delivery and autofill functionality. */ +"Addresses.EditAddress.AutofillAddressPin.v129" = "Pin"; + +/* Label for the postal code field, universally used in address forms to specify the area code for mail sorting. Essential for autofill to ensure mail and services are accurately routed. */ +"Addresses.EditAddress.AutofillAddressPostalCode.v129" = "Kod pocztowy"; + +/* Label for the post town field, used primarily in the UK and some other regions for mail sorting. Essential for users in applicable areas to specify for correct mail delivery through autofill. */ +"Addresses.EditAddress.AutofillAddressPostTown.v129" = "Post town"; + +/* Label for the prefecture field, essential for addresses in countries like Japan where prefectures are a major administrative division. Aids in precise location specification for autofill. */ +"Addresses.EditAddress.AutofillAddressPrefecture.v129" = "Prefektura"; + +/* Label for the province field, required in countries where provinces are a primary administrative division. Helps in pinpointing the user's location more accurately for autofill purposes. */ +"Addresses.EditAddress.AutofillAddressProvince.v129" = "Prowincja"; + +/* Label for the state field, a necessary component of addresses in many countries, especially the USA. It ensures that state-specific details are correctly filled in forms using autofill. */ +"Addresses.EditAddress.AutofillAddressState.v129" = "Stan"; + +/* Label for the suburb field, enabling users to add suburb details to their address. This is important for accurate delivery and services in suburban areas, enhancing autofill functionality. */ +"Addresses.EditAddress.AutofillAddressSuburb.v129" = "Suburb"; + +/* Label for the telephone number field, allowing users to input their contact number. This is essential for communication and service provision, ensuring contact details are autofilled correctly. */ +"Addresses.EditAddress.AutofillAddressTel.v129" = "Telefon"; + +/* Label for the input field for the townland, a specific type of land division used in rural areas. Enhances address detail for users in regions where townlands are a common addressing component. */ +"Addresses.EditAddress.AutofillAddressTownland.v129" = "Townland"; + +/* Label for the field to input the name of a village or township. This is crucial for addresses in rural areas, ensuring the autofill feature accurately captures all necessary geographical details. */ +"Addresses.EditAddress.AutofillAddressVillageTownship.v129" = "Wioska lub township"; + +/* Label for the ZIP code field, primarily used in the United States for mail sorting. Key for autofill to accurately complete addresses for shipping, billing, and service provision. */ +"Addresses.EditAddress.AutofillAddressZip.v129" = "Kod ZIP"; + +/* Label for the button to cancel the current autofill operation or exit the form without saving changes. Provides users with an option to back out of a process without making any modifications. */ +"Addresses.EditAddress.AutofillCancelButton.v129" = "Anuluj"; + +/* Title for the option allowing users to edit an existing saved address. This is used within the settings for autofill, enabling users to update their address details for accurate form autofilling. */ +"Addresses.EditAddress.AutofillEditAddressTitle.v129" = "Edytuj adres"; + +/* Title for the input field where users can enter their street address. This is used within the settings for autofill, allowing users to provide their street address for accurate form autofilling. */ +"Addresses.EditAddress.AutofillEditStreetAddressTitle.v129" = "Adres"; + +/* Label for the button to save the current address details entered or edited by the user. This action confirms the user's changes and updates their autofill settings accordingly. */ +"Addresses.EditAddress.AutofillSaveButton.v129" = "Zachowaj"; + +/* Title for the option allowing users to view an existing saved address. This is used within the settings for autofill, enabling users to see their address details for accurate form autofilling. */ +"Addresses.EditAddress.AutofillViewAddressTitle.v129" = "Wyświetl adres"; + +/* Button label for closing the view where user can view their address info. */ +"Addresses.EditAddress.CloseNavBarButtonLabel.v129" = "Zamknij"; + +/* Button label for editing the address details shown in the form. */ +"Addresses.EditAddress.EditNavBarButtonLabel.v129" = "Edytuj"; + +/* Title for button that offers the user the option to remove an address. */ +"Addresses.EditAddress.RemoveAddressButtonTitle.v129" = "Usuń adres"; + +/* Toast message confirming that an address has been successfully removed. */ +"Addresses.Toast.AddressRemovedConfirmation.v129" = "Usunięto adres"; + +/* Toast message confirming that an address has been successfully saved. */ +"Addresses.Toast.AddressSavedConfirmation.v129" = "Zachowano adres"; + +/* Toast message indicating an error occurred while trying to save an address. */ +"Addresses.Toast.AddressSaveError.v129" = "Nie można zachować adresu"; + +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Nie można usunąć adresu"; + +/* Suggestion to try again after an error occurred while trying to save an address. */ +"Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Spróbuj ponownie"; + +/* Toast message confirming that an address has been successfully updated. */ +"Addresses.Toast.AddressUpdatedConfirmation.v129" = "Uaktualniono adres"; + diff --git a/firefox-ios/Shared/Supporting Files/pl.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/pl.lproj/EnhancedTrackingProtection.strings index 86c6240fc6c4..f25c12ac87a2 100644 --- a/firefox-ios/Shared/Supporting Files/pl.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/pl.lproj/EnhancedTrackingProtection.strings @@ -22,8 +22,19 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Niezabezpieczone połączenie"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Nie znaleziono żadnych elementów śledzących"; + +/* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Ciasteczka śledzące między witrynami: %@"; + +/* Text to let users know how many fingerprinters were blocked on the current website. The placeholder will show the number of fingerprinters detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.Fingerprinter.v129" = "Elementy śledzące przez zbieranie informacji o konfiguracji: %@"; + +/* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Elementy śledzące serwisów społecznościowych: %@"; + +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Zablokowane elementy śledzące: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -35,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Wyłączono ochronę"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Ochrona jest włączona. Jeśli coś zauważymy, damy Ci znać."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "Połączenie nie jest zabezpieczone."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Zachowaj ostrożność na tej witrynie"; @@ -54,6 +67,9 @@ /* A switch to disable enhanced tracking protection inside the menu. */ "Menu.EnhancedTrackingProtection.SwitchOff.Text.v128" = "Ochrona jest wyłączona. Zalecamy ponowne jej włączenie."; +/* A switch to disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.SwitchOff.Text.v129" = "Ochrona jest wyłączona. Zalecamy ponowne jej włączenie."; + /* A switch to disable enhanced tracking protection inside the menu. */ "Menu.EnhancedTrackingProtection.SwitchOn.Text.v128" = "Jeśli coś na tej witrynie nie działa, spróbuj ją wyłączyć."; diff --git a/firefox-ios/Shared/Supporting Files/pl.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/pl.lproj/Microsurvey.strings index d8d0b65b71aa..a02df5a96ab3 100644 --- a/firefox-ios/Shared/Supporting Files/pl.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/pl.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Nie zaznaczone"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Ankieta"; + diff --git a/firefox-ios/Shared/Supporting Files/pl.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/pl.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..a6ed4b346454 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/pl.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Wczytaj ponownie"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "Wystąpił błąd SSL i nie można nawiązać zabezpieczonego połączenia z serwerem."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "Zachowaj ostrożność. Coś tu nie gra."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "Spróbuj połączyć się na innym urządzeniu. Sprawdź modem lub router. Rozłącz się i ponownie połącz z siecią Wi-Fi."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "Wystąpił problem z połączeniem z Internetem."; + diff --git a/firefox-ios/Shared/Supporting Files/pl.lproj/PasswordAutofill.strings b/firefox-ios/Shared/Supporting Files/pl.lproj/PasswordAutofill.strings index 5d7b7f658e35..b7e7e543e4f7 100644 --- a/firefox-ios/Shared/Supporting Files/pl.lproj/PasswordAutofill.strings +++ b/firefox-ios/Shared/Supporting Files/pl.lproj/PasswordAutofill.strings @@ -1,3 +1,6 @@ +/* This label is used in a cell found in the list of autofill login options in place of an actual username to denote that no username was saved for this login */ +"PasswordAutofill.LoginListCellNoUsername.v129" = "(bez nazwy użytkownika)"; + /* This label is used for a button in the password list screen allowing users to manage their saved passwords. It's meant to direct users to where they can add, remove, or edit their saved passwords. */ "PasswordAutofill.ManagePasswordsButton.v124" = "Zarządzaj hasłami"; diff --git a/firefox-ios/Shared/Supporting Files/pl.lproj/ScanQRCode.strings b/firefox-ios/Shared/Supporting Files/pl.lproj/ScanQRCode.strings new file mode 100644 index 000000000000..069ce497b62b --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/pl.lproj/ScanQRCode.strings @@ -0,0 +1,9 @@ +/* Allow button to open URL from scanned QR Code */ +"ScanQRCode.ConfirmOpenURL.AllowButton.v129" = "Zezwól"; + +/* Deny button to cancel opening URL from scanned QR Code */ +"ScanQRCode.ConfirmOpenURL.DenyButton.v129" = "Odmów"; + +/* Text of the prompt to ask user permission to open a URL from a scanned QR code. Placeholder is app name. */ +"ScanQRCode.ConfirmOpenURL.Message.v129" = "Czy zezwolić aplikacji %@ na otwarcie?"; + diff --git a/firefox-ios/Shared/Supporting Files/pl.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/pl.lproj/Settings.strings index fcbe938cd8ed..c3c65f001ff1 100644 --- a/firefox-ios/Shared/Supporting Files/pl.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/pl.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Zarządzaj adresami"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "Adres dla „%@”"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "Zachowane adresy"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Zachowuj adresy w przeglądarce %@"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "Bezpiecznie zachowuj swoje dane, aby później mieć do nich szybki dostęp."; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "W tym numery telefonów i adresy e-mail"; diff --git a/firefox-ios/Shared/Supporting Files/pl.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/pl.lproj/Toolbar.strings new file mode 100644 index 000000000000..68e4bc3db0b3 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/pl.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Nowa karta"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Zamknij tę kartę"; + diff --git a/firefox-ios/Shared/Supporting Files/pt-BR.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/pt-BR.lproj/EditAddress.strings index 6cd31595768e..0874f765ac7d 100644 --- a/firefox-ios/Shared/Supporting Files/pt-BR.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/pt-BR.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "O endereço não pôde ser salvo"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "O endereço não pôde ser removido"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Tentar novamente"; diff --git a/firefox-ios/Shared/Supporting Files/pt-BR.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/pt-BR.lproj/EnhancedTrackingProtection.strings index b9743aeabe08..2db5546d452d 100644 --- a/firefox-ios/Shared/Supporting Files/pt-BR.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/pt-BR.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Conexão não segura"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Nenhum rastreador encontrado"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Cookies de rastreamento entre sites: %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Rastreadores de mídias sociais: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Rastreadores bloqueados: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Você desativou proteções"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Você está protegido. Se detectarmos algo, avisaremos você."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "Sua conexão não é segura."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Tenha cuidado neste site"; diff --git a/firefox-ios/Shared/Supporting Files/pt-BR.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/pt-BR.lproj/Microsurvey.strings index f4533738be92..9e9f25aa683c 100644 --- a/firefox-ios/Shared/Supporting Files/pt-BR.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/pt-BR.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Não selecionado"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Pesquisa de opinião"; + diff --git a/firefox-ios/Shared/Supporting Files/pt-BR.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/pt-BR.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..f22292ec0249 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/pt-BR.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Recarregar"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "Ocorreu um erro de SSL e não foi possível estabelecer uma conexão segura com o servidor."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "Tenha cuidado. Algo não parece estar certo."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "Experimente se conectar usando outro dispositivo. Verifique seu modem ou roteador. Desconecte e reconecte ao WiFi."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "Parece que há um problema na sua conexão com a internet."; + diff --git a/firefox-ios/Shared/Supporting Files/pt-BR.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/pt-BR.lproj/Settings.strings index e8b9588a7d29..28cf8903ebf4 100644 --- a/firefox-ios/Shared/Supporting Files/pt-BR.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/pt-BR.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Gerenciar endereços"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "Endereço de %@"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "ENDEREÇOS SALVOS"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Salvar endereços no %@"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "Salvar suas informações com segurança para ter acesso rápido a elas mais tarde."; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "Incluir números de telefone e endereços de email"; diff --git a/firefox-ios/Shared/Supporting Files/pt-BR.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/pt-BR.lproj/Toolbar.strings new file mode 100644 index 000000000000..3d5607320715 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/pt-BR.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Nova aba"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Fechar esta aba"; + diff --git a/firefox-ios/Shared/Supporting Files/pt-PT.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/pt-PT.lproj/EditAddress.strings index f70bda954c20..4cd0f716fb77 100644 --- a/firefox-ios/Shared/Supporting Files/pt-PT.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/pt-PT.lproj/EditAddress.strings @@ -41,7 +41,7 @@ "Addresses.EditAddress.AutofillAddressEircode.v129" = "Eircode"; /* Label for the email address field, where users input their email. Critical for digital communication and account verification, this ensures email addresses are autofilled accurately. */ -"Addresses.EditAddress.AutofillAddressEmail.v129" = "Email"; +"Addresses.EditAddress.AutofillAddressEmail.v129" = "E-mail"; /* Label for the emirate field, essential for addresses in the United Arab Emirates. Including emirate details ensures the autofill feature accurately represents user addresses. */ "Addresses.EditAddress.AutofillAddressEmirate.v129" = "Emirado"; @@ -62,10 +62,10 @@ "Addresses.EditAddress.AutofillAddressOrganization.v129" = "Organização"; /* Label for the parish field, significant in places where parishes are used for local administration and addressing. Ensures users can specify parish details for better autofill accuracy. */ -"Addresses.EditAddress.AutofillAddressParish.v129" = "Freguesia"; +"Addresses.EditAddress.AutofillAddressParish.v129" = "Paróquia"; /* Label for the PIN (Postal Index Number) field, used in India. It's a code representing a specific area, crucial for accurate mail delivery and autofill functionality. */ -"Addresses.EditAddress.AutofillAddressPin.v129" = "Afixar"; +"Addresses.EditAddress.AutofillAddressPin.v129" = "PIN"; /* Label for the postal code field, universally used in address forms to specify the area code for mail sorting. Essential for autofill to ensure mail and services are accurately routed. */ "Addresses.EditAddress.AutofillAddressPostalCode.v129" = "Código postal"; @@ -92,10 +92,10 @@ "Addresses.EditAddress.AutofillAddressTownland.v129" = "Cidade"; /* Label for the field to input the name of a village or township. This is crucial for addresses in rural areas, ensuring the autofill feature accurately captures all necessary geographical details. */ -"Addresses.EditAddress.AutofillAddressVillageTownship.v129" = "Aldeia ou município"; +"Addresses.EditAddress.AutofillAddressVillageTownship.v129" = "Vila ou município"; /* Label for the ZIP code field, primarily used in the United States for mail sorting. Key for autofill to accurately complete addresses for shipping, billing, and service provision. */ -"Addresses.EditAddress.AutofillAddressZip.v129" = "Código postal"; +"Addresses.EditAddress.AutofillAddressZip.v129" = "Código ZIP"; /* Label for the button to cancel the current autofill operation or exit the form without saving changes. Provides users with an option to back out of a process without making any modifications. */ "Addresses.EditAddress.AutofillCancelButton.v129" = "Cancelar"; @@ -128,7 +128,10 @@ "Addresses.Toast.AddressSavedConfirmation.v129" = "Endereço guardado"; /* Toast message indicating an error occurred while trying to save an address. */ -"Addresses.Toast.AddressSaveError.v129" = "O endereço não pôde ser guardado"; +"Addresses.Toast.AddressSaveError.v129" = "Não foi possível guardar o endereço"; + +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "O endereço não pôde ser removido"; /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Tentar novamente"; diff --git a/firefox-ios/Shared/Supporting Files/pt-PT.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/pt-PT.lproj/EnhancedTrackingProtection.strings index 6c6c16637257..b6ae16087aa9 100644 --- a/firefox-ios/Shared/Supporting Files/pt-PT.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/pt-PT.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Ligação insegura"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Não foram encontrados rastreadores"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Cookies de monitorização entre sites: %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Rastreadores de redes sociais: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Rastreadores bloqueados: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Desativou as proteções"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Está protegido. Se detetarmos algo, iremos fornecer-lhe essa informação."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "A sua ligação não é segura."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Tenha cuidado neste site"; @@ -64,7 +68,7 @@ "Menu.EnhancedTrackingProtection.SwitchOff.Text.v128" = "As proteções estão desativadas. Sugerimos que a sua reativação."; /* A switch to disable enhanced tracking protection inside the menu. */ -"Menu.EnhancedTrackingProtection.SwitchOff.Text.v129" = "As proteções estão desativadas. Sugerimos que os ligue novamente."; +"Menu.EnhancedTrackingProtection.SwitchOff.Text.v129" = "As proteções estão desativadas. Sugerimos que as ligue novamente."; /* A switch to disable enhanced tracking protection inside the menu. */ "Menu.EnhancedTrackingProtection.SwitchOn.Text.v128" = "Se parecer que algo não está a funcionar neste site, experimente desativar."; diff --git a/firefox-ios/Shared/Supporting Files/pt-PT.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/pt-PT.lproj/Microsurvey.strings index a676e54e3a52..b98f2157994a 100644 --- a/firefox-ios/Shared/Supporting Files/pt-PT.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/pt-PT.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Não selecionado"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Inquérito"; + diff --git a/firefox-ios/Shared/Supporting Files/pt-PT.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/pt-PT.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..6f9a2764824d --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/pt-PT.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Recarregar"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "Ocorreu um erro de SSL e não foi possível estabelecer uma ligação segura com o servidor."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "Tenha cuidado. Algo não parece bem."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "Tente ligar-se num dispositivo diferente. Verifique o seu modem ou router. Desligue-se e volte a ligar-se ao Wi-Fi."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "Parece que há um problema com a sua ligação à Internet."; + diff --git a/firefox-ios/Shared/Supporting Files/pt-PT.lproj/ScanQRCode.strings b/firefox-ios/Shared/Supporting Files/pt-PT.lproj/ScanQRCode.strings index 6d7a2c107a58..d4c25be1f145 100644 --- a/firefox-ios/Shared/Supporting Files/pt-PT.lproj/ScanQRCode.strings +++ b/firefox-ios/Shared/Supporting Files/pt-PT.lproj/ScanQRCode.strings @@ -5,5 +5,5 @@ "ScanQRCode.ConfirmOpenURL.DenyButton.v129" = "Recusar"; /* Text of the prompt to ask user permission to open a URL from a scanned QR code. Placeholder is app name. */ -"ScanQRCode.ConfirmOpenURL.Message.v129" = "Permitir que o %@ abra?"; +"ScanQRCode.ConfirmOpenURL.Message.v129" = "Permitir que %@ abra?"; diff --git a/firefox-ios/Shared/Supporting Files/pt-PT.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/pt-PT.lproj/Settings.strings index 66f31fc9d05e..4e9b68793000 100644 --- a/firefox-ios/Shared/Supporting Files/pt-PT.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/pt-PT.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Gerir endereços"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "Endereço para %@"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "ENDEREÇOS GUARDADOS"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Guardar endereços no %@"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "Guarde a sua informação com segurança para obter acesso rápido à mesma mais tarde."; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "Inclui números de telefone e endereços de e-mail"; diff --git a/firefox-ios/Shared/Supporting Files/pt-PT.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/pt-PT.lproj/Toolbar.strings new file mode 100644 index 000000000000..45d8fca515c3 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/pt-PT.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Novo separador"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Fechar este separador"; + diff --git a/firefox-ios/Shared/Supporting Files/rm.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/rm.lproj/EditAddress.strings index 497908034cf3..3b8768bba291 100644 --- a/firefox-ios/Shared/Supporting Files/rm.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/rm.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "Betg reussì da memorisar l’adressa"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Betg reussì dad allontanar l’adressa"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Empruvar anc ina giada"; diff --git a/firefox-ios/Shared/Supporting Files/rm.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/rm.lproj/EnhancedTrackingProtection.strings index eacec1784aea..649bb4f8df3a 100644 --- a/firefox-ios/Shared/Supporting Files/rm.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/rm.lproj/EnhancedTrackingProtection.strings @@ -31,8 +31,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Fastizaders da social media: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Fastizaders bloccads: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,8 +43,7 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Ti has deactivà las protecziuns"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Ti es protegì. Sche nus scuvrin insatge, t’infurmain nus."; /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ diff --git a/firefox-ios/Shared/Supporting Files/rm.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/rm.lproj/Toolbar.strings new file mode 100644 index 000000000000..a2c37eac1fda --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/rm.lproj/Toolbar.strings @@ -0,0 +1,3 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Nov tab"; + diff --git a/firefox-ios/Shared/Supporting Files/ru.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/ru.lproj/EditAddress.strings index 8a0f6ccfd821..38d6fe16e6ab 100644 --- a/firefox-ios/Shared/Supporting Files/ru.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/ru.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "Не удалось сохранить адрес"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Не удалось удалить адрес"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Попробовать снова"; diff --git a/firefox-ios/Shared/Supporting Files/ru.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/ru.lproj/EnhancedTrackingProtection.strings index d1e3d25e5a3c..34bd742d18e7 100644 --- a/firefox-ios/Shared/Supporting Files/ru.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/ru.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Незащищённое соединение"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Трекеров не найдено"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Межсайтовые отслеживающие куки: %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Трекеры социальных сетей: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Заблокировано трекеров: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Вы отключили защиту"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Вы защищены. Если мы что-то заметим, мы сообщим вам."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "Ваше соединение не защищено."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Будьте осторожны на этом сайте"; diff --git a/firefox-ios/Shared/Supporting Files/ru.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/ru.lproj/Microsurvey.strings index a79498557dc3..dcb8f46e83ce 100644 --- a/firefox-ios/Shared/Supporting Files/ru.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/ru.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Выбор снят"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Опрос"; + diff --git a/firefox-ios/Shared/Supporting Files/ru.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/ru.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..db4dc6871dc2 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ru.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Обновить"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "Произошла ошибка SSL, и не удалось установить защищённое соединение с сервером."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "Будьте осторожны. Что-то выглядит неправильно."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "Попробуйте подключиться на другом устройстве. Проверьте ваш модем или маршрутизатор. Отключитесь и снова подключитесь к Wi-Fi."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "Похоже, есть проблема с вашим интернет-соединением."; + diff --git a/firefox-ios/Shared/Supporting Files/ru.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/ru.lproj/Settings.strings index 8b0218ae8910..ee1358dca57b 100644 --- a/firefox-ios/Shared/Supporting Files/ru.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/ru.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Управление адресами"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "Адрес для %@"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "СОХРАНЁННЫЕ АДРЕСА"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Сохранять адреса в %@"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "Надежно сохраните вашу информацию, чтобы получить к ней быстрый доступ в будущем."; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "Включает номера телефонов и адреса электронной почты"; diff --git a/firefox-ios/Shared/Supporting Files/ru.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/ru.lproj/Toolbar.strings new file mode 100644 index 000000000000..5dbc6cf45a90 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ru.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Новая вкладка"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Закрыть эту вкладку"; + diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/Credentials.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/Credentials.strings new file mode 100644 index 000000000000..ad684d07d5db --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/Credentials.strings @@ -0,0 +1,6 @@ +/* Message shown when you enter Logins & Passwords without having a device passcode set. */ +"Logins.DevicePasscodeRequired.Message.v122" = "ᱫᱟᱱᱟᱝ ᱥᱟᱵᱟᱫᱽ ᱠᱚ ᱟᱡ ᱛᱮ ᱥᱟᱺᱪᱟᱣ ᱞᱟᱹᱜᱤᱫ, ᱢᱮᱫᱦᱟᱸ ID, ᱴᱤᱯᱟᱹᱶ ID ᱟᱨᱵᱟᱝᱠᱷᱟᱱ ᱢᱤᱫᱴᱟᱝ ᱥᱟᱫᱷᱚᱱ ᱫᱟᱱᱟᱝᱠᱳᱰ ᱥᱟᱺᱪᱟᱣ ᱢᱮ ᱾"; + +/* Message shown when you enter Payment Methods without having a device passcode set. */ +"Logins.PaymentMethods.DevicePasscodeRequired.Message.v124.v2" = "ᱟᱡ ᱛᱮ ᱠᱨᱮᱰᱤᱴ ᱠᱟᱰ ᱥᱟᱺᱪᱟᱣ ᱟᱨ ᱯᱮᱨᱮᱡ ᱞᱟᱹᱜᱤᱫ, ᱢᱮᱫᱦᱟᱸ ID, ᱴᱤᱯᱟᱹᱶ ID ᱟᱨᱵᱟᱝᱠᱷᱟᱱ ᱢᱤᱫᱴᱟᱝ ᱥᱟᱫᱷᱚᱱ ᱫᱟᱱᱟᱝᱠᱳᱰ ᱥᱟᱺᱪᱟᱣ ᱢᱮ ᱾"; + diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/CustomizeFirefoxHome.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/CustomizeFirefoxHome.strings index 26bd804839ae..0c9880aee25f 100644 --- a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/CustomizeFirefoxHome.strings +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/CustomizeFirefoxHome.strings @@ -1,3 +1,6 @@ +/* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle Bookmarks section on the Firefox homepage on or off */ +"Settings.Home.Option.Bookmarks.v128" = "ᱵᱩᱠᱢᱟᱨᱠ ᱠᱚ"; + /* In the settings menu, in the Firefox homepage customization section, this is the subtitle for the option that allows users to turn the Pocket Recommendations section on the Firefox homepage on or off. The placeholder is the pocket app name. */ "Settings.Home.Option.ThoughtProvokingStories.subtitle.v116" = "%@ ᱫᱟᱨᱟᱭ ᱛᱮ ᱫᱟᱲᱮ ᱮᱢ ᱠᱟᱱ ᱚᱱᱚᱞᱠᱚ"; diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/Edit Card.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/Edit Card.strings index 66d3ab3f3400..42b6ae0be173 100644 --- a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/Edit Card.strings +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/Edit Card.strings @@ -1,3 +1,3 @@ /* Title label for the view where user can edit their credit card info */ -"CreditCard.EditCard.EditCreditCardTitle.v113" = "ᱠᱨᱮᱰᱤᱴ ᱠᱟᱰ ᱥᱟᱯᱲᱟᱣ ᱢᱮ"; +"CreditCard.EditCard.EditCreditCardTitle.v122" = "ᱠᱟᱰ ᱥᱟᱯᱲᱟᱣ ᱢᱮ"; diff --git a/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/EditAddress.strings new file mode 100644 index 000000000000..77bef59da5b6 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sat-Olck.lproj/EditAddress.strings @@ -0,0 +1,138 @@ +/* Title for the cancel button in the remove address alert. */ +"Addresses.EditAddress.Alert.CancelButton.v129" = "ᱵᱟᱹᱰᱨᱟᱹ"; + +/* Message explaining the consequences of removing an address from all synced devices. */ +"Addresses.EditAddress.Alert.Message.v129" = "ᱱᱚᱶᱟ ᱴᱷᱤᱠᱬᱟᱹ ᱫᱚ ᱟᱢᱟᱜ ᱡᱷᱚᱛᱚ ᱟᱹᱭᱩᱨ ᱢᱤᱫ ᱟᱠᱟᱱ ᱥᱟᱫᱷᱚᱱ ᱠᱷᱚᱱ ᱚᱪᱚᱜᱚᱜᱼᱟ ᱾"; + +/* Title for the remove button in the remove address alert. */ +"Addresses.EditAddress.Alert.RemoveButton.v129" = "ᱚᱪᱚᱜᱽ ᱢᱮ"; + +/* Title for the alert indicating the action to remove an address. */ +"Addresses.EditAddress.Alert.Title.v129" = "ᱴᱷᱤᱠᱬᱟᱹ ᱚᱪᱚᱜᱽ ᱢᱮ"; + +/* Title for the interface option where users can add a new address for autofill purposes. This facilitates quicker form completion by automatically filling in the user's address information. */ +"Addresses.EditAddress.AutofillAddAddressTitle.v129" = "ᱴᱷᱤᱠᱬᱟᱹ ᱥᱮᱞᱮᱫ ᱢᱮ"; + +/* Label for the area field, allowing users to specify a particular area within a city or region. This detail can improve the specificity and accuracy of autofilled addresses. */ +"Addresses.EditAddress.AutofillAddressArea.v129" = "ᱡᱟᱭᱜᱟ"; + +/* Label for the field where users input the city part of their address. This information is crucial for mail delivery and service provision, ensuring accurate city identification in autofill settings. */ +"Addresses.EditAddress.AutofillAddressCity.v129" = "ᱥᱚᱦᱚᱨ"; + +/* Label for the field where users can specify just the country, used in contexts where full address details are not required. Simplifies autofill when only country information is necessary. */ +"Addresses.EditAddress.AutofillAddressCountryOnly.v129" = "ᱫᱤᱥᱚᱢ"; + +/* Label for the country or region field in address forms, allowing users to specify their country or territorial region. This is fundamental for international mail and services, ensuring autofill accuracy across borders. */ +"Addresses.EditAddress.AutofillAddressCountryRegion.v129" = "ᱫᱤᱥᱚᱢ ᱟᱨᱵᱟᱝ ᱡᱟᱭᱜᱟ"; + +/* Label for the county field, crucial for addressing in regions where county lines play a key role in postal services. Enhances autofill accuracy by including county information. */ +"Addresses.EditAddress.AutofillAddressCounty.v129" = "ᱫᱤᱥᱚᱢ"; + +/* Label for the department field, used in countries like France and Colombia where departments are a key administrative division. Ensures correct departmental information is autofilled. */ +"Addresses.EditAddress.AutofillAddressDepartment.v129" = "ᱛᱟᱞᱢᱟ"; + +/* Label for the district field in the address form, allowing users to specify their district for more precise location identification. This aids in refining address details for accurate autofill. */ +"Addresses.EditAddress.AutofillAddressDistrict.v129" = "ᱡᱤᱞᱟ"; + +/* Label for the Do/Si field, pertinent to addresses in South Korea. Do/Si refers to provincial level divisions, and specifying this enhances address accuracy in autofill settings. */ +"Addresses.EditAddress.AutofillAddressDoSi.v129" = "Do/Si"; + +/* Label for the Eircode field, specific to Ireland. It's a unique postal code system that helps in precise location identification, enhancing the effectiveness of autofill. */ +"Addresses.EditAddress.AutofillAddressEircode.v129" = "ᱤᱭᱮᱨᱠᱳᱰ"; + +/* Label for the email address field, where users input their email. Critical for digital communication and account verification, this ensures email addresses are autofilled accurately. */ +"Addresses.EditAddress.AutofillAddressEmail.v129" = "ᱤᱢᱮᱞ"; + +/* Label for the emirate field, essential for addresses in the United Arab Emirates. Including emirate details ensures the autofill feature accurately represents user addresses. */ +"Addresses.EditAddress.AutofillAddressEmirate.v129" = "ᱮᱢᱤᱨᱮᱴ"; + +/* Label for the field where users specify the name of an island, if applicable. Important for addresses in archipelagic regions, aiding in precise location identification during autofill. */ +"Addresses.EditAddress.AutofillAddressIsland.v129" = "ᱟᱭᱞᱮᱱᱰ"; + +/* Label for the field where the user inputs their full name as part of an address form. Essential for personalized form submissions and ensuring information accuracy in autofilled forms. */ +"Addresses.EditAddress.AutofillAddressName.v129" = "ᱧᱩᱛᱩᱢ"; + +/* Label for the field where users can input the name of their neighborhood. This detail adds precision to addresses, especially in densely populated areas, improving the accuracy of autofill. */ +"Addresses.EditAddress.AutofillAddressNeighborhood.v129" = "ᱥᱩᱨᱥᱩᱨᱦᱚᱲ"; + +/* Label for the oblast field, relevant for addresses in countries like Russia and Ukraine. Oblasts are a significant administrative division, and their specification aids in autofill accuracy. */ +"Addresses.EditAddress.AutofillAddressOblast.v129" = "ᱚᱵᱞᱟᱥᱴ"; + +/* Label for the input field designated for the organization's name related to the address. Helps in distinguishing addresses used for business or personal purposes in autofill settings. */ +"Addresses.EditAddress.AutofillAddressOrganization.v129" = "ᱜᱟᱶᱛᱟ"; + +/* Label for the parish field, significant in places where parishes are used for local administration and addressing. Ensures users can specify parish details for better autofill accuracy. */ +"Addresses.EditAddress.AutofillAddressParish.v129" = "ᱯᱮᱨᱤᱥ"; + +/* Label for the PIN (Postal Index Number) field, used in India. It's a code representing a specific area, crucial for accurate mail delivery and autofill functionality. */ +"Addresses.EditAddress.AutofillAddressPin.v129" = "ᱞᱟᱴᱷᱟ"; + +/* Label for the postal code field, universally used in address forms to specify the area code for mail sorting. Essential for autofill to ensure mail and services are accurately routed. */ +"Addresses.EditAddress.AutofillAddressPostalCode.v129" = "ᱯᱚᱥᱴ ᱠᱳᱰ"; + +/* Label for the post town field, used primarily in the UK and some other regions for mail sorting. Essential for users in applicable areas to specify for correct mail delivery through autofill. */ +"Addresses.EditAddress.AutofillAddressPostTown.v129" = "ᱯᱚᱥᱴ ᱴᱚᱣᱩᱱ"; + +/* Label for the prefecture field, essential for addresses in countries like Japan where prefectures are a major administrative division. Aids in precise location specification for autofill. */ +"Addresses.EditAddress.AutofillAddressPrefecture.v129" = "ᱯᱨᱤᱯᱷᱮᱠᱪᱚᱨ"; + +/* Label for the province field, required in countries where provinces are a primary administrative division. Helps in pinpointing the user's location more accurately for autofill purposes. */ +"Addresses.EditAddress.AutofillAddressProvince.v129" = "ᱯᱚᱱᱚᱛ"; + +/* Label for the state field, a necessary component of addresses in many countries, especially the USA. It ensures that state-specific details are correctly filled in forms using autofill. */ +"Addresses.EditAddress.AutofillAddressState.v129" = "ᱯᱚᱱᱚᱛ"; + +/* Label for the suburb field, enabling users to add suburb details to their address. This is important for accurate delivery and services in suburban areas, enhancing autofill functionality. */ +"Addresses.EditAddress.AutofillAddressSuburb.v129" = "ᱥᱟᱹᱵᱟᱹᱨᱵ"; + +/* Label for the telephone number field, allowing users to input their contact number. This is essential for communication and service provision, ensuring contact details are autofilled correctly. */ +"Addresses.EditAddress.AutofillAddressTel.v129" = "ᱯᱷᱚᱱ"; + +/* Label for the input field for the townland, a specific type of land division used in rural areas. Enhances address detail for users in regions where townlands are a common addressing component. */ +"Addresses.EditAddress.AutofillAddressTownland.v129" = "ᱴᱟᱣᱱᱞᱮᱱᱰ"; + +/* Label for the field to input the name of a village or township. This is crucial for addresses in rural areas, ensuring the autofill feature accurately captures all necessary geographical details. */ +"Addresses.EditAddress.AutofillAddressVillageTownship.v129" = "ᱟᱹᱛᱩ ᱟᱨᱵᱟᱝ ᱴᱚᱞᱟ"; + +/* Label for the ZIP code field, primarily used in the United States for mail sorting. Key for autofill to accurately complete addresses for shipping, billing, and service provision. */ +"Addresses.EditAddress.AutofillAddressZip.v129" = "ZIP ᱠᱳᱰ"; + +/* Label for the button to cancel the current autofill operation or exit the form without saving changes. Provides users with an option to back out of a process without making any modifications. */ +"Addresses.EditAddress.AutofillCancelButton.v129" = "ᱵᱟᱹᱰᱨᱟᱹ"; + +/* Title for the option allowing users to edit an existing saved address. This is used within the settings for autofill, enabling users to update their address details for accurate form autofilling. */ +"Addresses.EditAddress.AutofillEditAddressTitle.v129" = "ᱴᱷᱤᱠᱬᱟᱹ ᱥᱟᱯᱲᱟᱣ ᱢᱮ"; + +/* Title for the input field where users can enter their street address. This is used within the settings for autofill, allowing users to provide their street address for accurate form autofilling. */ +"Addresses.EditAddress.AutofillEditStreetAddressTitle.v129" = "ᱥᱚᱰᱚᱠ ᱴᱷᱤᱠᱬᱟᱹ"; + +/* Label for the button to save the current address details entered or edited by the user. This action confirms the user's changes and updates their autofill settings accordingly. */ +"Addresses.EditAddress.AutofillSaveButton.v129" = "ᱥᱟᱺᱪᱟᱣ ᱢᱮ"; + +/* Title for the option allowing users to view an existing saved address. This is used within the settings for autofill, enabling users to see their address details for accurate form autofilling. */ +"Addresses.EditAddress.AutofillViewAddressTitle.v129" = "ᱴᱷᱤᱠᱬᱟᱹ ᱧᱮᱞ ᱢᱮ"; + +/* Button label for closing the view where user can view their address info. */ +"Addresses.EditAddress.CloseNavBarButtonLabel.v129" = "ᱵᱚᱸᱫᱚᱭ ᱢᱮ"; + +/* Button label for editing the address details shown in the form. */ +"Addresses.EditAddress.EditNavBarButtonLabel.v129" = "ᱥᱟᱯᱲᱟᱣ"; + +/* Title for button that offers the user the option to remove an address. */ +"Addresses.EditAddress.RemoveAddressButtonTitle.v129" = "ᱴᱷᱤᱠᱬᱟᱹ ᱚᱪᱚᱜᱽ ᱢᱮ"; + +/* Toast message confirming that an address has been successfully removed. */ +"Addresses.Toast.AddressRemovedConfirmation.v129" = "ᱴᱷᱤᱠᱬᱟᱹᱠᱚ ᱚᱪᱚᱜ ᱮᱱᱟ"; + +/* Toast message confirming that an address has been successfully saved. */ +"Addresses.Toast.AddressSavedConfirmation.v129" = "ᱴᱷᱤᱠᱬᱟᱹᱠᱚ ᱥᱟᱧᱪᱟᱣ ᱮᱱᱟ"; + +/* Toast message indicating an error occurred while trying to save an address. */ +"Addresses.Toast.AddressSaveError.v129" = "ᱴᱷᱤᱠᱬᱟᱹ ᱥᱟᱧᱪᱟᱣ ᱵᱟᱭ ᱜᱟᱱ ᱞᱮᱱᱟ"; + +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "ᱴᱷᱤᱠᱬᱟᱹ ᱚᱪᱚᱜ ᱵᱟᱭ ᱜᱟᱱ ᱞᱮᱱᱟ"; + +/* Suggestion to try again after an error occurred while trying to save an address. */ +"Addresses.Toast.AddressSaveRetrySuggestion.v129" = "ᱫᱩᱦᱲᱟᱹ ᱠᱩᱨᱩᱢᱩᱴᱩᱭ ᱢᱮ"; + diff --git a/firefox-ios/Shared/Supporting Files/si.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/si.lproj/EditAddress.strings new file mode 100644 index 000000000000..120dc57b8f20 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/si.lproj/EditAddress.strings @@ -0,0 +1,90 @@ +/* Title for the cancel button in the remove address alert. */ +"Addresses.EditAddress.Alert.CancelButton.v129" = "අවලංගු"; + +/* Title for the remove button in the remove address alert. */ +"Addresses.EditAddress.Alert.RemoveButton.v129" = "ඉවත් කරන්න"; + +/* Title for the alert indicating the action to remove an address. */ +"Addresses.EditAddress.Alert.Title.v129" = "ලිපිනය ඉවත් කරන්න"; + +/* Title for the interface option where users can add a new address for autofill purposes. This facilitates quicker form completion by automatically filling in the user's address information. */ +"Addresses.EditAddress.AutofillAddAddressTitle.v129" = "ලිපිනය යොදන්න"; + +/* Label for the area field, allowing users to specify a particular area within a city or region. This detail can improve the specificity and accuracy of autofilled addresses. */ +"Addresses.EditAddress.AutofillAddressArea.v129" = "ප්‍රදේශය"; + +/* Label for the field where users input the city part of their address. This information is crucial for mail delivery and service provision, ensuring accurate city identification in autofill settings. */ +"Addresses.EditAddress.AutofillAddressCity.v129" = "නගරය"; + +/* Label for the field where users can specify just the country, used in contexts where full address details are not required. Simplifies autofill when only country information is necessary. */ +"Addresses.EditAddress.AutofillAddressCountryOnly.v129" = "රට"; + +/* Label for the country or region field in address forms, allowing users to specify their country or territorial region. This is fundamental for international mail and services, ensuring autofill accuracy across borders. */ +"Addresses.EditAddress.AutofillAddressCountryRegion.v129" = "රට හෝ කලාපය"; + +/* Label for the county field, crucial for addressing in regions where county lines play a key role in postal services. Enhances autofill accuracy by including county information. */ +"Addresses.EditAddress.AutofillAddressCounty.v129" = "රට"; + +/* Label for the department field, used in countries like France and Colombia where departments are a key administrative division. Ensures correct departmental information is autofilled. */ +"Addresses.EditAddress.AutofillAddressDepartment.v129" = "ක්‍රියාංශය"; + +/* Label for the Do/Si field, pertinent to addresses in South Korea. Do/Si refers to provincial level divisions, and specifying this enhances address accuracy in autofill settings. */ +"Addresses.EditAddress.AutofillAddressDoSi.v129" = "පළා/කොට්ඨා"; + +/* Label for the email address field, where users input their email. Critical for digital communication and account verification, this ensures email addresses are autofilled accurately. */ +"Addresses.EditAddress.AutofillAddressEmail.v129" = "වි-තැපෑල"; + +/* Label for the emirate field, essential for addresses in the United Arab Emirates. Including emirate details ensures the autofill feature accurately represents user addresses. */ +"Addresses.EditAddress.AutofillAddressEmirate.v129" = "එමිරේට්"; + +/* Label for the field where users specify the name of an island, if applicable. Important for addresses in archipelagic regions, aiding in precise location identification during autofill. */ +"Addresses.EditAddress.AutofillAddressIsland.v129" = "දූපත"; + +/* Label for the field where the user inputs their full name as part of an address form. Essential for personalized form submissions and ensuring information accuracy in autofilled forms. */ +"Addresses.EditAddress.AutofillAddressName.v129" = "නම"; + +/* Label for the input field designated for the organization's name related to the address. Helps in distinguishing addresses used for business or personal purposes in autofill settings. */ +"Addresses.EditAddress.AutofillAddressOrganization.v129" = "සංවිධානය"; + +/* Label for the parish field, significant in places where parishes are used for local administration and addressing. Ensures users can specify parish details for better autofill accuracy. */ +"Addresses.EditAddress.AutofillAddressParish.v129" = "වසම"; + +/* Label for the PIN (Postal Index Number) field, used in India. It's a code representing a specific area, crucial for accurate mail delivery and autofill functionality. */ +"Addresses.EditAddress.AutofillAddressPin.v129" = "අංකය"; + +/* Label for the postal code field, universally used in address forms to specify the area code for mail sorting. Essential for autofill to ensure mail and services are accurately routed. */ +"Addresses.EditAddress.AutofillAddressPostalCode.v129" = "තැපැල් කේතය"; + +/* Label for the post town field, used primarily in the UK and some other regions for mail sorting. Essential for users in applicable areas to specify for correct mail delivery through autofill. */ +"Addresses.EditAddress.AutofillAddressPostTown.v129" = "තැපැල් නගරය"; + +/* Label for the province field, required in countries where provinces are a primary administrative division. Helps in pinpointing the user's location more accurately for autofill purposes. */ +"Addresses.EditAddress.AutofillAddressProvince.v129" = "පළාත"; + +/* Label for the state field, a necessary component of addresses in many countries, especially the USA. It ensures that state-specific details are correctly filled in forms using autofill. */ +"Addresses.EditAddress.AutofillAddressState.v129" = "ප්‍රාන්තය"; + +/* Label for the button to cancel the current autofill operation or exit the form without saving changes. Provides users with an option to back out of a process without making any modifications. */ +"Addresses.EditAddress.AutofillCancelButton.v129" = "අවලංගු"; + +/* Title for the option allowing users to edit an existing saved address. This is used within the settings for autofill, enabling users to update their address details for accurate form autofilling. */ +"Addresses.EditAddress.AutofillEditAddressTitle.v129" = "ලිපිනය සංස්කරණය"; + +/* Title for the input field where users can enter their street address. This is used within the settings for autofill, allowing users to provide their street address for accurate form autofilling. */ +"Addresses.EditAddress.AutofillEditStreetAddressTitle.v129" = "වීදියේ ලිපිනය"; + +/* Label for the button to save the current address details entered or edited by the user. This action confirms the user's changes and updates their autofill settings accordingly. */ +"Addresses.EditAddress.AutofillSaveButton.v129" = "සුරකින්න"; + +/* Title for the option allowing users to view an existing saved address. This is used within the settings for autofill, enabling users to see their address details for accurate form autofilling. */ +"Addresses.EditAddress.AutofillViewAddressTitle.v129" = "ලිපිනය බලන්න"; + +/* Button label for closing the view where user can view their address info. */ +"Addresses.EditAddress.CloseNavBarButtonLabel.v129" = "වසන්න"; + +/* Button label for editing the address details shown in the form. */ +"Addresses.EditAddress.EditNavBarButtonLabel.v129" = "සංස්කරණය"; + +/* Title for button that offers the user the option to remove an address. */ +"Addresses.EditAddress.RemoveAddressButtonTitle.v129" = "ලිපිනය ඉවත් කරන්න"; + diff --git a/firefox-ios/Shared/Supporting Files/si.lproj/ResearchSurface.strings b/firefox-ios/Shared/Supporting Files/si.lproj/ResearchSurface.strings index a7fd588b8a1a..db34bb5c0e84 100644 --- a/firefox-ios/Shared/Supporting Files/si.lproj/ResearchSurface.strings +++ b/firefox-ios/Shared/Supporting Files/si.lproj/ResearchSurface.strings @@ -1,5 +1,5 @@ /* On the Research Survey popup, the text that explains what the screen is about. Placeholder is for the app name. */ -"Body.Text.v112" = "කෙටි සමීක්‍ෂණයෙන් %@ දියුණු කිරීමට උදවු කරන්න."; +"Body.Text.v112" = "කෙටි සමීක්‍ෂණයෙන් %@ දියුණු කිරීමට උදව් කරන්න."; /* On the Research Survey popup, the text for the button that, when tapped, will dismiss the popup and take the user to a survey. */ "PrimaryButton.Label.v112" = "සමීක්‍ෂණය ගන්න"; diff --git a/firefox-ios/Shared/Supporting Files/sk.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/sk.lproj/EditAddress.strings index e60e99b93c95..67b96297ad95 100644 --- a/firefox-ios/Shared/Supporting Files/sk.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/sk.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "Adresu sa nepodarilo uložiť"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Adresu sa nepodarilo odstrániť"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Skúsiť znova"; diff --git a/firefox-ios/Shared/Supporting Files/sk.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/sk.lproj/EnhancedTrackingProtection.strings index 46d42ca23957..864a78a82dd7 100644 --- a/firefox-ios/Shared/Supporting Files/sk.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/sk.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Pripojenie nie je zabezpečené"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Nenašli sa žiadne sledovacie prvky"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Sledovacie cookies tretích strán: %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Sledovacie prvky sociálnych sietí: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Zablokované sledovacie prvky: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Ochrana je vypnutá"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Ste chránení. Ak niečo zistíme, dáme vám vedieť."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "Vaše pripojenie nie je zabezpečené."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Buďte opatrní na tejto stránke"; diff --git a/firefox-ios/Shared/Supporting Files/sk.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/sk.lproj/Microsurvey.strings index 1e2bafbb5e70..7322694117aa 100644 --- a/firefox-ios/Shared/Supporting Files/sk.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/sk.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Nezvolená"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Prieskum"; + diff --git a/firefox-ios/Shared/Supporting Files/sk.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/sk.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..8f31cda145dd --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sk.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Obnoviť"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "Vyskytla sa chyba SSL a nie je možné vytvoriť zabezpečené pripojenie k serveru."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "Buďte opatrní. Niečo nie je v poriadku."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "Skúste sa pripojiť na inom zariadení. Skontrolujte modem alebo smerovač. Odpojte sa a znova sa pripojte k sieti Wi-Fi."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "Zdá sa, že sa vyskytol problém s vaším internetovým pripojením."; + diff --git a/firefox-ios/Shared/Supporting Files/sk.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/sk.lproj/Settings.strings index 4bcba1675ebd..b53845a4ce94 100644 --- a/firefox-ios/Shared/Supporting Files/sk.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/sk.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Spravovať adresy"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "Adresa pre %@"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "ULOŽENÉ ADRESY"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Uložiť adresy do %@u"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "Bezpečne si uložte svoje informácie, aby ste k nim neskôr mali rýchly prístup."; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "Zahŕňa telefónne čísla a e‑mailové adresy"; diff --git a/firefox-ios/Shared/Supporting Files/sk.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/sk.lproj/Toolbar.strings new file mode 100644 index 000000000000..866b50dbbdab --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sk.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Nová karta"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Zavrieť túto kartu"; + diff --git a/firefox-ios/Shared/Supporting Files/sl.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/sl.lproj/EditAddress.strings index 0a64a85d0767..69159f9e5a9b 100644 --- a/firefox-ios/Shared/Supporting Files/sl.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/sl.lproj/EditAddress.strings @@ -88,6 +88,9 @@ /* Label for the telephone number field, allowing users to input their contact number. This is essential for communication and service provision, ensuring contact details are autofilled correctly. */ "Addresses.EditAddress.AutofillAddressTel.v129" = "Telefon"; +/* Label for the input field for the townland, a specific type of land division used in rural areas. Enhances address detail for users in regions where townlands are a common addressing component. */ +"Addresses.EditAddress.AutofillAddressTownland.v129" = "Townland"; + /* Label for the field to input the name of a village or township. This is crucial for addresses in rural areas, ensuring the autofill feature accurately captures all necessary geographical details. */ "Addresses.EditAddress.AutofillAddressVillageTownship.v129" = "Vas ali okraj"; @@ -127,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "Naslova ni bilo mogoče shraniti"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Naslova ni bilo mogoče odstraniti"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Poskusi znova"; diff --git a/firefox-ios/Shared/Supporting Files/sl.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/sl.lproj/EnhancedTrackingProtection.strings index 73057c04b339..fa537a86a7bd 100644 --- a/firefox-ios/Shared/Supporting Files/sl.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/sl.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Povezava ni varna"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Ni najdenih sledilcev"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Piškotkov za sledenje med spletnimi mesti: %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Sledilcev družbenih omrežij: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Zavrnjenih sledilcev: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Zaščito ste izključili"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Zaščiteni ste. Če kaj opazimo, vas obvestimo."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "Vaša povezava ni varna."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Previdno na tem spletnem mestu"; diff --git a/firefox-ios/Shared/Supporting Files/sl.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/sl.lproj/Microsurvey.strings index a3da1bdaecdd..b58438bcbc65 100644 --- a/firefox-ios/Shared/Supporting Files/sl.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/sl.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Neizbrano"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Anketa"; + diff --git a/firefox-ios/Shared/Supporting Files/sl.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/sl.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..eb118bc21494 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sl.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Ponovno naloži"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "Prišlo je do napake SSL, zaradi katere ni mogoče vzpostaviti varne povezave s strežnikom."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "Previdno. Nekaj ni videti v redu."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "Poskusite se povezati z drugo napravo. Preverite modem ali usmerjevalnik. Odklopite se in se ponovno povežite z Wi-Fi."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "Videti je, da obstaja težava z vašo internetno povezavo."; + diff --git a/firefox-ios/Shared/Supporting Files/sl.lproj/Onboarding.strings b/firefox-ios/Shared/Supporting Files/sl.lproj/Onboarding.strings index 91f3c0464c33..bcad1e6ca0f5 100644 --- a/firefox-ios/Shared/Supporting Files/sl.lproj/Onboarding.strings +++ b/firefox-ios/Shared/Supporting Files/sl.lproj/Onboarding.strings @@ -19,6 +19,9 @@ /* String used to describe the option to continue to the next onboarding card in Firefox Onboarding screens. Placeholder is for the app name. */ "Onboarding.Customization.Intro.Continue.Action.v123" = "Prilagodi %@"; +/* String used to describe the description label of the customization onboarding page in our Onboarding screens. */ +"Onboarding.Customization.Intro.Description.v123" = "Izberite si temo in orodno vrstico, ki se bo ujemala z vašim edinstvenim slogom brskanja."; + /* String used to describe the option to skip the customization cards in Firefox Onboarding screens and start browsing. */ "Onboarding.Customization.Intro.Skip.Action.v123" = "Začnite brskati"; @@ -91,6 +94,9 @@ /* String used to describes the description of what Firefox is on the Sync onboarding page for current version in our Onboarding screens. Placeholder is for the app name. */ "Onboarding.Sync.Description.v120" = "Prijavljeni in sinhronizirani ste varnejši. %@ šifrira vaša gesla, zaznamke in ostale podatke."; +/* String used to describes the description of what Firefox is on the Sync onboarding page for current version in our Onboarding screens. Placeholder is for the app name. */ +"Onboarding.Sync.Description.v123" = "%@ ob vključeni sinhronizaciji šifrira vaša gesla, zaznamke in drugo."; + /* String used to describes the option to skip the Sync sign in during onboarding for the current version in Firefox Onboarding screens. */ "Onboarding.Sync.SignIn.Action.v114" = "Prijava"; diff --git a/firefox-ios/Shared/Supporting Files/sl.lproj/ScanQRCode.strings b/firefox-ios/Shared/Supporting Files/sl.lproj/ScanQRCode.strings new file mode 100644 index 000000000000..80730ce56a4b --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sl.lproj/ScanQRCode.strings @@ -0,0 +1,9 @@ +/* Allow button to open URL from scanned QR Code */ +"ScanQRCode.ConfirmOpenURL.AllowButton.v129" = "Dovoli"; + +/* Deny button to cancel opening URL from scanned QR Code */ +"ScanQRCode.ConfirmOpenURL.DenyButton.v129" = "Zavrni"; + +/* Text of the prompt to ask user permission to open a URL from a scanned QR code. Placeholder is app name. */ +"ScanQRCode.ConfirmOpenURL.Message.v129" = "Dovoli odpiranje %@?"; + diff --git a/firefox-ios/Shared/Supporting Files/sl.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/sl.lproj/Settings.strings index 85f89fbedbb1..812d48e3af7e 100644 --- a/firefox-ios/Shared/Supporting Files/sl.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/sl.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Upravljanje naslovov"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "Naslov za %@"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "SHRANJENI NASLOVI"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Shranjuj naslove v %@"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "Varno si shranite podatke za kasnejši hiter dostop."; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "Vključuje telefonske številke in e-poštne naslove"; @@ -94,6 +106,9 @@ /* Title for alternate search engines settings section in the Search page in the Settings menu. */ "Settings.Search.AlternateSearchEngines.Title.v124.v2" = "Pomožni iskalniki"; +/* Footer for the `default search engine` settings section in the Search Settings page, which explains in more details what the `Show Search Suggestions` setting includes. */ +"Settings.Search.DefaultSearchEngine.Footer.v122" = "Rezultati iskanja, zgodovine, zaznamkov in drugo"; + /* Title for the `default search engine` settings section in the Search page in the Settings menu. */ "Settings.Search.DefaultSearchEngine.Title.v121" = "Privzeti iskalnik"; @@ -103,6 +118,9 @@ /* Navigation title for search page in the Settings menu. */ "Settings.Search.PageTitle.v121" = "Iskanje"; +/* Description for `Show in Private Sessions` toggle, located in `Suggestions from Search Engines` section in the Search page in the Settings menu. */ +"Settings.Search.PrivateSession.Description.v125" = "Prikazuj predloge iskalnikov v zasebnih sejah"; + /* Label for toggle. Explains that in private browsing mode, the search suggestions which appears at the top of the search bar, can be toggled on or off. Located in the Private Session section in the Search page in the Settings menu. */ "Settings.Search.PrivateSession.Setting.v122" = "Prikazuj predloge v zasebnem brskanju"; @@ -124,6 +142,9 @@ /* In the search page of the Settings menu, the title for the link to the SUMO Page about Firefox Suggest. */ "Settings.Search.Suggest.LearnAboutSuggestions.v124" = "Več o Firefoxovih predlogih"; +/* Description for `Show in Private Sessions` toggle, located in `Address Bar - Firefox Suggest` section in the Search page in the Settings menu. */ +"Settings.Search.Suggest.PrivateSession.Description.v125" = "Prikazuj Firefoxove predloge v zasebnih sejah"; + /* In the Search page of the Settings menu, the title for the setting to enable search browsing history. */ "Settings.Search.Suggest.SearchBrowsingHistory.Title.v124" = "Iskanje po zgodovini iskanja"; @@ -148,3 +169,6 @@ /* In the Search page of the Settings menu, the description for the setting to enable Suggestions from sponsors. Placeholder is for the app name - Firefox. */ "Settings.Search.Suggest.ShowSponsoredSuggestions.Description.v124" = "Podprite %@ z občasnimi sponzoriranimi predlogi"; +/* In the Search page of the Settings menu, the title for the setting to enable Suggestions from sponsors. */ +"Settings.Search.Suggest.ShowSponsoredSuggestions.Title.v124" = "Predlogi sponzorjev"; + diff --git a/firefox-ios/Shared/Supporting Files/sl.lproj/Shopping.strings b/firefox-ios/Shared/Supporting Files/sl.lproj/Shopping.strings index d9be01264e20..36b44801b085 100644 --- a/firefox-ios/Shared/Supporting Files/sl.lproj/Shopping.strings +++ b/firefox-ios/Shared/Supporting Files/sl.lproj/Shopping.strings @@ -1,12 +1,48 @@ +/* Contextual hints are little popups that appear for the users informing them of new features. This one indicates that a user can tap on the shopping button to start using the Shopping feature. */ +"ContextualHints.Shopping.NotOptedIn.v120" = "Pred nakupom se pozanimajte, ali lahko zaupate mnenjem o tem izdelku."; + +/* Contextual hints are little popups that appear for the users informing them of new features. This one is a call to action for the popup describing the Shopping feature. It indicates that a user can go directly to the Shopping feature by tapping the text of the action. */ +"ContextualHints.Shopping.NotOptedInAction.v120" = "Preizkusite pregledovalnik mnenj"; + +/* Contextual hints are little popups that appear for the users informing them of new features. This is a call to action for the popup that appears after the user has opted in for the Shopping feature. It indicates that a user can directly open the review checker by tapping the text of the action. */ +"ContextualHints.Shopping.OptedInAction.v120" = "Odpri pregledovalnik mnenj"; + +/* Contextual hints are little popups that appear for the users informing them of new features. This one appears after the user has opted in and informs him if he wants use the review checker by tapping the Shopping button. */ +"ContextualHints.Shopping.OptedInBody.v120" = "Ali so ta mnenja zanesljiva? Preverite zdaj za ogled prilagojene ocene."; + +/* Footer label from the Fakespot Ad card displayed for the related product we advertise. This is displayed below the ad card, suggested as an alternative to the product reviewed. The first parameter will be replaced by the Fakespot app name */ +"Shopping.AdCard.Footer.v121" = "Naročnik oglasa je %@"; + +/* Title label for the Fakespot Ad card. This is displayed above a product image, suggested as an alternative to the product reviewed. */ +"Shopping.AdCard.Title.v121" = "Več za razmislek"; + +/* Description adjusted of the rating card displayed in the shopping review quality bottom sheet. */ +"Shopping.AdjustedRating.Description.v121" = "Temelji na zanesljivih mnenjih"; + +/* Accessibility label, associated to adjusted rating stars. %@ is a decimal value from 0 to 5 that will only use a tenth (example: 3.5). */ +"Shopping.AdjustedRating.StarsAccessibilityLabel.v120" = "%@ od 5 zvezdic"; + +/* Title of the adjusted rating card displayed in the shopping review quality bottom sheet. */ +"Shopping.AdjustedRating.Title.v120" = "Prilagojena ocena"; + /* Button text of the confirmation displayed in the shopping review quality bottom sheet. */ "Shopping.ConfirmationCard.Button.Text.v120" = "Razumem"; +/* Title of the confirmation displayed in the shopping review quality bottom sheet. */ +"Shopping.ConfirmationCard.Title.v120" = "Analiza je posodobljena"; + +/* Section title of the review highlights displayed in the shopping review quality bottom sheet. */ +"Shopping.HighlightsCard.Competitiveness.Title.v120" = "Konkurenčnost"; + /* Title of the button that shows less reviews in the review highlights displayed in the shopping review quality bottom sheet. */ "Shopping.HighlightsCard.LessButton.Title.v120" = "Prikaži manj"; /* Title of the button that shows more reviews in the review highlights displayed in the shopping review quality bottom sheet. */ "Shopping.HighlightsCard.MoreButton.Title.v120" = "Prikaži več"; +/* Section title of the review highlights displayed in the shopping review quality bottom sheet, specifically focusing on the quality, design, and condition of the product's packaging. This may include details about the box, protective materials, presentation, and overall packaging experience. */ +"Shopping.HighlightsCard.Packaging.Title.v120" = "Embalaža"; + /* Section title of the review highlights displayed in the shopping review quality bottom sheet. */ "Shopping.HighlightsCard.Price.Title.v120" = "Cena"; @@ -19,18 +55,198 @@ /* Title of the review highlights displayed in the shopping review quality bottom sheet. */ "Shopping.HighlightsCard.Title.v120" = "Poudarki iz nedavnih mnenj"; +/* Title for info card when Fakespot cannot analyze reviews for a certain product type */ +"Shopping.InfoCard.FakespotDoesNotAnalyzeReviews.Description.v120" = "Za nekatere vrste izdelkov žal ne moremo preveriti kakovosti mnenj. Na primer za darilne kartice ter pretočne videe, glasbo in igre."; + +/* Title for info card when Fakespot cannot analyze reviews for a certain product type */ +"Shopping.InfoCard.FakespotDoesNotAnalyzeReviews.Title.v120" = "Teh mnenj ne moremo preveriti"; + +/* Description text for an information card used in the review checker section. This message is displayed when the reviews for a product are not yet available but are expected to be provided within the next 24 hours. It serves to inform users of the short wait for reviews and encourages them to return soon for the updated information. */ +"Shopping.InfoCard.InfoComingSoon.Description.v121" = "Podatki o mnenjih za ta izdelek bi morali biti pripravljeni v 24 urah. Preverite znova kasneje."; + +/* Title for an information card that is displayed in the review checker section when certain details about a product or feature are not currently available but are expected to be provided soon. The message should imply that the user can look forward to receiving more information shortly. */ +"Shopping.InfoCard.InfoComingSoon.Title.v121" = "Kmalu bo na voljo še več vsebine"; + +/* Primary action title for info card when the product needs analysis */ +"Shopping.InfoCard.NeedsAnalysis.PrimaryAction.v120" = "Preveri zdaj"; + +/* Title for info card when the product needs analysis */ +"Shopping.InfoCard.NeedsAnalysis.Title.v120" = "Novi podatki za pregled"; + +/* Description for info card when no information is available at the moment */ +"Shopping.InfoCard.NoInfoAvailableRightNow.Description.v120" = "Poskušamo odpraviti težavo. Preverite znova pozneje."; + +/* Title for info card when no information is available at the moment */ +"Shopping.InfoCard.NoInfoAvailableRightNow.Title.v120" = "Trenutno ni na voljo nobenih podatkov"; + +/* Description for info card when there are not enough reviews for a product */ +"Shopping.InfoCard.NotEnoughReviews.Description.v120" = "Ko bo za ta izdelek na voljo več mnenj, bomo lahko preverili njihovo kakovost."; + +/* Title for info card when there are not enough reviews for a product */ +"Shopping.InfoCard.NotEnoughReviews.Title.v120" = "Ni še dovolj mnenj"; + +/* Description for the information card displayed by the review checker feature when the product the user is looking at is out of stock. This description is used for info card where the user can report if it's back in stock. */ +"Shopping.InfoCard.ProductNotInStock.Description.v121" = "Če opazite, da je izdelek znova na zalogi, nam to sporočite in preverili bomo mnenja."; + +/* Primary action label for the information card displayed by the review checker feature when the product the user is looking at is out of stock. This primary action label is used for info card button where the user can report if it's back in stock. */ +"Shopping.InfoCard.ProductNotInStock.PrimaryAction.v121" = "Sporoči, da je izdelek znova na zalogi"; + +/* Title for the information card displayed by the review checker feature when the product the user is looking at is out of stock. This title is used for info card where the user can report if it's back in stock. */ +"Shopping.InfoCard.ProductNotInStock.Title.v121" = "Izdelek ni na voljo"; + +/* Description for info card when the product is in analysis mode */ +"Shopping.InfoCard.ProgressAnalysis.Description.v120" = "To lahko traja približno 60 sekund."; + +/* Title for info card when the product is in analysis mode */ +"Shopping.InfoCard.ProgressAnalysis.Title.v120" = "Preverjanje kakovosti mnenj"; + +/* Title for info card when the product is in analysis mode. The placeholder represents the percentage of the analysis progress, ranging between 1 and 100. */ +"Shopping.InfoCard.ProgressAnalysis.Title.v123" = "Preverjanje kakovosti mnenj (%@)"; + +/* This description appears beneath the confirmation title on the information card to inform the user that their report regarding the product stock status has been received and is being processed. It serves to set the expectation that the review information will be updated within 24 hours and invites the user to revisit the product page for updates. */ +"Shopping.InfoCard.ReportSubmittedByCurrentUser.Description.v121" = "Podatki o mnenjih za ta izdelek bi morali biti pripravljeni v 24 urah. Preverite znova kasneje."; + +/* This title is displayed on the information card as a confirmation message after a user reports that a previously out-of-stock product is now available. It's meant to acknowledge the user's contribution and encourage community engagement by letting them know their report has been successfully submitted. */ +"Shopping.InfoCard.ReportSubmittedByCurrentUser.Title.v121" = "Hvala za sporočilo!"; + +/* Text for the analyzer button displayed when an analysis can be updated for a product. */ +"Shopping.NoAnalysisCard.AnalyzerButton.Title.v120" = "Preveri kakovost mnenj"; + +/* Text for the body label, to check the reliability of a product. */ +"Shopping.NoAnalysisCard.BodyLabel.Title.v120" = "Če želite preveriti, ali so ocene tega izdelka zanesljive, preverite kakovost mnenj. Traja le približno 60 sekund."; + +/* Title for card displayed when a shopping product has not been analysed yet. */ +"Shopping.NoAnalysisCard.HeadlineLabel.Title.v120" = "O teh mnenjih še ni podatkov"; + +/* Text for the disclaimer that appears underneath the rating image of the Shopping Experience Opt In onboarding Card (Fakespot). The parameter will be replaced by the Fakespot app name. After the colon, what appears are two links, each on their own line. The first link is to a Privacy policy. The second link is to Terms of use. */ +"Shopping.OptInCard.Disclaimer.Text.v120" = "Z izbiro “Da, poskusi” se strinjate z naslednjimi dokumenti %@a:"; + +/* Text for the disclaimer that appears underneath the rating image of the Shopping Experience Opt In onboarding Card (Fakespot). After the colon, there will be two links, each on their own line. The first link is to a Privacy policy. The second link is to Terms of use. */ +"Shopping.OptInCard.Disclaimer.Text.v123" = "Z izbiro “Da, poskusi” se strinjate z naslednjimi dokumenti:"; + +/* Label for the first paragraph of the Shopping Experience Opt In onboarding Card (Fakespot). The first parameter will be the website the user is coming from when viewing this screen (default Amazon). The second parameter will be replaced by the app name. This string is almost identical with 'Shopping.OptInCard.FirstParagraph.Description', but without Best Buy and Walmart websites, which are not available in many locales. */ +"Shopping.OptInCard.FirstParagraph.AmazonOnly.Description.v122" = "Pred nakupom preverite, kako zanesljiva so mnenja o izdelkih v trgovini %1$@. Pregledovalnik mnenj, preizkusna zmogljivost %2$@a, je vgrajen neposredno v brskalnik."; + +/* Label for the first paragraph of the Shopping Experience Opt In onboarding Card (Fakespot). The first parameter will be the website the user is coming from when viewing this screen (default Amazon). The second parameter will be replaced by the app name. The third and fourth parameters will be the other two websites that are currently supported (Amazon, Best Buy or Walmart) besides the one used for the first parameter. */ +"Shopping.OptInCard.FirstParagraph.Description.v120" = "Pred nakupom preverite, kako zanesljiva so mnenja o izdelkih v trgovini %1$@. Pregledovalnik mnenj, preizkusna zmogljivost %2$@a, je vgrajen neposredno v brskalnik. Deluje tudi v trgovinah %3$@ in %4$@."; + +/* Label for the header of the Shopping Experience Opt In onboarding Card (Fakespot) */ +"Shopping.OptInCard.HeaderLabel.Title.v120" = "Preizkusite naš zaupanja vreden vodnik po ocenah izdelkov"; + /* Label for the Learn more button in the Shopping Experience Opt In onboarding Card (Fakespot) */ "Shopping.OptInCard.LearnMoreButtonTitle.Title.v120" = "Več o tem"; +/* Text for the main button of the Shopping Experience Opt In onboarding Card (Fakespot) */ +"Shopping.OptInCard.MainButton.Title.v120" = "Da, poskusi"; + +/* Show Firefox Browser Privacy Policy page from the Privacy section in the Shopping Experience Opt In onboarding Card (Fakespot). See https://www.mozilla.org/privacy/firefox/ */ +"Shopping.OptInCard.PrivacyPolicy.Button.Title.v120" = "Pravilnik o zasebnosti"; + +/* Show Firefox Browser Privacy Policy page from the Privacy section in the Shopping Experience Opt In onboarding Card (Fakespot). The parameter will be replace by the Firefox app name. */ +"Shopping.OptInCard.PrivacyPolicy.Button.Title.v123" = "Obvestilo o zasebnosti %@a"; + /* Text for the secondary button of the Shopping Experience Opt In onboarding Card (Fakespot) */ "Shopping.OptInCard.SecondaryButton.Title.v120" = "Ne zdaj"; +/* Label for the second paragraph of the Shopping Experience Opt In onboarding Card (Fakespot). The first parameter will be replaced by the Fakespot app name. The second parameter will be replaced the company name of Mozilla. */ +"Shopping.OptInCard.SecondParagraph.Description.v120" = "%2$@ %1$@ vam omogoča, da se izognete pristranskim in nepristnim mnenjem. Naš model umetne inteligence se nenehno izboljšuje, da vas ščiti med nakupovanjem."; + /* Show Firefox Browser Terms of Use page from the Privacy section in the Shopping Experience Opt In onboarding Card (Fakespot). See https://www.mozilla.org/privacy/firefox/ */ "Shopping.OptInCard.TermsOfUse.Button.Title.v120" = "Pogoji uporabe"; +/* Show Fakespot Terms of Use page in the Shopping Experience Opt In onboarding Card (Fakespot). The parameter will be replace by the Fakespot name. */ +"Shopping.OptInCard.TermsOfUse.Button.Title.v123" = "Pogoji uporabe %@a"; + +/* Accessibility label for the Grade labels used in 'How we determine review quality' card and 'How reliable are these reviews' card displayed in the shopping review quality bottom sheet. The placeholder will be replaced by a grade letter (e.g. A). The grading system contains letters from A-F. */ +"Shopping.ReliabilityScore.Grade.A11y.Label.v120" = "Ocena %@"; + +/* Title of the reliability card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQuality.ReliabilityCardTitle.v120" = "Kako zanesljiva so ta mnenja?"; + +/* Description of the reliability ratings for rating 'A' and 'B' displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQuality.ReliabilityRating.AB.Description.v120" = "Zanesljiva mnenja"; + +/* Description of the reliability rating 'C' displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQuality.ReliabilityRating.C.Description.v120" = "Mešanica zanesljivih in nezanesljivih mnenj"; + +/* Description of the reliability ratings for rating 'D' and 'F' displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQuality.ReliabilityRating.DF.Description.v120" = "Nezanesljiva mnenja"; + +/* Adjusted rating label from How we determine review quality card displayed in the shopping review quality bottom sheet. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"Shopping.ReviewQualityCard.AdjustedRating.Label.v120" = "*Prilagojena ocena* temelji samo na mnenjih, za katere menimo, da so zanesljiva."; + +/* Accessibility label for the up chevron, from 'How we determine review quality' card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.Collapse.AccessibilityLabel.v120" = "Strani kartico Kako določamo kakovost mnenj"; + +/* Accessibility label for the down chevron, from 'How we determine review quality' card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.Expand.AccessibilityLabel.v120" = "Razširi kartico Kako določamo kakovost mnenj"; + +/* Label of the headline from How we determine review quality card displayed in the shopping review quality bottom sheet. The first parameter will be replaced by the Fakespot app name and the second parameter by the company name of Mozilla. */ +"Shopping.ReviewQualityCard.Headline.Label.v120" = "Za preverjanje zanesljivosti mnenj o izdelkih uporabljamo tehnologijo umetne inteligence %2$@ %1$@. To vam bo pomagalo oceniti zgolj kakovost mnenja, ne pa tudi izdelka."; + +/* Highlights label from How we determine review quality card displayed in the shopping review quality bottom sheet. The parameter substitutes the partner website the user is coming from. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"Shopping.ReviewQualityCard.Highlights.Label.v120" = "*Poudarki* so vzeti iz mnenj v trgovini %1@ v zadnjih 80. dneh, za katera menimo, da so zanesljiva."; + +/* Highlights label from How we determine review quality card displayed in the shopping review quality bottom sheet. The parameter substitutes the partner website the user is coming from. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"Shopping.ReviewQualityCard.Highlights.Label.v126" = "*Poudarki* so vzeti iz mnenj v trgovini %@ v zadnjih 80. dneh, za katera menimo, da so zanesljiva."; + +/* Title of the 'How we determine review quality' card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.Label.Title.v120" = "Kako določamo kakovost mnenj"; + +/* The title of the learn more button from How we determine review quality card displayed in the shopping review quality bottom sheet. The placeholder will be replaced with the Fakespot app name. */ +"Shopping.ReviewQualityCard.LearnMoreButton.Title.v120" = "Več o tem, kako %@ določa kakovost pregledov"; + +/* Mixed reviews label from How we determine review quality card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.MixedReviews.Label.v120" = "Menimo, da obstaja mešanica zanesljivih in nezanesljivih mnenj"; + +/* Reliable reviews label from How we determine review quality card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.ReliableReviews.Label.v120" = "Zanesljiva mnenja. Verjamemo, da so jih napisale resnične stranke, ki so pustile poštene in nepristranske ocene."; + +/* Label of the sub headline from How we determine review quality card displayed in the shopping review quality bottom sheet. The *text inside asterisks* denotes part of the string to bold, please leave the text inside the '*' so that it is bolded correctly. */ +"Shopping.ReviewQualityCard.SubHeadline.Label.v120" = "Vsako mnenje o izdelku ocenimo s *črkovno oceno* od A do F."; + +/* Unreliable reviews label from How we determine review quality card displayed in the shopping review quality bottom sheet. */ +"Shopping.ReviewQualityCard.UnreliableReviews.Label.v120" = "Nezanesljiva mnenja. Menimo, da so verjetno lažna ali pa so jih napisali pristranski ocenjevalci."; + +/* Accessibility label for the up chevron icon used to collapse or minimize the Settings Card within the shopping product review bottom sheet. */ +"Shopping.SettingsCard.Collapse.AccessibilityLabel.v120" = "Strni kartico z nastavitvami"; + +/* Accessibility label for the down chevron icon used to expand or show the details of the Settings Card within the shopping product review bottom sheet. */ +"Shopping.SettingsCard.Expand.AccessibilityLabel.v120" = "Razširi kartico z nastavitvami"; + +/* Accessibility hint for the recommended products label and switch, grouped together. When the group is selected in VoiceOver mode, the hint is read to help the user understand what action can be performed. */ +"Shopping.SettingsCard.Expand.GroupedRecommendedProductsAndSwitch.AccessibilityHint.v123" = "Dvotapnite za preklop nastavitve."; + +/* Accessibility label for the recommended products label and switch, grouped together. The first placeholder is for the recommended products label, and the second placeholder is for the state of the switch: On/Off. */ +"Shopping.SettingsCard.Expand.GroupedRecommendedProductsAndSwitch.AccessibilityLabel.v123" = "%1$@, preklopni gumb, %2$@."; + +/* Action title of the footer underneath the Settings Card displayed in the shopping review quality bottom sheet. The first parameter will be replaced by the Fakespot app name and the second parameter by the company name of Mozilla. */ +"Shopping.SettingsCard.Footer.Action.v120" = "Pregledovalnik mnenj uporablja tehnologijo %2$@ %1$@"; + /* Title of the settings card displayed in the shopping review quality bottom sheet. */ "Shopping.SettingsCard.Label.Title.v120" = "Nastavitve"; +/* Label of the switch from settings card displayed in the shopping review quality bottom sheet. The placeholder will be replaced with the app name. */ +"Shopping.SettingsCard.RecommendedProducts.Label.v120" = "Prikaži izdelke, ki jih priporoča %@"; + +/* Toggled Off accessibility switch value from Settings Card within the shopping product review bottom sheet. */ +"Shopping.SettingsCard.SwitchValueOff.AccessibilityLabel.v123" = "Izključeno"; + +/* Toggled On accessibility value, from Settings Card within the shopping product review bottom sheet. */ +"Shopping.SettingsCard.SwitchValueOn.AccessibilityLabel.v123" = "Vključeno"; + +/* Label of the button from settings card displayed in the shopping review quality bottom sheet. */ +"Shopping.SettingsCard.TurnOffButton.Title.v120" = "Izklopi pregledovalnik mnenj"; + +/* Beta label for the header of the Shopping Experience (Fakespot) sheet */ +"Shopping.Sheet.Beta.Title.v120" = "BETA"; + +/* Accessibility label for close button that dismisses the Shopping Experience (Fakespot) sheet. */ +"Shopping.Sheet.Close.AccessibilityLabel.v121" = "Zapri pregledovalnik mnenj"; + +/* Label for the header of the Shopping Experience (Fakespot) sheet */ +"Shopping.Sheet.Title.v120" = "Pregledovalnik mnenj"; + /* Text for body of error card displayed to the user when the device is disconnected from the network. */ "Shopping.WarningCard.CheckNoConnection.Description.v120" = "Preverite povezavo z omrežjem in poskusite znova naložiti stran."; diff --git a/firefox-ios/Shared/Supporting Files/sl.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/sl.lproj/Toolbar.strings new file mode 100644 index 000000000000..b1bc596d2c98 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sl.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Nov zavihek"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Zapri zavihek"; + diff --git a/firefox-ios/Shared/Supporting Files/sq.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/sq.lproj/EditAddress.strings index e4b8864f959d..b6b8e4b6a007 100644 --- a/firefox-ios/Shared/Supporting Files/sq.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/sq.lproj/EditAddress.strings @@ -124,6 +124,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "Adresa S’u Ruajt Dot"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Adresa S’u Hoq Dot"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Riprovoni"; diff --git a/firefox-ios/Shared/Supporting Files/sq.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/sq.lproj/EnhancedTrackingProtection.strings index 3599a9b5bafe..431ac9e922ba 100644 --- a/firefox-ios/Shared/Supporting Files/sq.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/sq.lproj/EnhancedTrackingProtection.strings @@ -22,14 +22,19 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Lidhje jo e sigurt"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "S’u gjetën gjurmues"; + +/* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "“Cookies” gjurmimi nga sajte në sajte: %@"; + /* Text to let users know how many fingerprinters were blocked on the current website. The placeholder will show the number of fingerprinters detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.Fingerprinter.v129" = "Krijues shenjash gishtash: %@"; /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Gjurmues prej mediash shoqërore: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Gjurmues të bllokuar: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -41,8 +46,7 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Keni mbyllur mbrojtjet"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Jeni i mbrojtur. Nëse vërejmë gjë, do t’ua bëjmë të ditur."; /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ diff --git a/firefox-ios/Shared/Supporting Files/sq.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/sq.lproj/Microsurvey.strings index d04624e1274e..c9022ced3b38 100644 --- a/firefox-ios/Shared/Supporting Files/sq.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/sq.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "U shpërzgjodh"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Pyetësor"; + diff --git a/firefox-ios/Shared/Supporting Files/sq.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/sq.lproj/Toolbar.strings new file mode 100644 index 000000000000..8f83610cf20e --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sq.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Skedë e Re"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Mbylle Këtë Skedë"; + diff --git a/firefox-ios/Shared/Supporting Files/su.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/su.lproj/EnhancedTrackingProtection.strings index f9519eef6a14..a3a18a8ddc7c 100644 --- a/firefox-ios/Shared/Supporting Files/su.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/su.lproj/EnhancedTrackingProtection.strings @@ -31,8 +31,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Pelacak média sosial: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Palacak dipeungpeuk: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,8 +43,7 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Anjeun mareuman panyalindungan"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Anjeun ditangtayungan. Upama urang manggih hiji hal, kami bakal ngiberan anjeun."; /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ diff --git a/firefox-ios/Shared/Supporting Files/sv.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/sv.lproj/EditAddress.strings index 4cdce964ae2d..fdebeee3247b 100644 --- a/firefox-ios/Shared/Supporting Files/sv.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/sv.lproj/EditAddress.strings @@ -14,7 +14,7 @@ "Addresses.EditAddress.AutofillAddAddressTitle.v129" = "Lägg till adress"; /* Label for the area field, allowing users to specify a particular area within a city or region. This detail can improve the specificity and accuracy of autofilled addresses. */ -"Addresses.EditAddress.AutofillAddressArea.v129" = "Yta"; +"Addresses.EditAddress.AutofillAddressArea.v129" = "Område"; /* Label for the field where users input the city part of their address. This information is crucial for mail delivery and service provision, ensuring accurate city identification in autofill settings. */ "Addresses.EditAddress.AutofillAddressCity.v129" = "Stad"; @@ -80,7 +80,7 @@ "Addresses.EditAddress.AutofillAddressProvince.v129" = "Län"; /* Label for the state field, a necessary component of addresses in many countries, especially the USA. It ensures that state-specific details are correctly filled in forms using autofill. */ -"Addresses.EditAddress.AutofillAddressState.v129" = "Status"; +"Addresses.EditAddress.AutofillAddressState.v129" = "Stat"; /* Label for the suburb field, enabling users to add suburb details to their address. This is important for accurate delivery and services in suburban areas, enhancing autofill functionality. */ "Addresses.EditAddress.AutofillAddressSuburb.v129" = "Förort"; @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "Adressen kunde inte sparas"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Adressen kunde inte tas bort"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Försök igen"; diff --git a/firefox-ios/Shared/Supporting Files/sv.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/sv.lproj/EnhancedTrackingProtection.strings index e25ae8521144..928b93e6561b 100644 --- a/firefox-ios/Shared/Supporting Files/sv.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/sv.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Anslutningen är inte säker"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Inga spårare hittades"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Globala spårningskakor: %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Sociala media-spårare: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Blockerade spårare: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Du stängde av skydden"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Du är skyddad. Om vi upptäcker något meddelar vi dig."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "Din anslutning är inte säker."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Var försiktig med den här sidan"; @@ -64,7 +68,7 @@ "Menu.EnhancedTrackingProtection.SwitchOff.Text.v128" = "Skydd är AV. Vi föreslår att du slår på det igen."; /* A switch to disable enhanced tracking protection inside the menu. */ -"Menu.EnhancedTrackingProtection.SwitchOff.Text.v129" = "Skydd är AV. Vi föreslår att du slår på dem igen."; +"Menu.EnhancedTrackingProtection.SwitchOff.Text.v129" = "Skydd är AV. Vi föreslår att du slår på det igen."; /* A switch to disable enhanced tracking protection inside the menu. */ "Menu.EnhancedTrackingProtection.SwitchOn.Text.v128" = "Om något ser trasigt ut på den här webbplatsen, försök att stänga av det."; diff --git a/firefox-ios/Shared/Supporting Files/sv.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/sv.lproj/Microsurvey.strings index 8fac7badc9ba..d574f8a2e4a5 100644 --- a/firefox-ios/Shared/Supporting Files/sv.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/sv.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Ej vald"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Undersökning"; + diff --git a/firefox-ios/Shared/Supporting Files/sv.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/sv.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..44a186d5592e --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sv.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Ladda om"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "Ett SSL-fel har uppstått och en säker anslutning till servern kan inte göras."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "Var försiktig. Något ser inte rätt ut."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "Prova att ansluta på en annan enhet. Kontrollera ditt modem eller router. Koppla ner och återanslut till Wi-Fi."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "Det verkar finnas ett problem med din internetanslutning."; + diff --git a/firefox-ios/Shared/Supporting Files/sv.lproj/ScanQRCode.strings b/firefox-ios/Shared/Supporting Files/sv.lproj/ScanQRCode.strings index 9913555a4590..35354c207328 100644 --- a/firefox-ios/Shared/Supporting Files/sv.lproj/ScanQRCode.strings +++ b/firefox-ios/Shared/Supporting Files/sv.lproj/ScanQRCode.strings @@ -5,5 +5,5 @@ "ScanQRCode.ConfirmOpenURL.DenyButton.v129" = "Neka"; /* Text of the prompt to ask user permission to open a URL from a scanned QR code. Placeholder is app name. */ -"ScanQRCode.ConfirmOpenURL.Message.v129" = "Tillåt %@ att öppnas?"; +"ScanQRCode.ConfirmOpenURL.Message.v129" = "Tillåta %@ att öppna?"; diff --git a/firefox-ios/Shared/Supporting Files/sv.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/sv.lproj/Settings.strings index cfd687e9008b..cc1ead1f3be6 100644 --- a/firefox-ios/Shared/Supporting Files/sv.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/sv.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Hantera adresser"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "Adress för %@"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "SPARADE ADRESSER"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Spara adresser till %@"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "Spara din information säkert för att få snabb åtkomst till den senare."; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "Inkluderar telefonnummer och e-postadresser"; diff --git a/firefox-ios/Shared/Supporting Files/sv.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/sv.lproj/Toolbar.strings new file mode 100644 index 000000000000..165a455f4189 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/sv.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Ny flik"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Stäng denna flik"; + diff --git a/firefox-ios/Shared/Supporting Files/th.lproj/ActivityStream.strings b/firefox-ios/Shared/Supporting Files/th.lproj/ActivityStream.strings new file mode 100644 index 000000000000..f9e443e496ce --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/th.lproj/ActivityStream.strings @@ -0,0 +1,6 @@ +/* String used in the section title of the Bookmarks section on Home Screen. */ +"ActivityStream.Bookmarks.Title.v128" = "ที่คั่นหน้า"; + +/* Show all button text for Bookmarks items on the home page, which opens the Bookmarks panel when tapped. */ +"Bookmarks.Actions.More.v128" = "แสดงทั้งหมด"; + diff --git a/firefox-ios/Shared/Supporting Files/th.lproj/AddressToolbar.strings b/firefox-ios/Shared/Supporting Files/th.lproj/AddressToolbar.strings new file mode 100644 index 000000000000..7fa41a851787 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/th.lproj/AddressToolbar.strings @@ -0,0 +1,12 @@ +/* Accessibility label for the address field in the address toolbar. */ +"AddressToolbar.Location.A11y.Label.v128" = "ค้นหาหรือป้อนที่อยู่"; + +/* Placeholder for the address field in the address toolbar. */ +"AddressToolbar.Location.Placeholder.v128" = "ค้นหาหรือป้อนที่อยู่"; + +/* Accessibility label for the lock icon button in the address field of the address toolbar, responsible with Privacy & Security Settings. */ +"AddressToolbar.PrivacyAndSecuriySettings.A11y.Label.v128" = "การตั้งค่าความเป็นส่วนตัวและความปลอดภัย"; + +/* Accessibility label for the search engine icon in the address field of the address toolbar. The placeholder is getting replaced with the name of the search engine (e.g. Google). */ +"AddressToolbar.SearchEngine.A11y.Label.v128" = "เครื่องมือค้นหา: %@"; + diff --git a/firefox-ios/Shared/Supporting Files/th.lproj/CustomizeFirefoxHome.strings b/firefox-ios/Shared/Supporting Files/th.lproj/CustomizeFirefoxHome.strings index f82475fe3fd8..bfc8b35bb358 100644 --- a/firefox-ios/Shared/Supporting Files/th.lproj/CustomizeFirefoxHome.strings +++ b/firefox-ios/Shared/Supporting Files/th.lproj/CustomizeFirefoxHome.strings @@ -1,3 +1,6 @@ +/* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle Bookmarks section on the Firefox homepage on or off */ +"Settings.Home.Option.Bookmarks.v128" = "ที่คั่นหน้า"; + /* In the settings menu, in the Firefox homepage customization section, this is the subtitle for the option that allows users to turn the Pocket Recommendations section on the Firefox homepage on or off. The placeholder is the pocket app name. */ "Settings.Home.Option.ThoughtProvokingStories.subtitle.v116" = "บทความขับเคลื่อนโดย %@"; diff --git a/firefox-ios/Shared/Supporting Files/th.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/th.lproj/EditAddress.strings new file mode 100644 index 000000000000..1b7234fba800 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/th.lproj/EditAddress.strings @@ -0,0 +1,141 @@ +/* Title for the cancel button in the remove address alert. */ +"Addresses.EditAddress.Alert.CancelButton.v129" = "ยกเลิก"; + +/* Message explaining the consequences of removing an address from all synced devices. */ +"Addresses.EditAddress.Alert.Message.v129" = "ที่อยู่นี้จะถูกเอาออกจากอุปกรณ์ที่ซิงค์ของคุณทั้งหมด"; + +/* Title for the remove button in the remove address alert. */ +"Addresses.EditAddress.Alert.RemoveButton.v129" = "เอาออก"; + +/* Title for the alert indicating the action to remove an address. */ +"Addresses.EditAddress.Alert.Title.v129" = "เอาที่อยู่ออก"; + +/* Title for the interface option where users can add a new address for autofill purposes. This facilitates quicker form completion by automatically filling in the user's address information. */ +"Addresses.EditAddress.AutofillAddAddressTitle.v129" = "เพิ่มที่อยู่"; + +/* Label for the area field, allowing users to specify a particular area within a city or region. This detail can improve the specificity and accuracy of autofilled addresses. */ +"Addresses.EditAddress.AutofillAddressArea.v129" = "เขต"; + +/* Label for the field where users input the city part of their address. This information is crucial for mail delivery and service provision, ensuring accurate city identification in autofill settings. */ +"Addresses.EditAddress.AutofillAddressCity.v129" = "เมือง"; + +/* Label for the field where users can specify just the country, used in contexts where full address details are not required. Simplifies autofill when only country information is necessary. */ +"Addresses.EditAddress.AutofillAddressCountryOnly.v129" = "ประเทศ"; + +/* Label for the country or region field in address forms, allowing users to specify their country or territorial region. This is fundamental for international mail and services, ensuring autofill accuracy across borders. */ +"Addresses.EditAddress.AutofillAddressCountryRegion.v129" = "ประเทศหรือภูมิภาค"; + +/* Label for the county field, crucial for addressing in regions where county lines play a key role in postal services. Enhances autofill accuracy by including county information. */ +"Addresses.EditAddress.AutofillAddressCounty.v129" = "เคาน์ตี"; + +/* Label for the department field, used in countries like France and Colombia where departments are a key administrative division. Ensures correct departmental information is autofilled. */ +"Addresses.EditAddress.AutofillAddressDepartment.v129" = "จังหวัด"; + +/* Label for the district field in the address form, allowing users to specify their district for more precise location identification. This aids in refining address details for accurate autofill. */ +"Addresses.EditAddress.AutofillAddressDistrict.v129" = "เขต"; + +/* Label for the Do/Si field, pertinent to addresses in South Korea. Do/Si refers to provincial level divisions, and specifying this enhances address accuracy in autofill settings. */ +"Addresses.EditAddress.AutofillAddressDoSi.v129" = "จังหวัด"; + +/* Label for the Eircode field, specific to Ireland. It's a unique postal code system that helps in precise location identification, enhancing the effectiveness of autofill. */ +"Addresses.EditAddress.AutofillAddressEircode.v129" = "เอียร์โค้ด"; + +/* Label for the email address field, where users input their email. Critical for digital communication and account verification, this ensures email addresses are autofilled accurately. */ +"Addresses.EditAddress.AutofillAddressEmail.v129" = "อีเมล"; + +/* Label for the emirate field, essential for addresses in the United Arab Emirates. Including emirate details ensures the autofill feature accurately represents user addresses. */ +"Addresses.EditAddress.AutofillAddressEmirate.v129" = "เอมิเรต"; + +/* Label for the field where users specify the name of an island, if applicable. Important for addresses in archipelagic regions, aiding in precise location identification during autofill. */ +"Addresses.EditAddress.AutofillAddressIsland.v129" = "เกาะ"; + +/* Label for the field where the user inputs their full name as part of an address form. Essential for personalized form submissions and ensuring information accuracy in autofilled forms. */ +"Addresses.EditAddress.AutofillAddressName.v129" = "ชื่อ"; + +/* Label for the field where users can input the name of their neighborhood. This detail adds precision to addresses, especially in densely populated areas, improving the accuracy of autofill. */ +"Addresses.EditAddress.AutofillAddressNeighborhood.v129" = "ชุมชน"; + +/* Label for the oblast field, relevant for addresses in countries like Russia and Ukraine. Oblasts are a significant administrative division, and their specification aids in autofill accuracy. */ +"Addresses.EditAddress.AutofillAddressOblast.v129" = "แคว้น"; + +/* Label for the input field designated for the organization's name related to the address. Helps in distinguishing addresses used for business or personal purposes in autofill settings. */ +"Addresses.EditAddress.AutofillAddressOrganization.v129" = "องค์กร"; + +/* Label for the parish field, significant in places where parishes are used for local administration and addressing. Ensures users can specify parish details for better autofill accuracy. */ +"Addresses.EditAddress.AutofillAddressParish.v129" = "ตำบล"; + +/* Label for the PIN (Postal Index Number) field, used in India. It's a code representing a specific area, crucial for accurate mail delivery and autofill functionality. */ +"Addresses.EditAddress.AutofillAddressPin.v129" = "พิน"; + +/* Label for the postal code field, universally used in address forms to specify the area code for mail sorting. Essential for autofill to ensure mail and services are accurately routed. */ +"Addresses.EditAddress.AutofillAddressPostalCode.v129" = "รหัสไปรษณีย์"; + +/* Label for the post town field, used primarily in the UK and some other regions for mail sorting. Essential for users in applicable areas to specify for correct mail delivery through autofill. */ +"Addresses.EditAddress.AutofillAddressPostTown.v129" = "เมือง"; + +/* Label for the prefecture field, essential for addresses in countries like Japan where prefectures are a major administrative division. Aids in precise location specification for autofill. */ +"Addresses.EditAddress.AutofillAddressPrefecture.v129" = "อำเภอ"; + +/* Label for the province field, required in countries where provinces are a primary administrative division. Helps in pinpointing the user's location more accurately for autofill purposes. */ +"Addresses.EditAddress.AutofillAddressProvince.v129" = "จังหวัด"; + +/* Label for the state field, a necessary component of addresses in many countries, especially the USA. It ensures that state-specific details are correctly filled in forms using autofill. */ +"Addresses.EditAddress.AutofillAddressState.v129" = "สถานะ"; + +/* Label for the suburb field, enabling users to add suburb details to their address. This is important for accurate delivery and services in suburban areas, enhancing autofill functionality. */ +"Addresses.EditAddress.AutofillAddressSuburb.v129" = "ชานเมือง"; + +/* Label for the telephone number field, allowing users to input their contact number. This is essential for communication and service provision, ensuring contact details are autofilled correctly. */ +"Addresses.EditAddress.AutofillAddressTel.v129" = "โทรศัพท์"; + +/* Label for the input field for the townland, a specific type of land division used in rural areas. Enhances address detail for users in regions where townlands are a common addressing component. */ +"Addresses.EditAddress.AutofillAddressTownland.v129" = "เขต"; + +/* Label for the field to input the name of a village or township. This is crucial for addresses in rural areas, ensuring the autofill feature accurately captures all necessary geographical details. */ +"Addresses.EditAddress.AutofillAddressVillageTownship.v129" = "หมู่บ้านหรือเขตการปกครอง"; + +/* Label for the ZIP code field, primarily used in the United States for mail sorting. Key for autofill to accurately complete addresses for shipping, billing, and service provision. */ +"Addresses.EditAddress.AutofillAddressZip.v129" = "รหัสไปรษณีย์"; + +/* Label for the button to cancel the current autofill operation or exit the form without saving changes. Provides users with an option to back out of a process without making any modifications. */ +"Addresses.EditAddress.AutofillCancelButton.v129" = "ยกเลิก"; + +/* Title for the option allowing users to edit an existing saved address. This is used within the settings for autofill, enabling users to update their address details for accurate form autofilling. */ +"Addresses.EditAddress.AutofillEditAddressTitle.v129" = "แก้ไขที่อยู่"; + +/* Title for the input field where users can enter their street address. This is used within the settings for autofill, allowing users to provide their street address for accurate form autofilling. */ +"Addresses.EditAddress.AutofillEditStreetAddressTitle.v129" = "ที่อยู่ถนน"; + +/* Label for the button to save the current address details entered or edited by the user. This action confirms the user's changes and updates their autofill settings accordingly. */ +"Addresses.EditAddress.AutofillSaveButton.v129" = "บันทึก"; + +/* Title for the option allowing users to view an existing saved address. This is used within the settings for autofill, enabling users to see their address details for accurate form autofilling. */ +"Addresses.EditAddress.AutofillViewAddressTitle.v129" = "ดูที่อยู่"; + +/* Button label for closing the view where user can view their address info. */ +"Addresses.EditAddress.CloseNavBarButtonLabel.v129" = "ปิด"; + +/* Button label for editing the address details shown in the form. */ +"Addresses.EditAddress.EditNavBarButtonLabel.v129" = "แก้ไข"; + +/* Title for button that offers the user the option to remove an address. */ +"Addresses.EditAddress.RemoveAddressButtonTitle.v129" = "เอาที่อยู่ออก"; + +/* Toast message confirming that an address has been successfully removed. */ +"Addresses.Toast.AddressRemovedConfirmation.v129" = "เอาที่อยู่ออกแล้ว"; + +/* Toast message confirming that an address has been successfully saved. */ +"Addresses.Toast.AddressSavedConfirmation.v129" = "บันทึกที่อยู่แล้ว"; + +/* Toast message indicating an error occurred while trying to save an address. */ +"Addresses.Toast.AddressSaveError.v129" = "ไม่สามารถบันทึกที่อยู่ได้"; + +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "ไม่สามารถเอาที่อยู่ออกได้"; + +/* Suggestion to try again after an error occurred while trying to save an address. */ +"Addresses.Toast.AddressSaveRetrySuggestion.v129" = "ลองอีกครั้ง"; + +/* Toast message confirming that an address has been successfully updated. */ +"Addresses.Toast.AddressUpdatedConfirmation.v129" = "ปรับปรุงข้อมูลที่อยู่แล้ว"; + diff --git a/firefox-ios/Shared/Supporting Files/th.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/th.lproj/EnhancedTrackingProtection.strings new file mode 100644 index 000000000000..db37098f558e --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/th.lproj/EnhancedTrackingProtection.strings @@ -0,0 +1,69 @@ +/* The text for the clear cookies and site data alert button inside the enhanced tracking protection screen. */ +"Menu.EnhancedTrackingProtection.ClearData.AlertCancelButton.v128" = "ยกเลิก"; + +/* The text for the clear cookies and site data alert button inside the enhanced tracking protection screen. */ +"Menu.EnhancedTrackingProtection.ClearData.AlertOkButton.v128" = "ล้าง"; + +/* The text for the clear cookies and site data alert inside the enhanced tracking protection screen. The placeholder will be replaced with the user's currently visited website */ +"Menu.EnhancedTrackingProtection.ClearData.AlertText.v128" = "การเอาคุกกี้และข้อมูลไซต์สำหรับ %@ ออกอาจทำให้คุณออกจากระบบเว็บไซต์และล้างตะกร้าสินค้า"; + +/* The title for the clear cookies and site data alert inside the enhanced tracking protection screen. */ +"Menu.EnhancedTrackingProtection.ClearData.AlertTitle.v128" = "ล้างคุกกี้และข้อมูลไซต์"; + +/* The title for the clear cookies and site data button inside the enhanced tracking protection screen. */ +"Menu.EnhancedTrackingProtection.ClearData.ButtonTitle.v128" = "ล้างคุกกี้และข้อมูลไซต์"; + +/* The text for the clear cookies and site data toast that appears when the user selects to clear the cookies */ +"Menu.EnhancedTrackingProtection.ClearData.ToastMessage.v128" = "เอาคุกกี้และข้อมูลไซต์ออกแล้ว"; + +/* Text to let users know that the current website is secure. */ +"Menu.EnhancedTrackingProtection.Details.ConnectionSecure.v128" = "การเชื่อมต่อปลอดภัย"; + +/* Text to let users know that the current website is not secure. */ +"Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "การเชื่อมต่อไม่ปลอดภัย"; + +/* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "คุกกี้ติดตามข้ามไซต์: %@"; + +/* Text to let users know how many fingerprinters were blocked on the current website. The placeholder will show the number of fingerprinters detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.Fingerprinter.v129" = "ลายนิ้วมือดิจิทัล: %@"; + +/* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ +"Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "ตัวติดตามโซเชียลมีเดีย: %@"; + +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ +"Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "ตัวติดตามที่ถูกปิดกั้น: %@"; + +/* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ +"Menu.EnhancedTrackingProtection.Details.Verifier.v128" = "ยืนยันโดย %@"; + +/* Header for the enhanced tracking protection screen when the user has opted out of the feature. Placeholder will be replaced by the app name */ +"Menu.EnhancedTrackingProtection.Off.Header.v128" = "%@ ไม่ได้ทำงานอยู่ เราขอแนะนำให้เปิดการป้องกันกลับมาใหม่อีกครั้ง"; + +/* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ +"Menu.EnhancedTrackingProtection.Off.Title.v128" = "คุณปิดการป้องกัน"; + +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ +"Menu.EnhancedTrackingProtection.On.Header.v128" = "คุณได้รับการปกป้อง หากเราพบสิ่งใดเราจะแจ้งให้คุณทราบ"; + +/* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "โปรดระวังในไซต์นี้"; + +/* Title for the enhanced tracking protection screen when the user has selected to be protected. The placeholder will have the value of the app name */ +"Menu.EnhancedTrackingProtection.On.Title.v128" = "%@ ทำงานอยู่"; + +/* The title for the privacy settings button inside the enhanced tracking protection screen. */ +"Menu.EnhancedTrackingProtection.PrivacySettings.Title.v128" = "การตั้งค่าความเป็นส่วนตัว"; + +/* Title for the switch to enable/disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.Switch.Title.v128" = "การป้องกันการติดตามแบบพิเศษ"; + +/* A switch to disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.SwitchOff.Text.v128" = "การป้องกันปิดอยู่ เราขอแนะนำให้เปิดกลับมาใหม่อีกครั้ง"; + +/* A switch to disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.SwitchOff.Text.v129" = "การป้องกันปิดอยู่ เราขอแนะนำให้เปิดกลับมาใหม่อีกครั้ง"; + +/* A switch to disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.SwitchOn.Text.v128" = "หากมีบางอย่างดูเสียหายในไซต์นี้ ให้ลองปิดการป้องกัน"; + diff --git a/firefox-ios/Shared/Supporting Files/th.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/th.lproj/Microsurvey.strings index 1485c21fbd95..1ccb90735d0d 100644 --- a/firefox-ios/Shared/Supporting Files/th.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/th.lproj/Microsurvey.strings @@ -7,6 +7,9 @@ /* On top of the bottom toolbar, there can be a microsurvey prompt, this is the accessibility label for the close button that appears on the prompt that allows the user to dismiss the microsurvey prompt. */ "Microsurvey.Prompt.Close.Button.AccessibilityLabel.v127" = "ปิด"; +/* On top of the bottom toolbar, there can be a microsurvey prompt, this is the logo image that appears on the prompt to inform the prompt is coming from the app specifically. Placeholder is for the app name. */ +"Microsurvey.Prompt.LogoImage.AccessibilityLabel.v129" = "โลโก้ %@"; + /* On top of the bottom toolbar, there can be a microsurvey prompt, this is the title for the text that appears on the prompt to inform the user that this is a prompt to take a survey. Placeholder is for the app name. */ "Microsurvey.Prompt.TitleLabel.v127" = "ช่วยเราทำให้ %@ ดีขึ้น โดยสละเวลาเพียงนาทีเดียว"; @@ -28,6 +31,12 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the title for the header on the screen. */ "Microsurvey.Survey.HeaderLabel.v127" = "ทำแบบสำรวจนี้ให้เสร็จสมบูรณ์"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the title for the header on the screen. */ +"Microsurvey.Survey.HeaderLabel.v129" = "โปรดทำแบบสำรวจให้เสร็จสมบูรณ์"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the logo image that appears on the bottom sheet that informs the user that it is coming from the app specifically. Placeholder is for the app name. */ +"Microsurvey.Survey.LogoImage.AccessibilityLabel.v129" = "โลโก้ %@"; + /* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ "Microsurvey.Survey.Options.LikertScaleOption1.v127" = "พึงพอใจมาก"; @@ -43,6 +52,18 @@ /* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ "Microsurvey.Survey.Options.LikertScaleOption5.v127" = "ไม่พอใจมาก"; +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. It indicates that the user has not use the feature that the survey is inquiring about. */ +"Microsurvey.Survey.Options.LikertScaleOption6.v129" = "ฉันไม่ได้ใช้มัน"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states the order the survey option in the list of options. First placeholder is the number the option is in the list and the second placeholder is the total number of options such as 1 out of 6. */ +"Microsurvey.Survey.OptionsOrder.AccessibilityLabel.v129" = "%1$@ จาก %2$@"; + /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this the title of a link on the survey and allows the user to navigate to our privacy policy details. */ "Microsurvey.Survey.PrivacyPolicyLink.v127" = "ประกาศความเป็นส่วนตัว"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was selected. */ +"Microsurvey.Survey.RadioButton.Selected.AccessibilityLabel.v129" = "เลือกแล้ว"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ +"Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "ไม่ได้เลือก"; + diff --git a/firefox-ios/Shared/Supporting Files/th.lproj/PasswordAutofill.strings b/firefox-ios/Shared/Supporting Files/th.lproj/PasswordAutofill.strings index 915b135c3484..832f2d863c73 100644 --- a/firefox-ios/Shared/Supporting Files/th.lproj/PasswordAutofill.strings +++ b/firefox-ios/Shared/Supporting Files/th.lproj/PasswordAutofill.strings @@ -1,3 +1,6 @@ +/* This label is used in a cell found in the list of autofill login options in place of an actual username to denote that no username was saved for this login */ +"PasswordAutofill.LoginListCellNoUsername.v129" = "(ไม่มีชื่อผู้ใช้)"; + /* This label is used for a button in the password list screen allowing users to manage their saved passwords. It's meant to direct users to where they can add, remove, or edit their saved passwords. */ "PasswordAutofill.ManagePasswordsButton.v124" = "จัดการรหัสผ่าน"; diff --git a/firefox-ios/Shared/Supporting Files/th.lproj/QRCode.strings b/firefox-ios/Shared/Supporting Files/th.lproj/QRCode.strings new file mode 100644 index 000000000000..b3ea815b926a --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/th.lproj/QRCode.strings @@ -0,0 +1,3 @@ +/* Accessibility label of the QR code button in the toolbar */ +"QRCode.Toolbar.Button.A11y.Title.v128" = "สแกนคิวอาร์โค้ด"; + diff --git a/firefox-ios/Shared/Supporting Files/th.lproj/ScanQRCode.strings b/firefox-ios/Shared/Supporting Files/th.lproj/ScanQRCode.strings new file mode 100644 index 000000000000..e73e01c34135 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/th.lproj/ScanQRCode.strings @@ -0,0 +1,9 @@ +/* Allow button to open URL from scanned QR Code */ +"ScanQRCode.ConfirmOpenURL.AllowButton.v129" = "อนุญาต"; + +/* Deny button to cancel opening URL from scanned QR Code */ +"ScanQRCode.ConfirmOpenURL.DenyButton.v129" = "ปฏิเสธ"; + +/* Text of the prompt to ask user permission to open a URL from a scanned QR code. Placeholder is app name. */ +"ScanQRCode.ConfirmOpenURL.Message.v129" = "อนุญาตให้ %@ เปิดหรือไม่?"; + diff --git a/firefox-ios/Shared/Supporting Files/th.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/th.lproj/Toolbar.strings new file mode 100644 index 000000000000..2010ad34c1d2 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/th.lproj/Toolbar.strings @@ -0,0 +1,3 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "แท็บใหม่"; + diff --git a/firefox-ios/Shared/Supporting Files/tr.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/tr.lproj/EditAddress.strings index bf8b862c8235..50f76b49f020 100644 --- a/firefox-ios/Shared/Supporting Files/tr.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/tr.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "Adres kaydedilemedi"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Adres kaldırılamadı"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Yeniden dene"; diff --git a/firefox-ios/Shared/Supporting Files/tr.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/tr.lproj/EnhancedTrackingProtection.strings index 32fbab745f79..6fd19302c05d 100644 --- a/firefox-ios/Shared/Supporting Files/tr.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/tr.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Bağlantı güvenli değil"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Hiç takip kodu bulunamadı"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Siteler arası takip çerezleri: %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Sosyal medya takip kodları: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Engellenen takip kodları: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Korumaları kapattınız"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Koruma altındasınız. Bir şey tespit edersek size haber vereceğiz."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "Bağlantınız güvenli değil."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Bu sitede dikkatli olun"; diff --git a/firefox-ios/Shared/Supporting Files/tr.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/tr.lproj/Microsurvey.strings index 485c8c8ef097..43bf0715932f 100644 --- a/firefox-ios/Shared/Supporting Files/tr.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/tr.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Seçilmedi"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Anket"; + diff --git a/firefox-ios/Shared/Supporting Files/tr.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/tr.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..8b515f0a645e --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/tr.lproj/NativeErrorPage.strings @@ -0,0 +1,9 @@ +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "Bir SSL hatası oluştu ve sunucuyla güvenli bağlantı kurulamadı."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "Farklı bir cihazdan bağlanmayı deneyin. Modeminizi veya yönlendiricinizi kontrol edin. Wi-Fi bağlantınızı kesip yeniden bağlanın."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "İnternet bağlantınızla ilgili bir sorun var."; + diff --git a/firefox-ios/Shared/Supporting Files/tr.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/tr.lproj/Settings.strings index d63254316dc4..118bb9141d38 100644 --- a/firefox-ios/Shared/Supporting Files/tr.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/tr.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Adresleri yönet"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "%@ adresi"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "KAYITLI ADRESLER"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Adresleri %@ uygulamasına kaydet"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "Bilgilerinizi daha sonra hızlıca erişmek için güvenli bir şekilde kaydedin."; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "Telefon numaraları ve e-posta adresleri de dahildir"; diff --git a/firefox-ios/Shared/Supporting Files/tr.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/tr.lproj/Toolbar.strings new file mode 100644 index 000000000000..3452401fd8c6 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/tr.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Yeni sekme"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Bu sekmeyi kapat"; + diff --git a/firefox-ios/Shared/Supporting Files/ug.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/ug.lproj/EditAddress.strings index d4a43d3346d2..886cdc5ad875 100644 --- a/firefox-ios/Shared/Supporting Files/ug.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/ug.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "ئادرېسنى ساقلىيالمايدۇ"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "ئادرېسنى چىقىرىۋېتەلمەيدۇ"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "قايتا سىنا"; diff --git a/firefox-ios/Shared/Supporting Files/ug.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/ug.lproj/EnhancedTrackingProtection.strings index a3cc03fd5a5f..91099c4039c4 100644 --- a/firefox-ios/Shared/Supporting Files/ug.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/ug.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "باغلىنىش بىخەتەر ئەمەس"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "ھېچقانداق ئىزلىغۇچى تېپىلمىدى"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "بېكەت ھالقىغان ئىزلاش ساقلانمىسى: %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "ئىجتىمائىي تاراتقۇ ئىزلىغۇچ: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "ئىزلىغۇچى توسۇلدى: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "قوغداشنى تاقىۋەتتىڭىز"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "قوغدالدىڭىز. ئەگەر بىرەر نەرسە بايقىساق، سىزگە بىلدۈرىمىز."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "باغلىنىشىڭىز بىخەتەر ئەمەس."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "بۇ تور بېكەتتىن ئېھتىيات قىلىڭ"; diff --git a/firefox-ios/Shared/Supporting Files/ug.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/ug.lproj/Microsurvey.strings index 62b655d0f3d6..08d00ef18313 100644 --- a/firefox-ios/Shared/Supporting Files/ug.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/ug.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "تاللانمىدى"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "راي سىناش"; + diff --git a/firefox-ios/Shared/Supporting Files/ug.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/ug.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..c5f367ed8469 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ug.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "قايتا يۈكلە"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "SSL خاتالىقى يۈز بەردى، مۇلازىمېتىرغا بىخەتەر باغلىنالمايدۇ."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "ئېھتىيات قىلىڭ. بىرەر نەرسە توغرا ئەمەستەك قىلىدۇ."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "باشقا ئۈسكۈنىگە باغلاپ سىناپ بېقىڭ. مودېمىڭىز ياكى يېتەكلىگۈچىڭىزنى تەكشۈرۈڭ. باغلىنىشنى ئۈزۈپ ئاندىن سىمسىز تور يەنى Wi-Fi غا قايتا باغلاڭ."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "ئىنتېرنېت باغلىنىشىڭىزدا مەسىلە باردەك قىلىدۇ."; + diff --git a/firefox-ios/Shared/Supporting Files/ug.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/ug.lproj/Settings.strings index 4f2aeb8289fb..615ad26289d3 100644 --- a/firefox-ios/Shared/Supporting Files/ug.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/ug.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "ئادرېس باشقۇرۇش"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "%@ نىڭ ئادرېسى"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "ساقلانغان ئادرېس"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "ئاردرېسنى %@ غا ساقلايدۇ"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "كېيىنچە تېز زىيارەت قىلىش ئۈچۈن ئۇچۇرلىرىڭىزنى بىخەتەر ساقلايدۇ."; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "تېلېفون نومۇرى ۋە ئېلخەت ئادرېسىنى ئۆز ئىچىگە ئالىدۇ"; diff --git a/firefox-ios/Shared/Supporting Files/ug.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/ug.lproj/Toolbar.strings new file mode 100644 index 000000000000..967125334676 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/ug.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "يېڭى بەتكۈچ"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "بۇ بەتكۈچنى ياپ"; + diff --git a/firefox-ios/Shared/Supporting Files/uk.lproj/ActivityStream.strings b/firefox-ios/Shared/Supporting Files/uk.lproj/ActivityStream.strings new file mode 100644 index 000000000000..8c4479770654 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/uk.lproj/ActivityStream.strings @@ -0,0 +1,3 @@ +/* String used in the section title of the Bookmarks section on Home Screen. */ +"ActivityStream.Bookmarks.Title.v128" = "Закладки"; + diff --git a/firefox-ios/Shared/Supporting Files/uk.lproj/AddressToolbar.strings b/firefox-ios/Shared/Supporting Files/uk.lproj/AddressToolbar.strings new file mode 100644 index 000000000000..e0e415b919f2 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/uk.lproj/AddressToolbar.strings @@ -0,0 +1,3 @@ +/* Accessibility label for the address field in the address toolbar. */ +"AddressToolbar.Location.A11y.Label.v128" = "Введіть запит чи адресу"; + diff --git a/firefox-ios/Shared/Supporting Files/uk.lproj/CustomizeFirefoxHome.strings b/firefox-ios/Shared/Supporting Files/uk.lproj/CustomizeFirefoxHome.strings index 1a191ed1322e..86b116668ad8 100644 --- a/firefox-ios/Shared/Supporting Files/uk.lproj/CustomizeFirefoxHome.strings +++ b/firefox-ios/Shared/Supporting Files/uk.lproj/CustomizeFirefoxHome.strings @@ -1,3 +1,6 @@ +/* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle Bookmarks section on the Firefox homepage on or off */ +"Settings.Home.Option.Bookmarks.v128" = "Закладки"; + /* In the settings menu, in the Firefox homepage customization section, this is the subtitle for the option that allows users to turn the Pocket Recommendations section on the Firefox homepage on or off. The placeholder is the pocket app name. */ "Settings.Home.Option.ThoughtProvokingStories.subtitle.v116" = "Пропоновані статті від %@"; diff --git a/firefox-ios/Shared/Supporting Files/uk.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/uk.lproj/EditAddress.strings new file mode 100644 index 000000000000..4eda5e78c5b1 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/uk.lproj/EditAddress.strings @@ -0,0 +1,96 @@ +/* Title for the cancel button in the remove address alert. */ +"Addresses.EditAddress.Alert.CancelButton.v129" = "Скасувати"; + +/* Title for the remove button in the remove address alert. */ +"Addresses.EditAddress.Alert.RemoveButton.v129" = "Вилучити"; + +/* Title for the alert indicating the action to remove an address. */ +"Addresses.EditAddress.Alert.Title.v129" = "Вилучити адресу"; + +/* Title for the interface option where users can add a new address for autofill purposes. This facilitates quicker form completion by automatically filling in the user's address information. */ +"Addresses.EditAddress.AutofillAddAddressTitle.v129" = "Додати адресу"; + +/* Label for the field where users input the city part of their address. This information is crucial for mail delivery and service provision, ensuring accurate city identification in autofill settings. */ +"Addresses.EditAddress.AutofillAddressCity.v129" = "Місто"; + +/* Label for the field where users can specify just the country, used in contexts where full address details are not required. Simplifies autofill when only country information is necessary. */ +"Addresses.EditAddress.AutofillAddressCountryOnly.v129" = "Країна"; + +/* Label for the country or region field in address forms, allowing users to specify their country or territorial region. This is fundamental for international mail and services, ensuring autofill accuracy across borders. */ +"Addresses.EditAddress.AutofillAddressCountryRegion.v129" = "Країна або регіон"; + +/* Label for the district field in the address form, allowing users to specify their district for more precise location identification. This aids in refining address details for accurate autofill. */ +"Addresses.EditAddress.AutofillAddressDistrict.v129" = "Район"; + +/* Label for the emirate field, essential for addresses in the United Arab Emirates. Including emirate details ensures the autofill feature accurately represents user addresses. */ +"Addresses.EditAddress.AutofillAddressEmirate.v129" = "Емірат"; + +/* Label for the field where users specify the name of an island, if applicable. Important for addresses in archipelagic regions, aiding in precise location identification during autofill. */ +"Addresses.EditAddress.AutofillAddressIsland.v129" = "Острів"; + +/* Label for the field where the user inputs their full name as part of an address form. Essential for personalized form submissions and ensuring information accuracy in autofilled forms. */ +"Addresses.EditAddress.AutofillAddressName.v129" = "Ім'я"; + +/* Label for the field where users can input the name of their neighborhood. This detail adds precision to addresses, especially in densely populated areas, improving the accuracy of autofill. */ +"Addresses.EditAddress.AutofillAddressNeighborhood.v129" = "Околиці"; + +/* Label for the oblast field, relevant for addresses in countries like Russia and Ukraine. Oblasts are a significant administrative division, and their specification aids in autofill accuracy. */ +"Addresses.EditAddress.AutofillAddressOblast.v129" = "Область"; + +/* Label for the input field designated for the organization's name related to the address. Helps in distinguishing addresses used for business or personal purposes in autofill settings. */ +"Addresses.EditAddress.AutofillAddressOrganization.v129" = "Організація"; + +/* Label for the parish field, significant in places where parishes are used for local administration and addressing. Ensures users can specify parish details for better autofill accuracy. */ +"Addresses.EditAddress.AutofillAddressParish.v129" = "Парафія"; + +/* Label for the postal code field, universally used in address forms to specify the area code for mail sorting. Essential for autofill to ensure mail and services are accurately routed. */ +"Addresses.EditAddress.AutofillAddressPostalCode.v129" = "Поштовий індекс"; + +/* Label for the post town field, used primarily in the UK and some other regions for mail sorting. Essential for users in applicable areas to specify for correct mail delivery through autofill. */ +"Addresses.EditAddress.AutofillAddressPostTown.v129" = "Поштове містечко"; + +/* Label for the prefecture field, essential for addresses in countries like Japan where prefectures are a major administrative division. Aids in precise location specification for autofill. */ +"Addresses.EditAddress.AutofillAddressPrefecture.v129" = "Префектура"; + +/* Label for the suburb field, enabling users to add suburb details to their address. This is important for accurate delivery and services in suburban areas, enhancing autofill functionality. */ +"Addresses.EditAddress.AutofillAddressSuburb.v129" = "Передмістя"; + +/* Label for the telephone number field, allowing users to input their contact number. This is essential for communication and service provision, ensuring contact details are autofilled correctly. */ +"Addresses.EditAddress.AutofillAddressTel.v129" = "Телефон"; + +/* Label for the input field for the townland, a specific type of land division used in rural areas. Enhances address detail for users in regions where townlands are a common addressing component. */ +"Addresses.EditAddress.AutofillAddressTownland.v129" = "Містечко"; + +/* Label for the field to input the name of a village or township. This is crucial for addresses in rural areas, ensuring the autofill feature accurately captures all necessary geographical details. */ +"Addresses.EditAddress.AutofillAddressVillageTownship.v129" = "Село чи селище"; + +/* Label for the ZIP code field, primarily used in the United States for mail sorting. Key for autofill to accurately complete addresses for shipping, billing, and service provision. */ +"Addresses.EditAddress.AutofillAddressZip.v129" = "Поштовий індекс"; + +/* Label for the button to cancel the current autofill operation or exit the form without saving changes. Provides users with an option to back out of a process without making any modifications. */ +"Addresses.EditAddress.AutofillCancelButton.v129" = "Скасувати"; + +/* Title for the option allowing users to edit an existing saved address. This is used within the settings for autofill, enabling users to update their address details for accurate form autofilling. */ +"Addresses.EditAddress.AutofillEditAddressTitle.v129" = "Редагувати адресу"; + +/* Title for the input field where users can enter their street address. This is used within the settings for autofill, allowing users to provide their street address for accurate form autofilling. */ +"Addresses.EditAddress.AutofillEditStreetAddressTitle.v129" = "Вулиця"; + +/* Label for the button to save the current address details entered or edited by the user. This action confirms the user's changes and updates their autofill settings accordingly. */ +"Addresses.EditAddress.AutofillSaveButton.v129" = "Зберегти"; + +/* Title for the option allowing users to view an existing saved address. This is used within the settings for autofill, enabling users to see their address details for accurate form autofilling. */ +"Addresses.EditAddress.AutofillViewAddressTitle.v129" = "Переглянути адресу"; + +/* Button label for closing the view where user can view their address info. */ +"Addresses.EditAddress.CloseNavBarButtonLabel.v129" = "Закрити"; + +/* Button label for editing the address details shown in the form. */ +"Addresses.EditAddress.EditNavBarButtonLabel.v129" = "Редагувати"; + +/* Toast message confirming that an address has been successfully saved. */ +"Addresses.Toast.AddressSavedConfirmation.v129" = "Адресу збережено"; + +/* Toast message indicating an error occurred while trying to save an address. */ +"Addresses.Toast.AddressSaveError.v129" = "Не вдалося зберегти адресу"; + diff --git a/firefox-ios/Shared/Supporting Files/uk.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/uk.lproj/EnhancedTrackingProtection.strings new file mode 100644 index 000000000000..17c2eb044f1f --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/uk.lproj/EnhancedTrackingProtection.strings @@ -0,0 +1,30 @@ +/* The text for the clear cookies and site data alert button inside the enhanced tracking protection screen. */ +"Menu.EnhancedTrackingProtection.ClearData.AlertCancelButton.v128" = "Скасувати"; + +/* The text for the clear cookies and site data alert button inside the enhanced tracking protection screen. */ +"Menu.EnhancedTrackingProtection.ClearData.AlertOkButton.v128" = "Очистити"; + +/* The title for the clear cookies and site data alert inside the enhanced tracking protection screen. */ +"Menu.EnhancedTrackingProtection.ClearData.AlertTitle.v128" = "Стерти файли cookie та дані сайтів"; + +/* The title for the clear cookies and site data button inside the enhanced tracking protection screen. */ +"Menu.EnhancedTrackingProtection.ClearData.ButtonTitle.v128" = "Стерти файли cookie та дані сайтів"; + +/* Text to let users know that the current website is not secure. */ +"Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Незахищене з'єднання"; + +/* Header for the enhanced tracking protection screen when the user has opted out of the feature. Placeholder will be replaced by the app name */ +"Menu.EnhancedTrackingProtection.Off.Header.v128" = "%@ не працює. Ми пропонуємо знову ввімкнути захист."; + +/* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ +"Menu.EnhancedTrackingProtection.Off.Title.v128" = "Ви вимкнули захист"; + +/* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Будьте обережні на цьому сайті"; + +/* Title for the enhanced tracking protection screen when the user has selected to be protected. The placeholder will have the value of the app name */ +"Menu.EnhancedTrackingProtection.On.Title.v128" = "%@ на сторожі"; + +/* A switch to disable enhanced tracking protection inside the menu. */ +"Menu.EnhancedTrackingProtection.SwitchOff.Text.v128" = "Захист ВИМКНЕНО. Ми пропонуємо ввімкнути його знову."; + diff --git a/firefox-ios/Shared/Supporting Files/uk.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/uk.lproj/Microsurvey.strings index abe1c2626560..0e0326b3bfa1 100644 --- a/firefox-ios/Shared/Supporting Files/uk.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/uk.lproj/Microsurvey.strings @@ -43,6 +43,21 @@ /* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. */ "Microsurvey.Survey.Options.LikertScaleOption5.v127" = "Дуже незадоволені"; +/* On the microsurvey, this is the title for one of the options that the user can select to answer the survey. It indicates that the user has not use the feature that the survey is inquiring about. */ +"Microsurvey.Survey.Options.LikertScaleOption6.v129" = "Я цим не користуюсь"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states the order the survey option in the list of options. First placeholder is the number the option is in the list and the second placeholder is the total number of options such as 1 out of 6. */ +"Microsurvey.Survey.OptionsOrder.AccessibilityLabel.v129" = "%1$@ з %2$@"; + /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this the title of a link on the survey and allows the user to navigate to our privacy policy details. */ "Microsurvey.Survey.PrivacyPolicyLink.v127" = "Положення про приватність"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was selected. */ +"Microsurvey.Survey.RadioButton.Selected.AccessibilityLabel.v129" = "Вибрано"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ +"Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Не вибрано"; + +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Опитування"; + diff --git a/firefox-ios/Shared/Supporting Files/uk.lproj/PasswordAutofill.strings b/firefox-ios/Shared/Supporting Files/uk.lproj/PasswordAutofill.strings index 99f9c596b913..d3bb8a57e0ce 100644 --- a/firefox-ios/Shared/Supporting Files/uk.lproj/PasswordAutofill.strings +++ b/firefox-ios/Shared/Supporting Files/uk.lproj/PasswordAutofill.strings @@ -1,3 +1,6 @@ +/* This label is used in a cell found in the list of autofill login options in place of an actual username to denote that no username was saved for this login */ +"PasswordAutofill.LoginListCellNoUsername.v129" = "(без імені користувача)"; + /* This label is used for a button in the password list screen allowing users to manage their saved passwords. It's meant to direct users to where they can add, remove, or edit their saved passwords. */ "PasswordAutofill.ManagePasswordsButton.v124" = "Керувати паролями"; diff --git a/firefox-ios/Shared/Supporting Files/uk.lproj/ScanQRCode.strings b/firefox-ios/Shared/Supporting Files/uk.lproj/ScanQRCode.strings new file mode 100644 index 000000000000..beb544f138d1 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/uk.lproj/ScanQRCode.strings @@ -0,0 +1,6 @@ +/* Allow button to open URL from scanned QR Code */ +"ScanQRCode.ConfirmOpenURL.AllowButton.v129" = "Дозволити"; + +/* Deny button to cancel opening URL from scanned QR Code */ +"ScanQRCode.ConfirmOpenURL.DenyButton.v129" = "Заборонити"; + diff --git a/firefox-ios/Shared/Supporting Files/uk.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/uk.lproj/Toolbar.strings new file mode 100644 index 000000000000..7710091148f4 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/uk.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Нова вкладка"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Закрити цю вкладку"; + diff --git a/firefox-ios/Shared/Supporting Files/vi.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/vi.lproj/EditAddress.strings index 137e42206ce3..34078b06334a 100644 --- a/firefox-ios/Shared/Supporting Files/vi.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/vi.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "Không thể lưu địa chỉ"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "Không thể xóa địa chỉ"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "Thử lại"; diff --git a/firefox-ios/Shared/Supporting Files/vi.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/vi.lproj/EnhancedTrackingProtection.strings index 38fa12e89396..5c56f6f58d9e 100644 --- a/firefox-ios/Shared/Supporting Files/vi.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/vi.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "Kết nối không an toàn"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "Không tìm thấy trình theo dõi"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "Cookie theo dõi trên nhiều trang web: %@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "Trình theo truyền thông xã hội: %@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "Trình theo dõi bị chặn: %@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "Bạn đã tắt trình chống"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "Bạn đã được bảo vệ. Nếu chúng tôi phát hiện ra điều gì đó, chúng tôi sẽ cho bạn biết."; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "Kết nối của bạn không an toàn."; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "Hãy cẩn thận trên trang web này"; diff --git a/firefox-ios/Shared/Supporting Files/vi.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/vi.lproj/Microsurvey.strings index 81a4cf3bc51b..951dc273d24b 100644 --- a/firefox-ios/Shared/Supporting Files/vi.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/vi.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "Đã bỏ chọn"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "Khảo sát"; + diff --git a/firefox-ios/Shared/Supporting Files/vi.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/vi.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..2054303aaa95 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/vi.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "Tải lại"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "Đã xảy ra lỗi SSL và không thể thực hiện kết nối an toàn tới máy chủ."; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "Hãy cẩn thận. Có điều gì đó không ổn."; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "Hãy thử kết nối trên một thiết bị khác. Kiểm tra modem hoặc bộ định tuyến của bạn. Ngắt kết nối và kết nối lại với Wi-Fi."; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "Có vẻ như đã xảy ra sự cố với kết nối Internet của bạn."; + diff --git a/firefox-ios/Shared/Supporting Files/vi.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/vi.lproj/Settings.strings index b2367344bb57..22cc1388f1ff 100644 --- a/firefox-ios/Shared/Supporting Files/vi.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/vi.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "Quản lý địa chỉ"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "Địa chỉ cho %@"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "ĐỊA CHỈ ĐÃ LƯU"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "Lưu địa chỉ đến %@"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "Lưu thông tin của bạn một cách an toàn để có thể truy cập nhanh vào thông tin đó sau này."; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "Bao gồm số điện thoại và địa chỉ email"; diff --git a/firefox-ios/Shared/Supporting Files/vi.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/vi.lproj/Toolbar.strings new file mode 100644 index 000000000000..c877d4a00d70 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/vi.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "Thẻ mới"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "Đóng thẻ này"; + diff --git a/firefox-ios/Shared/Supporting Files/zh-CN.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/zh-CN.lproj/EditAddress.strings index 94b64b61ab6e..c9196a32edb5 100644 --- a/firefox-ios/Shared/Supporting Files/zh-CN.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/zh-CN.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "无法保存地址"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "无法移除地址"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "重试"; diff --git a/firefox-ios/Shared/Supporting Files/zh-CN.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/zh-CN.lproj/EnhancedTrackingProtection.strings index 079e75584a6f..68df14f4f94c 100644 --- a/firefox-ios/Shared/Supporting Files/zh-CN.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/zh-CN.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "不安全连接"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "未发现跟踪器"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "跨站跟踪性 Cookie:%@ 个"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "社交媒体跟踪器:%@ 个"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "已拦截跟踪器:%@ 个"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "已关闭保护"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "您已受保护,我们会在发现异常情况时告知您。"; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "您的连接不安全。"; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "请谨慎浏览此网站"; diff --git a/firefox-ios/Shared/Supporting Files/zh-CN.lproj/Footer.strings b/firefox-ios/Shared/Supporting Files/zh-CN.lproj/Footer.strings index 6c7e3684ffc7..a51e29acc26d 100644 --- a/firefox-ios/Shared/Supporting Files/zh-CN.lproj/Footer.strings +++ b/firefox-ios/Shared/Supporting Files/zh-CN.lproj/Footer.strings @@ -1,5 +1,5 @@ /* This is the learn more text of the Pocket footer on Firefox Homepage. */ -"FirefoxHomepage.Pocket.Footer.LearnMore.v115" = "了解更多"; +"FirefoxHomepage.Pocket.Footer.LearnMore.v115" = "详细了解"; /* This is the title of the Pocket footer on Firefox Homepage. The first placeholder is for the Pocket app name and the second placeholder for the app name */ "FirefoxHomepage.Pocket.Footer.Title.v116" = "由 %2$@ 系列产品 - %1$@ 提供。"; diff --git a/firefox-ios/Shared/Supporting Files/zh-CN.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/zh-CN.lproj/Microsurvey.strings index 90afbc423a20..090c6691a02f 100644 --- a/firefox-ios/Shared/Supporting Files/zh-CN.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/zh-CN.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "未选中"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "问卷调查"; + diff --git a/firefox-ios/Shared/Supporting Files/zh-CN.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/zh-CN.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..d3a54061165f --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/zh-CN.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "重新加载"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "发生 SSL 错误,无法与服务器建立安全连接。"; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "当心,看来有异常情况。"; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "请尝试通过其他设备连接,检查调制解调器或路由器,断开 Wi-Fi 连接后重新连接。"; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "您的网络连接似乎出了点问题。"; + diff --git a/firefox-ios/Shared/Supporting Files/zh-CN.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/zh-CN.lproj/Settings.strings index 9df6c6312330..436c2a04e0f5 100644 --- a/firefox-ios/Shared/Supporting Files/zh-CN.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/zh-CN.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "管理地址"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "%@ 的地址"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "保存的地址"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "将地址保存到 %@"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "安全保存信息,以便日后使用。"; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "包括电话号码和邮箱地址"; diff --git a/firefox-ios/Shared/Supporting Files/zh-CN.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/zh-CN.lproj/Toolbar.strings new file mode 100644 index 000000000000..74cccebdb2aa --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/zh-CN.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "新建标签页"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "关闭此标签页"; + diff --git a/firefox-ios/Shared/Supporting Files/zh-TW.lproj/EditAddress.strings b/firefox-ios/Shared/Supporting Files/zh-TW.lproj/EditAddress.strings index 0b86e0aeb501..8faaa8eb4a3c 100644 --- a/firefox-ios/Shared/Supporting Files/zh-TW.lproj/EditAddress.strings +++ b/firefox-ios/Shared/Supporting Files/zh-TW.lproj/EditAddress.strings @@ -130,6 +130,9 @@ /* Toast message indicating an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveError.v129" = "無法儲存地址"; +/* Toast message indicating an error occurred while trying to remove an address. */ +"Addresses.Toast.AddressSaveError.v130" = "無法移除地址"; + /* Suggestion to try again after an error occurred while trying to save an address. */ "Addresses.Toast.AddressSaveRetrySuggestion.v129" = "重試"; diff --git a/firefox-ios/Shared/Supporting Files/zh-TW.lproj/EnhancedTrackingProtection.strings b/firefox-ios/Shared/Supporting Files/zh-TW.lproj/EnhancedTrackingProtection.strings index e04f3bf54b63..cf4f5bd4427b 100644 --- a/firefox-ios/Shared/Supporting Files/zh-TW.lproj/EnhancedTrackingProtection.strings +++ b/firefox-ios/Shared/Supporting Files/zh-TW.lproj/EnhancedTrackingProtection.strings @@ -22,6 +22,9 @@ /* Text to let users know that the current website is not secure. */ "Menu.EnhancedTrackingProtection.Details.ConnectionUnsecure.v128" = "連線不安全"; +/* Text to let users know that no trackers were found on the current website. */ +"Menu.EnhancedTrackingProtection.Details.NoTrackers.v131" = "找不到追蹤器"; + /* Text to let users know how many cross-site tracking cookies were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.CrossSite.v129" = "跨網站追蹤 Cookie:%@"; @@ -31,8 +34,7 @@ /* Text to let users know how many social media trackers were blocked on the current website. The placeholder will show the number of such cookies detected */ "Menu.EnhancedTrackingProtection.Details.Trackers.SocialMedia.v129" = "社交媒體追蹤器:%@"; -/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked - Text to let users know that no trackers were found on the current website. */ +/* Text to let users know how many trackers were blocked on the current website. Placeholder for the number of trackers blocked */ "Menu.EnhancedTrackingProtection.Details.Trackers.v128" = "追蹤器封鎖數量:%@"; /* Text to let users know the site verifier, where the placeholder represents the SSL certificate signer which is on the enhanced tracking protection screen after the user taps on the connection details. */ @@ -44,10 +46,12 @@ /* Title for the enhanced tracking protection screen when the user has opted out of the feature. */ "Menu.EnhancedTrackingProtection.Off.Title.v128" = "您已關閉保護"; -/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. - Header for the enhanced tracking protection screen when the user has selected to be protected. */ +/* Header for the enhanced tracking protection screen when the user has selected to be protected. */ "Menu.EnhancedTrackingProtection.On.Header.v128" = "您已受到保護,若我們發現某些不安全的地方會再通知您。"; +/* Header for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ +"Menu.EnhancedTrackingProtection.On.NotSecure.Header.v128" = "您的連線並不安全。"; + /* Title for the enhanced tracking protection screen when the user has selected to be protected but the connection is not secure. */ "Menu.EnhancedTrackingProtection.On.NotSecure.Title.v128" = "在此網站請小心"; diff --git a/firefox-ios/Shared/Supporting Files/zh-TW.lproj/Microsurvey.strings b/firefox-ios/Shared/Supporting Files/zh-TW.lproj/Microsurvey.strings index 52e738c31555..53b990d0501f 100644 --- a/firefox-ios/Shared/Supporting Files/zh-TW.lproj/Microsurvey.strings +++ b/firefox-ios/Shared/Supporting Files/zh-TW.lproj/Microsurvey.strings @@ -67,3 +67,6 @@ /* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label that states whether the survey option was not selected. */ "Microsurvey.Survey.RadioButton.Unselected.AccessibilityLabel.v129" = "未選擇"; +/* After engaging with the microsurvey prompt, the microsurvey pops up as a bottom sheet for the user to answer, this is the accessibility label used to announce that the sheet has appeared. */ +"Microsurvey.Survey.Sheet.AccessibilityLabel.v130" = "問卷調查"; + diff --git a/firefox-ios/Shared/Supporting Files/zh-TW.lproj/NativeErrorPage.strings b/firefox-ios/Shared/Supporting Files/zh-TW.lproj/NativeErrorPage.strings new file mode 100644 index 000000000000..fa9403a18aa6 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/zh-TW.lproj/NativeErrorPage.strings @@ -0,0 +1,15 @@ +/* On error page, this is the text on a button that will try to load the page again. */ +"NativeErrorPage.ButtonLabel.v131" = "重新載入"; + +/* On error page, this is the description for generic error. */ +"NativeErrorPage.GenericError.Description.v131" = "發生 SSL 錯誤,無法與伺服器建立安全連線。"; + +/* On error page, this is the title for generic error. */ +"NativeErrorPage.GenericError.TitleLabel.v131" = "請小心,有些東西不對勁。"; + +/* On error page, this is the description for no internet connection. */ +"NativeErrorPage.NoInternetConnection.Description.v131" = "您可以試著連線到其他裝置、檢查您的數據機或路由器,或將 Wi-Fi 斷線重連。"; + +/* On error page, this is the title for no internet connection */ +"NativeErrorPage.NoInternetConnection.TitleLabel.v131" = "您的網路連線可能有點問題。"; + diff --git a/firefox-ios/Shared/Supporting Files/zh-TW.lproj/Settings.strings b/firefox-ios/Shared/Supporting Files/zh-TW.lproj/Settings.strings index 6526c0220f06..71f08b346e6d 100644 --- a/firefox-ios/Shared/Supporting Files/zh-TW.lproj/Settings.strings +++ b/firefox-ios/Shared/Supporting Files/zh-TW.lproj/Settings.strings @@ -1,6 +1,18 @@ +/* This label is used for a button in the address list screen allowing users to manage their saved addresses. It's meant to direct users to where they can add, remove, or edit their saved addresses. */ +"Addresses.ManageAddressesButton.v130" = "管理已存地址"; + +/* Accessibility label for an address list item in autofill settings screen. The %@ parameter is the address of the user that will read the name, street, city, state, postal code if available. */ +"Addresses.Settings.ListItemA11y.v130" = "%@ 的地址"; + /* On the autofill settings screen, a label for the section that displays the list of saved addresses. This label adds additional context for users regarding the toggle switch that allows saving and autofilling of addresses for webpages. */ "Addresses.Settings.SavedAddressesSectionTitle.v124" = "已儲存的地址"; +/* Title text for the content unavailable view informing users they can create or add new addresses. %@ is the name of the app. */ +"Addresses.Settings.SaveToFirefox.Title.v130" = "將地址儲存至 %@"; + +/* Description text for the content unavailable view informing users they can create or add new addresses. */ +"Addresses.Settings.SecureSaveInfo.Description.v130" = "安全地儲存您的資訊,以便之後快速存取。"; + /* On the autofill settings screen, a label under the title label to add additional context for user in regards to what the toggle switch that allow saving and autofilling of addresses for webpages does, letting users know that this action includes phone numbers and email addresses. */ "Addresses.Settings.Switch.Description.v124" = "包含電話號碼與電子郵件地址"; diff --git a/firefox-ios/Shared/Supporting Files/zh-TW.lproj/Toolbar.strings b/firefox-ios/Shared/Supporting Files/zh-TW.lproj/Toolbar.strings new file mode 100644 index 000000000000..3f0b9f5cca89 --- /dev/null +++ b/firefox-ios/Shared/Supporting Files/zh-TW.lproj/Toolbar.strings @@ -0,0 +1,6 @@ +/* Accessibility label for the new tab button that can be displayed in the navigation or address toolbar. */ +"Toolbar.NewTab.Button.v130" = "開新分頁"; + +/* Label for button on action sheet, accessed via long pressing tab toolbar button, that closes the current tab when pressed */ +"Toolbar.Tab.CloseThisTab.Button.v130" = "關閉此分頁"; + diff --git a/firefox-ios/Shared/co.lproj/Localizable.strings b/firefox-ios/Shared/co.lproj/Localizable.strings index db4d391d61df..8054abe741a5 100644 --- a/firefox-ios/Shared/co.lproj/Localizable.strings +++ b/firefox-ios/Shared/co.lproj/Localizable.strings @@ -611,7 +611,7 @@ "LibraryPanel.History.DeleteGroupedItem.v104" = "Squassà"; /* In the history panel, users will be able to search terms in their browsing history. This label is shown when there is no results after querying the search terms in the user's history. */ -"LibraryPanel.History.NoHistoryFound.v99" = "Nisunu elementu trovu in a cronolugia"; +"LibraryPanel.History.NoHistoryFound.v99" = "Ùn si trova nunda in a cronolugia"; /* In the history panel, this is the title on the button that navigates the user to a screen showing their recently closed tabs. */ "LibraryPanel.History.RecentlyClosedTabs.v99" = "Unghjette chjose pocu fà"; diff --git a/firefox-ios/Shared/co.lproj/LoginManager.strings b/firefox-ios/Shared/co.lproj/LoginManager.strings index 4bc94679709c..45f7e04bef53 100644 --- a/firefox-ios/Shared/co.lproj/LoginManager.strings +++ b/firefox-ios/Shared/co.lproj/LoginManager.strings @@ -50,10 +50,10 @@ "Modified %@" = "Mudificatu %@"; /* Label displayed when no logins are found after searching. */ -"No logins found" = "Alcuna identificazione di cunnessione trova"; +"No logins found" = "Ùn si trova nisuna identificazione di cunnessione"; /* Label displayed when no logins are found after searching. */ -"NoLoginsFound.Title.v122" = "Nisuna parolla d’intesa trova"; +"NoLoginsFound.Title.v122" = "Ùn si trova nisuna parolla d’intesa"; /* Open and Fill website text selection menu item */ "Open & Fill" = "Apre è rinsignà"; diff --git a/firefox-ios/Shared/dsb.lproj/Localizable.strings b/firefox-ios/Shared/dsb.lproj/Localizable.strings index d861801b1f52..dd5d94ccd497 100644 --- a/firefox-ios/Shared/dsb.lproj/Localizable.strings +++ b/firefox-ios/Shared/dsb.lproj/Localizable.strings @@ -5,10 +5,10 @@ "+" = "+"; /* Authentication prompt message with no realm. Parameter is the hostname of the site */ -"A username and password are being requested by %@." = "Wužywarske mě a gronidło pominatej se wót %@."; +"A username and password are being requested by %@." = "Wužywaŕske mě a gronidło pominatej se wót %@."; /* Authentication prompt message with a realm. First parameter is the hostname. Second is the realm string */ -"A username and password are being requested by %@. The site says: %@" = "Wužywarske mě a gronidło pominatej se wót %1$@. Sedło groni: %2$@"; +"A username and password are being requested by %@. The site says: %@" = "Wužywaŕske mě a gronidło pominatej se wót %1$@. Sedło groni: %2$@"; /* Button for reader mode font size. Keep this extremely short! This is shown in the reader mode toolbar. */ "Aa" = "Aa"; @@ -77,7 +77,7 @@ "Amazon" = "Amazon"; /* Authentication prompt title */ -"Authentication required" = "Awtentifikacija trěbna"; +"Authentication required" = "Awtentifikacija trjebna"; /* Accessibility label for the Back button in the tab toolbar. */ "Back" = "Slědk"; @@ -167,7 +167,7 @@ "ContextMenu.ButtonToast.NewPrivateTabOpened.LabelText" = "Nowy priwatny rejtarik jo se wócynił"; /* The label text in the Button Toast for switching to a fresh New Private Tab. */ -"ContextMenu.ButtonToast.NewPrivateTabOpened.LabelText.v113" = "Nowy priwatny rejtark jo se wócynił"; +"ContextMenu.ButtonToast.NewPrivateTabOpened.LabelText.v113" = "Nowy priwatny rejtarik jo se wócynił"; /* The button text in the Button Toast for switching to a fresh New Tab. */ "ContextMenu.ButtonToast.NewTabOpened.ButtonText" = "Pśešaltowaś"; @@ -1754,7 +1754,7 @@ "Use stage servers" = "Testowe serwery wužywaś"; /* Username textbox in Authentication prompt */ -"Username" = "Wužywarske mě"; +"Username" = "Wužywaŕske mě"; /* The message of the error displayed to a user when they trying to change a wallpaper failed. */ "Wallpaper.Change.Error.Body.v106" = "Něco njejo se raźiło z toś tym slězynowym wobrazom."; diff --git a/firefox-ios/Shared/dsb.lproj/LoginManager.strings b/firefox-ios/Shared/dsb.lproj/LoginManager.strings index b68a67cc7fd4..7733e1e022d0 100644 --- a/firefox-ios/Shared/dsb.lproj/LoginManager.strings +++ b/firefox-ios/Shared/dsb.lproj/LoginManager.strings @@ -77,7 +77,7 @@ "Select All" = "Wšykne wubraś"; /* Label displayed above the username row in Login Detail View. */ -"Username" = "Wužywarske mě"; +"Username" = "Wužywaŕske mě"; /* Label displayed above the website row in Login Detail View. */ "Website" = "Websedło"; diff --git a/firefox-ios/Shared/dsb.lproj/Today.strings b/firefox-ios/Shared/dsb.lproj/Today.strings index 46387a413d56..b5df66daba7a 100644 --- a/firefox-ios/Shared/dsb.lproj/Today.strings +++ b/firefox-ios/Shared/dsb.lproj/Today.strings @@ -1,8 +1,8 @@ /* Close Private Tabs button label */ -"TodayWidget.ClosePrivateTabsButton" = "Priwatne rejtarki zacyniś"; +"TodayWidget.ClosePrivateTabsButton" = "Priwatne rejtariki zacyniś"; /* Close Private Tabs */ -"TodayWidget.ClosePrivateTabsLabelV2" = "Priwatne rejtarki zacyniś"; +"TodayWidget.ClosePrivateTabsLabelV2" = "Priwatne\nrejtariki zacyniś"; /* Description for medium size widget to add Firefox Shortcut to home screen */ "TodayWidget.FirefoxShortcutGalleryDescription" = "Pśidajśo swójej startowej wobrazowce zwězanja Firefox"; diff --git a/firefox-ios/Shared/eo.lproj/Localizable.strings b/firefox-ios/Shared/eo.lproj/Localizable.strings index e2f2c6ab3169..a58f8e4d3132 100644 --- a/firefox-ios/Shared/eo.lproj/Localizable.strings +++ b/firefox-ios/Shared/eo.lproj/Localizable.strings @@ -515,7 +515,7 @@ "Hotkeys.Reload.DiscoveryTitle" = "Reŝargi paĝon"; /* A label indicating the keyboard shortcut of directly accessing the URL, location, bar. This label is displayed inside the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ -"Hotkeys.SelectLocationBar.DiscoveryTitle" = "Elekti adresstrion"; +"Hotkeys.SelectLocationBar.DiscoveryTitle" = "Elekti la readresan strion"; /* A label indicating the keyboard shortcut of switching to a subsequent tab. This label is displayed inside the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ "Hotkeys.ShowNextTab.DiscoveryTitle" = "Montri venontan langeton"; diff --git a/firefox-ios/Shared/es-MX.lproj/Localizable.strings b/firefox-ios/Shared/es-MX.lproj/Localizable.strings index 61fbc31d6381..7a1d9694ad4f 100644 --- a/firefox-ios/Shared/es-MX.lproj/Localizable.strings +++ b/firefox-ios/Shared/es-MX.lproj/Localizable.strings @@ -757,6 +757,9 @@ /* Label for the button, displayed in the menu, takes you to History screen when pressed. */ "Menu.History.Label" = "Historial"; +/* Label for the new private tab button in the menu page. Pressing this button opens a new private tab. */ +"Menu.NewPrivateTab.Label" = "Nueva pestaña privada"; + /* Label for the new tab button in the menu page. Pressing this button opens a new tab. */ "Menu.NewTab.v99" = "Nueva pestaña"; diff --git a/firefox-ios/Shared/es-MX.lproj/Menu.strings b/firefox-ios/Shared/es-MX.lproj/Menu.strings index fd43bf0cd701..9669ecea1a34 100644 --- a/firefox-ios/Shared/es-MX.lproj/Menu.strings +++ b/firefox-ios/Shared/es-MX.lproj/Menu.strings @@ -4,6 +4,9 @@ /* Label for the button, displayed in the menu, used to copy the current page link to the clipboard. */ "Menu.CopyLink.Title" = "Copiar enlace"; +/* Label for the button, displayed in the menu, downloads a pdf when pressed. */ +"Menu.DownloadPDF.Label.v129" = "Descargar PDF"; + /* Label for the button, displayed in the menu, used to open the toolbar to search for text within the current page. */ "Menu.FindInPageAction.Title" = "Encontrar en la página"; diff --git a/firefox-ios/Shared/hr.lproj/Default Browser.strings b/firefox-ios/Shared/hr.lproj/Default Browser.strings index 783974a59a5e..fa9269abf171 100644 --- a/firefox-ios/Shared/hr.lproj/Default Browser.strings +++ b/firefox-ios/Shared/hr.lproj/Default Browser.strings @@ -13,6 +13,15 @@ /* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ "DefaultBrowserCard.NextLevel.Description.v108" = "Odaberi Firefox kao zadani preglednik za automatsku brzinu, sigurnost i privatnost."; +/* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ +"DefaultBrowserCard.NextLevel.Title.v108" = "Poboljšaj svakodnevno pregledavanje"; + +/* Description for small home tab banner shown that allows the user to switch their default browser to Firefox. */ +"DefaultBrowserCard.PeaceOfMind.Description.v108" = "Firefox u prosjeku blokira više od 3000 programa za praćenje po korisniku svaki mjesec. Postavi nas kao standardni preglednik za bezbrižnu privatnost."; + +/* Title for small home tab banner shown that allows the user to switch their default browser to Firefox. */ +"DefaultBrowserCard.PeaceOfMind.Title.v108" = "Firefox pokriva privatnost"; + /* Title for small home tab banner shown that allows user to switch their default browser to Firefox. */ "DefaultBrowserCard.Title" = "Promijeni standardni preglednik"; diff --git a/firefox-ios/Shared/hr.lproj/Localizable.strings b/firefox-ios/Shared/hr.lproj/Localizable.strings index 154042c2c97e..af5f25d38a05 100644 --- a/firefox-ios/Shared/hr.lproj/Localizable.strings +++ b/firefox-ios/Shared/hr.lproj/Localizable.strings @@ -544,6 +544,9 @@ /* A label indicating the keyboard shortcut of finding text a user desires within a page again. This label is displayed in the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ "Keyboard.Shortcuts.FindAgain" = "Ponovno pronađi"; +/* A label indicating the keyboard shortcut to reload a tab without it's cache. This label is displayed in the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. */ +"Keyboard.Shortcuts.RefreshWithoutCache.v108" = "Učitaj ponovo zanemarujući predmemoriju"; + /* A label indicating a grouping of related keyboard shortcuts describing actions a user can do with Bookmarks. This label is displayed inside the Discoverability overlay when a user presses the Command key. The Discoverability overlay and shortcut become available only when a user has connected a hardware keyboard to an iPad. See https://drive.google.com/file/d/1gH3tbvDceg7yG5N67NIHS-AXgDgCzBHN/view?usp=sharing for more details. */ "Keyboard.Shortcuts.Section.Bookmark" = "Zabilješke"; @@ -754,6 +757,9 @@ /* Label for the button, displayed in the menu, takes you to History screen when pressed. */ "Menu.History.Label" = "Povijest"; +/* Label for the new private tab button in the menu page. Pressing this button opens a new private tab. */ +"Menu.NewPrivateTab.Label" = "Nova privatna kartica"; + /* Label for the new tab button in the menu page. Pressing this button opens a new tab. */ "Menu.NewTab.v99" = "Nova kartica"; @@ -1141,6 +1147,9 @@ /* The placeholder for URL Field when saving a custom search engine */ "Settings.AddCustomEngine.URLPlaceholder" = "URL (Zamijeni upit s %s)"; +/* Title of setting to block opening external apps when pressing links. */ +"Settings.BlockOpeningExternalApps.Title" = "Blokiraj otvaranje eksternih aplikacija"; + /* Button in Data Management that clears all items. */ "Settings.ClearAllWebsiteData.Clear.Button" = "Obriši sve podatke web lokacije"; @@ -1240,6 +1249,9 @@ /* This is the description below the settings section located in the menu under customize current homepage. It describes what the options in the section are for. */ "Settings.Home.Current.Description.v101" = "Odaberi što se prikazuje kao početna stranica."; +/* In the settings menu, on the Firefox homepage customization section, this is the description below the section, describing what the options in the section are for. */ +"Settings.Home.Option.Description.v101" = "Odaberi što je uključeno na početnoj Firefox stranici."; + /* In the settings menu, in the Firefox homepage customization section, this is the title for the option that allows users to toggle the Jump Back In section on homepage on or off */ "Settings.Home.Option.JumpBackIn" = "Vrati se natrag"; @@ -1276,6 +1288,27 @@ /* In the settings menu, on the Firefox homepage customization section, this is the title for the option that allows users to access the wallpaper settings for the application. */ "Settings.Home.Option.Wallpaper" = "Pozadinska slika"; +/* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the amethyst firefox wallpaper. */ +"Settings.Home.Option.Wallpaper.Accessibility.AmethystWallpaper.v99" = "Firefox pozadina, uzorak ametista."; + +/* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the beach hills firefox wallpaper. */ +"Settings.Home.Option.Wallpaper.Accessibility.BeachHillsWallpaper.v100" = "Firefox pozadina, uzorak brežuljaka na plaži."; + +/* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the cerulean firefox wallpaper. */ +"Settings.Home.Option.Wallpaper.Accessibility.CeruleanWallpaper.v99" = "Firefox pozadina, nebesko plavi uzorak."; + +/* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string for the default wallpaper. */ +"Settings.Home.Option.Wallpaper.Accessibility.DefaultWallpaper.v99" = "Standardna pozadina."; + +/* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the sunrise firefox wallpaper. */ +"Settings.Home.Option.Wallpaper.Accessibility.SunriseWallpaper.v99" = "Firefox pozadina, uzorak izlaska sunca."; + +/* In the settings menu, on the Firefox wallpaper customization screen, this is the accessibility string of the toggle for turning wallpaper cycling shortcut on or off on the homepage. */ +"Settings.Home.Option.Wallpaper.Accessibility.ToggleButton" = "Prebacivanje pozadine početne stranice"; + +/* In the settings menu, on the Firefox wallpaper customization screen, this is the title accessibility string for the twilight hills firefox wallpaper. */ +"Settings.Home.Option.Wallpaper.Accessibility.TwilightHillsWallpaper.v100" = "Firefox pozadina, uzorak brda u sumraku."; + /* In the settings menu, on the Firefox wallpaper customization screen, this is the title of the group of wallpapers that are always available to the user. The %@ will be replaced by the app name and thus doesn't need translation. */ "Settings.Home.Option.Wallpaper.Classic.Title.v106" = "Klasični %@"; @@ -1372,6 +1405,9 @@ /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Status" = "Prilikom otvaranja Firefoxa"; +/* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ +"Settings.OfferClipboardBar.Status.v128" = "Prilikom otvaranja %@"; + /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.StatusV2" = "Prilikom otvaranja Firefoxa"; @@ -1387,6 +1423,9 @@ /* Touch ID prompt subtitle when accessing logins and passwords */ "Settings.Passwords.FingerPrintReason.v103" = "Koristi otisak prsta za pristup lozinkama."; +/* Message shown when you enter Passwords screen for the first time. It explains how password are protected in the Firefox for iOS application. */ +"Settings.Passwords.OnboardingMessage.v103" = "Tvoje su lozinke sada zaštićene Face ID-om, Touch ID-om ili šifrom uređaja."; + /* Setting that appears in the Passwords screen to enable the built-in password manager so users can save their passwords. */ "Settings.Passwords.SavePasswords.v103" = "Spremi lozinke"; @@ -1441,6 +1480,9 @@ /* Label used as a toggle item in Settings. When this is off, the user is opting out of all studies. */ "Settings.Studies.Toggle.Title" = "Studije"; +/* Ddescription that appears in the settings screen to explain what Firefox Sync is useful for. */ +"Settings.Sync.ButtonDescription.v103" = "Prijavi se za sinkroniziranje kartica, zabilješki, lozinki i više."; + /* Button label that appears in the settings to prompt the user to sign in to Firefox for iOS sync service to sync and save data. */ "Settings.Sync.ButtonTitle.v103" = "Sinkroniziraj i spremi podatke"; @@ -1450,6 +1492,9 @@ /* This is the description for the setting that toggles the Inactive Tabs feature in the settings menu under the Tabs customization section. Inactive tabs are a separate section of tabs that appears in the Tab Tray, which can be enabled or not */ "Settings.Tabs.CustomizeTabsSection.InactiveTabs" = "Neaktivne kartice"; +/* This is the description for the setting that toggles the Inactive Tabs feature in the settings menu under the Tabs customization section. Inactive tabs are a separate section of tabs that appears in the Tab Tray, which can be enabled or not */ +"Settings.Tabs.CustomizeTabsSection.InactiveTabsDescription.v101" = "Kartice koje nisu pogledane u zadnja dva tjedna premještaju se u odjeljak neaktivnih kartica."; + /* In the settings menu, in the Tabs customization section, this is the title for the setting that toggles the Tab Groups feature - where tabs from related searches are grouped - on or off */ "Settings.Tabs.CustomizeTabsSection.TabGroups" = "Grupe kartica"; @@ -1471,6 +1516,9 @@ /* Dismiss button for the tracker protection alert. */ "Settings.TrackingProtection.Alert.Button" = "U redu, razumijem"; +/* Decription for the tracker protection alert. */ +"Settings.TrackingProtection.Alert.Description" = "Ako web stranica ne radi prema očekivanjima, dodirni lokot u adresnoj traci i isključi poboljšanu zaštitu od praćenja za tu stranicu."; + /* Title for the tracker protection alert. */ "Settings.TrackingProtection.Alert.Title" = "Oprez!"; @@ -1483,6 +1531,12 @@ /* Additional information about your Enhanced Tracking Protection */ "Settings.TrackingProtection.ProtectionCellFooter" = "Smanjuje broj ciljanih oglasa i pomaže spriječiti oglašivače da prate tvoje kretanje internetom."; +/* Footer information for tracker protection level. */ +"Settings.TrackingProtection.ProtectionLevel.Footer" = "Ako web stranica ne radi prema očekivanjima, dodirni lokot u adresnoj traci i isključi poboljšanu zaštitu od praćenja za tu stranicu."; + +/* Footer information for tracker protection level. */ +"Settings.TrackingProtection.ProtectionLevel.Footer.Lock" = "Ako web stranica ne radi prema očekivanjima, dodirni lokot u adresnoj traci i isključi poboljšanu zaštitu od praćenja za tu stranicu."; + /* Description for standard level tracker protection */ "Settings.TrackingProtection.ProtectionLevelStandard.Description" = "Dopušta neke programe za praćenje, kako bi web stranice mogle ispravno funkcionirati."; diff --git a/firefox-ios/Shared/hr.lproj/LoginManager.strings b/firefox-ios/Shared/hr.lproj/LoginManager.strings index 0735b5467161..49fc1bab3ac3 100644 --- a/firefox-ios/Shared/hr.lproj/LoginManager.strings +++ b/firefox-ios/Shared/hr.lproj/LoginManager.strings @@ -16,6 +16,21 @@ /* Label for the button used to delete the current login. */ "Delete" = "Izbriši"; +/* Prompt option for cancelling out of deletion */ +"DeleteLoginAlert.DeleteButton.Cancel.v122" = "Odustani"; + +/* Label for the button used to delete the current login. */ +"DeleteLoginAlert.DeleteButton.Title.v122" = "Ukloni"; + +/* Prompt message warning the user that deleting non-synced logins will permanently remove them, when they attempt to do so */ +"DeleteLoginAlert.Message.Local.v122" = "Ovu radnju ne možeš poništiti."; + +/* Prompt message warning the user that deleted logins will remove logins from all connected devices */ +"DeleteLoginAlert.Message.Synced.v122" = "Ovo će ukloniti lozinku sa svih tvojih sinkroniziranih uređaja."; + +/* Title for the prompt that appears when the user deletes a login. */ +"DeleteLoginsAlert.Title.v122" = "Ukloniti lozinku?"; + /* Label for the button used to deselect all logins. */ "Deselect All" = "Poništi odabir"; @@ -37,6 +52,9 @@ /* Label displayed when no logins are found after searching. */ "No logins found" = "Nema pronađenih prijava"; +/* Label displayed when no logins are found after searching. */ +"NoLoginsFound.Title.v122" = "Nije pronađena nijedna lozinka"; + /* Open and Fill website text selection menu item */ "Open & Fill" = "Otvori i ispuni"; diff --git a/firefox-ios/Shared/hr.lproj/Menu.strings b/firefox-ios/Shared/hr.lproj/Menu.strings index 5640dd7951c9..b001ad1d153d 100644 --- a/firefox-ios/Shared/hr.lproj/Menu.strings +++ b/firefox-ios/Shared/hr.lproj/Menu.strings @@ -4,6 +4,12 @@ /* Label for the button, displayed in the menu, used to copy the current page link to the clipboard. */ "Menu.CopyLink.Title" = "Kopiraj poveznicu"; +/* Toast displayed to user after downlaod pdf was pressed. */ +"Menu.DownloadPDF.Confirm.v129" = "Uspješno preuzet PDF"; + +/* Label for the button, displayed in the menu, downloads a pdf when pressed. */ +"Menu.DownloadPDF.Label.v129" = "Preuzmi PDF"; + /* Label for the button, displayed in the menu, used to open the toolbar to search for text within the current page. */ "Menu.FindInPageAction.Title" = "Pronađi na stranici"; diff --git a/firefox-ios/Shared/hr.lproj/Search.strings b/firefox-ios/Shared/hr.lproj/Search.strings index bb70d75ed171..6686f1632be9 100644 --- a/firefox-ios/Shared/hr.lproj/Search.strings +++ b/firefox-ios/Shared/hr.lproj/Search.strings @@ -4,3 +4,6 @@ /* Label for search settings button. */ "Search Settings" = "Postavke pretraživanja"; +/* When making a new search from the awesome bar, suggestions appear to the user as they write new letters in their search. Different types of suggestions can appear. This string will be used as a label for sponsored Firefox suggestions. */ +"Search.SponsoredSuggestionDescription.v119" = "Sponzorirano"; + diff --git a/firefox-ios/Shared/id.lproj/Localizable.strings b/firefox-ios/Shared/id.lproj/Localizable.strings index cf1245536e23..e8c475060a38 100644 --- a/firefox-ios/Shared/id.lproj/Localizable.strings +++ b/firefox-ios/Shared/id.lproj/Localizable.strings @@ -166,12 +166,18 @@ /* The label text in the Button Toast for switching to a fresh New Private Tab. */ "ContextMenu.ButtonToast.NewPrivateTabOpened.LabelText" = "Tab Pribadi Baru dibuka"; +/* The label text in the Button Toast for switching to a fresh New Private Tab. */ +"ContextMenu.ButtonToast.NewPrivateTabOpened.LabelText.v113" = "Tab Pribadi Baru Dibuka"; + /* The button text in the Button Toast for switching to a fresh New Tab. */ "ContextMenu.ButtonToast.NewTabOpened.ButtonText" = "Ganti"; /* The label text in the Button Toast for switching to a fresh New Tab. */ "ContextMenu.ButtonToast.NewTabOpened.LabelText" = "Tab Baru dibuka"; +/* The label text in the Button Toast for switching to a fresh New Tab. */ +"ContextMenu.ButtonToast.NewTabOpened.LabelText.v114" = "Tab Baru Dibuka"; + /* Context menu item for copying an image to the clipboard */ "ContextMenu.CopyImageButtonTitle" = "Salin Gambar"; @@ -343,6 +349,9 @@ /* When a user taps and holds on an item from the Recently Visited section, this label will appear indicating the option to remove that item. */ "FirefoxHome.RecentHistory.Remove" = "Hapus"; +/* Label showing how many pages there is in a search group. %d represents a number */ +"FirefoxHomepage.Common.PagesCount.v112" = "Halaman: %d"; + /* The title for the Settings context menu action for sponsored tiles in the Firefox home page shortcuts section. Clicking this brings the users to the Shortcuts Settings. */ "FirefoxHomepage.ContextualMenu.Settings.v101" = "Pengaturan"; @@ -748,6 +757,9 @@ /* Label for the button, displayed in the menu, takes you to History screen when pressed. */ "Menu.History.Label" = "Riwayat"; +/* Label for the new private tab button in the menu page. Pressing this button opens a new private tab. */ +"Menu.NewPrivateTab.Label" = "Tab Pribadi Baru"; + /* Label for the new tab button in the menu page. Pressing this button opens a new tab. */ "Menu.NewTab.v99" = "Tab Baru"; @@ -847,6 +859,9 @@ /* The title for the option to view the What's new page. */ "Menu.WhatsNew.Title" = "Kabar Terbaru"; +/* Label for the zoom page button in the menu, used to show the Zoom Page bar. The placeholder shows the current zoom level in percent. */ +"Menu.ZoomPage.Title.v113" = "Perbesar (%@)"; + /* Accessibility label for Mobile Device image in remote tabs list */ "mobile device" = "perangkat seluler"; @@ -1132,6 +1147,9 @@ /* The placeholder for URL Field when saving a custom search engine */ "Settings.AddCustomEngine.URLPlaceholder" = "URL (Ganti Kueri dengan %s)"; +/* Title of setting to block opening external apps when pressing links. */ +"Settings.BlockOpeningExternalApps.Title" = "Blokir Pembukaan Aplikasi Eksternal"; + /* Button in Data Management that clears all items. */ "Settings.ClearAllWebsiteData.Clear.Button" = "Hapus Semua Data Situs Web"; @@ -1360,6 +1378,9 @@ /* Option in settings to show a blank page when you open a new tab */ "Settings.NewTab.Option.BlankPage" = "Laman Kosong"; +/* Option in settings to show your homepage when you open a new tab */ +"Settings.NewTab.Option.Custom" = "Ubahsuai"; + /* Option in settings to show Firefox Home when you open a new tab */ "Settings.NewTab.Option.FirefoxHome" = "Firefox Home"; @@ -1384,6 +1405,9 @@ /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Status" = "Ketika Membuka Firefox"; +/* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ +"Settings.OfferClipboardBar.Status.v128" = "Saat membuka %@"; + /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.StatusV2" = "Ketika Membuka Firefox"; @@ -1492,6 +1516,9 @@ /* Dismiss button for the tracker protection alert. */ "Settings.TrackingProtection.Alert.Button" = "Oke, paham!"; +/* Decription for the tracker protection alert. */ +"Settings.TrackingProtection.Alert.Description" = "Jika situs tidak berfungsi seperti yang diharapkan, ketuk kunci di bilah alamat dan matikan Perlindungan Pelacakan yang Ditingkatkan untuk laman itu."; + /* Title for the tracker protection alert. */ "Settings.TrackingProtection.Alert.Title" = "Perhatian!"; @@ -1504,6 +1531,12 @@ /* Additional information about your Enhanced Tracking Protection */ "Settings.TrackingProtection.ProtectionCellFooter" = "Mengurangi iklan yang ditargetkan dan membantu menghentikan pengiklan melacak penjelajahan Anda."; +/* Footer information for tracker protection level. */ +"Settings.TrackingProtection.ProtectionLevel.Footer" = "Jika situs tidak berfungsi seperti yang diharapkan, ketuk kunci di bilah alamat dan matikan Perlindungan Pelacakan yang Ditingkatkan untuk laman itu."; + +/* Footer information for tracker protection level. */ +"Settings.TrackingProtection.ProtectionLevel.Footer.Lock" = "Jika situs tidak berfungsi seperti yang diharapkan, ketuk kunci di bilah alamat dan matikan Perlindungan Pelacakan yang Ditingkatkan untuk laman itu."; + /* Description for standard level tracker protection */ "Settings.TrackingProtection.ProtectionLevelStandard.Description" = "Mengizinkan beberapa pelacak agar situs web berfungsi dengan baik."; @@ -1519,6 +1552,9 @@ /* Tracking protection settings option for using the basic blocklist. */ "Settings.TrackingProtectionOption.BasicBlockList" = "Standar (baku)"; +/* Tracking protection settings status showing the current option selected. */ +"Settings.TrackingProtectionOption.BasicBlockList.Status" = "Standar"; + /* Tracking protection settings option for using the strict blocklist. */ "Settings.TrackingProtectionOption.BlockListStrict" = "Ketat"; diff --git a/firefox-ios/Shared/id.lproj/LoginManager.strings b/firefox-ios/Shared/id.lproj/LoginManager.strings index 53dd45217662..1b7957a9aca6 100644 --- a/firefox-ios/Shared/id.lproj/LoginManager.strings +++ b/firefox-ios/Shared/id.lproj/LoginManager.strings @@ -16,6 +16,21 @@ /* Label for the button used to delete the current login. */ "Delete" = "Hapus"; +/* Prompt option for cancelling out of deletion */ +"DeleteLoginAlert.DeleteButton.Cancel.v122" = "Batal"; + +/* Label for the button used to delete the current login. */ +"DeleteLoginAlert.DeleteButton.Title.v122" = "Hapus"; + +/* Prompt message warning the user that deleting non-synced logins will permanently remove them, when they attempt to do so */ +"DeleteLoginAlert.Message.Local.v122" = "Anda tidak dapat membatalkan tindakan ini."; + +/* Prompt message warning the user that deleted logins will remove logins from all connected devices */ +"DeleteLoginAlert.Message.Synced.v122" = "Ini akan menghapus kata sandi dari semua perangkat Anda yang disinkronkan."; + +/* Title for the prompt that appears when the user deletes a login. */ +"DeleteLoginsAlert.Title.v122" = "Hapus Kata Sandi?"; + /* Label for the button used to deselect all logins. */ "Deselect All" = "Batalkan semua pilihan"; diff --git a/firefox-ios/Shared/nb.lproj/Localizable.strings b/firefox-ios/Shared/nb.lproj/Localizable.strings index 458c40cc4a61..728aae9c8536 100644 --- a/firefox-ios/Shared/nb.lproj/Localizable.strings +++ b/firefox-ios/Shared/nb.lproj/Localizable.strings @@ -757,6 +757,9 @@ /* Label for the button, displayed in the menu, takes you to History screen when pressed. */ "Menu.History.Label" = "Historikk"; +/* Label for the new private tab button in the menu page. Pressing this button opens a new private tab. */ +"Menu.NewPrivateTab.Label" = "Ny privat fane"; + /* Label for the new tab button in the menu page. Pressing this button opens a new tab. */ "Menu.NewTab.v99" = "Ny fane"; diff --git a/firefox-ios/Shared/nb.lproj/Menu.strings b/firefox-ios/Shared/nb.lproj/Menu.strings index 03521ff634f9..75a16cb73ffb 100644 --- a/firefox-ios/Shared/nb.lproj/Menu.strings +++ b/firefox-ios/Shared/nb.lproj/Menu.strings @@ -4,6 +4,12 @@ /* Label for the button, displayed in the menu, used to copy the current page link to the clipboard. */ "Menu.CopyLink.Title" = "Kopier lenke"; +/* Toast displayed to user after downlaod pdf was pressed. */ +"Menu.DownloadPDF.Confirm.v129" = "PDF er lastet ned"; + +/* Label for the button, displayed in the menu, downloads a pdf when pressed. */ +"Menu.DownloadPDF.Label.v129" = "Last ned PDF"; + /* Label for the button, displayed in the menu, used to open the toolbar to search for text within the current page. */ "Menu.FindInPageAction.Title" = "Søk på siden"; diff --git a/firefox-ios/Shared/si.lproj/Localizable.strings b/firefox-ios/Shared/si.lproj/Localizable.strings index 24dd3ec49781..ed2f75e52032 100644 --- a/firefox-ios/Shared/si.lproj/Localizable.strings +++ b/firefox-ios/Shared/si.lproj/Localizable.strings @@ -5,10 +5,10 @@ "+" = "+"; /* Authentication prompt message with no realm. Parameter is the hostname of the site */ -"A username and password are being requested by %@." = "%@ වෙතින් පරිශීලක නාමයක් හා මුරපදයක් ඉල්ලා සිටියි."; +"A username and password are being requested by %@." = "%@ වෙතින් පරිශ්‍රීලක නාමයක් හා මුරපදයක් ඉල්ලා සිටියි."; /* Authentication prompt message with a realm. First parameter is the hostname. Second is the realm string */ -"A username and password are being requested by %@. The site says: %@" = "%1$@ විසින් පරිශීලක නාමයක් සහ මුරපදයක් ඉල්ලා සිටියි. අඩවිය පවසන්නේ: %2$@"; +"A username and password are being requested by %@. The site says: %@" = "%1$@ විසින් පරිශ්‍රීලක නාමයක් සහ මුරපදයක් ඉල්ලා සිටියි. අඩවිය පවසන්නේ: %2$@"; /* Button for reader mode font size. Keep this extremely short! This is shown in the reader mode toolbar. */ "Aa" = "Aa"; @@ -1748,7 +1748,7 @@ "Use stage servers" = "අදියර සේවාදායක භාවිතය"; /* Username textbox in Authentication prompt */ -"Username" = "පරිශීලක නාමය"; +"Username" = "පරිශ්‍රීලක නාමය"; /* The message of the error displayed to a user when they trying to change a wallpaper failed. */ "Wallpaper.Change.Error.Body.v106" = "මෙම බිතුපතෙහි යම් දෙයක් වැරදී ඇත."; diff --git a/firefox-ios/Shared/si.lproj/LoginManager.strings b/firefox-ios/Shared/si.lproj/LoginManager.strings index 02eca742c103..9dd804cb1d58 100644 --- a/firefox-ios/Shared/si.lproj/LoginManager.strings +++ b/firefox-ios/Shared/si.lproj/LoginManager.strings @@ -77,7 +77,7 @@ "Select All" = "සියල්ල තෝරන්න"; /* Label displayed above the username row in Login Detail View. */ -"Username" = "පරිශීලක නාමය"; +"Username" = "පරිශ්‍රීලක නාමය"; /* Label displayed above the website row in Login Detail View. */ "Website" = "අඩවිය"; diff --git a/firefox-ios/Shared/th.lproj/Localizable.strings b/firefox-ios/Shared/th.lproj/Localizable.strings index 3d96ed696490..45b086f7e49a 100644 --- a/firefox-ios/Shared/th.lproj/Localizable.strings +++ b/firefox-ios/Shared/th.lproj/Localizable.strings @@ -757,6 +757,9 @@ /* Label for the button, displayed in the menu, takes you to History screen when pressed. */ "Menu.History.Label" = "ประวัติ"; +/* Label for the new private tab button in the menu page. Pressing this button opens a new private tab. */ +"Menu.NewPrivateTab.Label" = "แท็บส่วนตัวใหม่"; + /* Label for the new tab button in the menu page. Pressing this button opens a new tab. */ "Menu.NewTab.v99" = "แท็บใหม่"; @@ -1144,6 +1147,9 @@ /* The placeholder for URL Field when saving a custom search engine */ "Settings.AddCustomEngine.URLPlaceholder" = "URL (แทนที่คำสืบค้นด้วย %s)"; +/* Title of setting to block opening external apps when pressing links. */ +"Settings.BlockOpeningExternalApps.Title" = "ปิดกั้นการเปิดแอปภายนอก"; + /* Button in Data Management that clears all items. */ "Settings.ClearAllWebsiteData.Clear.Button" = "ล้างข้อมูลเว็บไซต์ทั้งหมด"; @@ -1399,6 +1405,9 @@ /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Status" = "เมื่อเปิด Firefox"; +/* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ +"Settings.OfferClipboardBar.Status.v128" = "เมื่อเปิด %@"; + /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.StatusV2" = "เมื่อเปิด Firefox"; diff --git a/firefox-ios/Shared/th.lproj/Menu.strings b/firefox-ios/Shared/th.lproj/Menu.strings index 495bff422c86..36a9018048e7 100644 --- a/firefox-ios/Shared/th.lproj/Menu.strings +++ b/firefox-ios/Shared/th.lproj/Menu.strings @@ -4,6 +4,12 @@ /* Label for the button, displayed in the menu, used to copy the current page link to the clipboard. */ "Menu.CopyLink.Title" = "คัดลอกลิงก์"; +/* Toast displayed to user after downlaod pdf was pressed. */ +"Menu.DownloadPDF.Confirm.v129" = "ดาวน์โหลดไฟล์ PDF สำเร็จแล้ว"; + +/* Label for the button, displayed in the menu, downloads a pdf when pressed. */ +"Menu.DownloadPDF.Label.v129" = "ดาวน์โหลด PDF"; + /* Label for the button, displayed in the menu, used to open the toolbar to search for text within the current page. */ "Menu.FindInPageAction.Title" = "ค้นหาในหน้า"; diff --git a/firefox-ios/Shared/uk.lproj/Localizable.strings b/firefox-ios/Shared/uk.lproj/Localizable.strings index b7ef1b2d2b10..5cc610a6fd2c 100644 --- a/firefox-ios/Shared/uk.lproj/Localizable.strings +++ b/firefox-ios/Shared/uk.lproj/Localizable.strings @@ -757,6 +757,9 @@ /* Label for the button, displayed in the menu, takes you to History screen when pressed. */ "Menu.History.Label" = "Історія"; +/* Label for the new private tab button in the menu page. Pressing this button opens a new private tab. */ +"Menu.NewPrivateTab.Label" = "Нова приватна вкладка"; + /* Label for the new tab button in the menu page. Pressing this button opens a new tab. */ "Menu.NewTab.v99" = "Нова вкладка"; @@ -1144,6 +1147,9 @@ /* The placeholder for URL Field when saving a custom search engine */ "Settings.AddCustomEngine.URLPlaceholder" = "Адреса URL (%s заміниться на запит)"; +/* Title of setting to block opening external apps when pressing links. */ +"Settings.BlockOpeningExternalApps.Title" = "Блокувати відкриття зовнішніх програм"; + /* Button in Data Management that clears all items. */ "Settings.ClearAllWebsiteData.Clear.Button" = "Стерти всі дані вебсайтів"; @@ -1399,6 +1405,9 @@ /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.Status" = "При запуску Firefox"; +/* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349. Placeholder is for the app name. */ +"Settings.OfferClipboardBar.Status.v128" = "Під час відкриття %@"; + /* Description displayed under the ”Offer to Open Copied Link” option. See https://bug1223660.bmoattachments.org/attachment.cgi?id=8898349 */ "Settings.OfferClipboardBar.StatusV2" = "При запуску Firefox"; diff --git a/firefox-ios/Shared/uk.lproj/Menu.strings b/firefox-ios/Shared/uk.lproj/Menu.strings index 80b4ab379ef6..c6e4a4923d86 100644 --- a/firefox-ios/Shared/uk.lproj/Menu.strings +++ b/firefox-ios/Shared/uk.lproj/Menu.strings @@ -4,6 +4,12 @@ /* Label for the button, displayed in the menu, used to copy the current page link to the clipboard. */ "Menu.CopyLink.Title" = "Копіювати посилання"; +/* Toast displayed to user after downlaod pdf was pressed. */ +"Menu.DownloadPDF.Confirm.v129" = "Успішно завантажено PDF"; + +/* Label for the button, displayed in the menu, downloads a pdf when pressed. */ +"Menu.DownloadPDF.Label.v129" = "Завантажити PDF"; + /* Label for the button, displayed in the menu, used to open the toolbar to search for text within the current page. */ "Menu.FindInPageAction.Title" = "Знайти на сторінці"; diff --git a/firefox-ios/Shared/zh-CN.lproj/PrivateBrowsing.strings b/firefox-ios/Shared/zh-CN.lproj/PrivateBrowsing.strings index 05f6dcdd71f8..5a0a50afab88 100644 --- a/firefox-ios/Shared/zh-CN.lproj/PrivateBrowsing.strings +++ b/firefox-ios/Shared/zh-CN.lproj/PrivateBrowsing.strings @@ -8,7 +8,7 @@ "Firefox won’t remember any of your history or cookies, but new bookmarks will be saved." = "Firefox 不会记住您的历史记录和 Cookie,但新的书签将被保留。"; /* Text button displayed when there are no tabs open while in private mode */ -"Learn More" = "了解更多"; +"Learn More" = "详细了解"; /* Toggled OFF accessibility value */ "Off" = "关"; diff --git a/firefox-ios/Storage/DefaultSuggestedSites.swift b/firefox-ios/Storage/DefaultSuggestedSites.swift index e42218c454ca..c7746469f579 100644 --- a/firefox-ios/Storage/DefaultSuggestedSites.swift +++ b/firefox-ios/Storage/DefaultSuggestedSites.swift @@ -49,7 +49,8 @@ open class DefaultSuggestedSites { ), SuggestedSiteData( url: "https://www.amazon.com/", - // FIXME Need the correct favicon URL... grabbed this one from the contile services link + // NOTE: Amazon does not host a high quality favicon. We are falling back to the one hosted in our + // ContileProvider.contileProdResourceEndpoint (https://ads.mozilla.org/v1/tiles). faviconUrl: "https://tiles-cdn.prod.ads.prod.webservices.mozgcp.net/CAP5k4gWqcBGwir7bEEmBWveLMtvldFu-y_kyO3txFA=.9991.jpg", trackingId: 630, title: .DefaultSuggestedAmazon @@ -61,16 +62,16 @@ open class DefaultSuggestedSites { title: .DefaultSuggestedWikipedia ), SuggestedSiteData( - url: "https://x.com/", // TODO Update Twitter to X + url: "https://x.com/", faviconUrl: "https://abs.twimg.com/responsive-web/client-web/icon-ios.77d25eba.png", trackingId: 628, - title: .DefaultSuggestedTwitter + title: .DefaultSuggestedX ) ], "zh_CN": [ // FIXME Do we still want this as a special case localization? Android doesn't compile this vers. anymore SuggestedSiteData( url: "http://mozilla.com.cn", - faviconUrl: "asset://mozChinaLogo", // FIXME We need a favicon link + faviconUrl: "http://mozilla.com.cn/favicon.ico", // FIXME We need a higher quality favicon link trackingId: 700, title: "火狐社区" ), @@ -105,7 +106,7 @@ open class DefaultSuggestedSites { QIHUV9bCUkQAF8LGFoRXgcAXVttOEsSMyRmGmsXXAcAXFdaAEwVM28PH10TVAMHVVpbDE8\ nBG8BKydLFl5fCQ5eCUsSM184GGsSXQ8WUiwcWl8RcV84G1slXTZdEAMAOEkWAmsBK2s """, - faviconUrl: "asset://jdLogo", // FIXME We need a favicon link + faviconUrl: "https://corporate.jd.com/favicon.ico", // FIXME We need a higher quality favicon link trackingId: 705, title: "京东" ) diff --git a/firefox-ios/Storage/Rust/RustFirefoxSuggest.swift b/firefox-ios/Storage/Rust/RustFirefoxSuggest.swift index 1821653fd11f..931998177e50 100644 --- a/firefox-ios/Storage/Rust/RustFirefoxSuggest.swift +++ b/firefox-ios/Storage/Rust/RustFirefoxSuggest.swift @@ -59,7 +59,7 @@ public class RustFirefoxSuggest: RustFirefoxSuggestProtocol { try await withCheckedThrowingContinuation { continuation in writerQueue.async(qos: .utility) { do { - try self.store.ingest(constraints: SuggestIngestionConstraints()) + _ = try self.store.ingest(constraints: SuggestIngestionConstraints()) continuation.resume() } catch { continuation.resume(throwing: error) diff --git a/firefox-ios/WidgetKit/Assets.xcassets/privateModeLarge.imageset/Contents.json b/firefox-ios/WidgetKit/Assets.xcassets/privateModeLarge.imageset/Contents.json index 570a48042855..64b21449c03b 100644 --- a/firefox-ios/WidgetKit/Assets.xcassets/privateModeLarge.imageset/Contents.json +++ b/firefox-ios/WidgetKit/Assets.xcassets/privateModeLarge.imageset/Contents.json @@ -10,6 +10,7 @@ "version" : 1 }, "properties" : { - "preserves-vector-representation" : true + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" } } diff --git a/firefox-ios/WidgetKit/ImageButtonWithLabel.swift b/firefox-ios/WidgetKit/ImageButtonWithLabel.swift index 46b447261db6..8906e09683d8 100644 --- a/firefox-ios/WidgetKit/ImageButtonWithLabel.swift +++ b/firefox-ios/WidgetKit/ImageButtonWithLabel.swift @@ -4,6 +4,7 @@ #if canImport(WidgetKit) import SwiftUI +import Common // View for Quick Action Widget Buttons (Small & Medium) // +-------------------------------------------------------+ @@ -90,11 +91,11 @@ struct ImageButtonWithLabel: View { } Spacer() if link == .search && isSmall { - Image("searchLarge") + Image(decorative: StandardImageIdentifiers.Large.search) .scaledToFit() .frame(height: 24.0) } else { - Image(link.imageName) + Image(decorative: link.imageName) .scaledToFit() .frame(height: 24.0) } @@ -102,7 +103,7 @@ struct ImageButtonWithLabel: View { if isSmall { HStack(alignment: .bottom) { Spacer() - Image("faviconFox") + Image(decorative: "faviconFox") .scaledToFit() .frame(height: 24.0) } diff --git a/firefox-ios/WidgetKit/Info.plist b/firefox-ios/WidgetKit/Info.plist index 0dfb99b82ddc..1e0bf29516de 100644 --- a/firefox-ios/WidgetKit/Info.plist +++ b/firefox-ios/WidgetKit/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType $(PRODUCT_BUNDLE_PACKAGE_TYPE) CFBundleShortVersionString - 130.0 + 131.0 CFBundleVersion 1 MozDevelopmentTeam diff --git a/firefox-ios/WidgetKit/OpenTabs/OpenTabsWidget.swift b/firefox-ios/WidgetKit/OpenTabs/OpenTabsWidget.swift index 3171e78439f6..0ba727176fca 100644 --- a/firefox-ios/WidgetKit/OpenTabs/OpenTabsWidget.swift +++ b/firefox-ios/WidgetKit/OpenTabs/OpenTabsWidget.swift @@ -6,6 +6,7 @@ import SwiftUI import WidgetKit import UIKit import Combine +import Common struct OpenTabsWidget: Widget { private let kind: String = "Quick View" @@ -36,7 +37,7 @@ struct OpenTabsView: View { if entry.favicons[tab.imageKey] != nil { (entry.favicons[tab.imageKey])!.resizable().frame(width: 16, height: 16) } else { - Image("globeLarge") + Image(decorative: StandardImageIdentifiers.Large.globe) .foregroundColor(Color.white) .frame(width: 16, height: 16) } @@ -58,7 +59,7 @@ struct OpenTabsView: View { var openFirefoxButton: some View { HStack(alignment: .center, spacing: 15) { - Image("externalLinkSmall").foregroundColor(Color.white) + Image(decorative: StandardImageIdentifiers.Small.externalLink).foregroundColor(Color.white) Text("Open Firefox") .foregroundColor(Color.white).lineLimit(1) .font(.system(size: 13, weight: .semibold, design: .default)) @@ -81,7 +82,7 @@ struct OpenTabsView: View { Text(String.NoOpenTabsLabel) HStack { Spacer() - Image("externalLinkSmall") + Image(decorative: StandardImageIdentifiers.Small.externalLink) Text(String.OpenFirefoxLabel) .foregroundColor(Color.white).lineLimit(1) .font(.system(size: 13, weight: .semibold, design: .default)) @@ -96,7 +97,9 @@ struct OpenTabsView: View { if entry.tabs.count > numberOfTabsToDisplay { HStack(alignment: .center, spacing: 15) { - Image("externalLinkSmall").foregroundColor(Color.white).frame(width: 16, height: 16) + Image(decorative: StandardImageIdentifiers.Small.externalLink) + .foregroundColor(Color.white) + .frame(width: 16, height: 16) Text( String.localizedStringWithFormat( String.MoreTabsLabel, diff --git a/firefox-ios/WidgetKit/QuickLink.swift b/firefox-ios/WidgetKit/QuickLink.swift index ba5ef8a8cc3d..c513707c545f 100644 --- a/firefox-ios/WidgetKit/QuickLink.swift +++ b/firefox-ios/WidgetKit/QuickLink.swift @@ -3,6 +3,7 @@ // file, You can obtain one at http://mozilla.org/MPL/2.0/ import SwiftUI +import Common /// Enum file that holds the different cases for the Quick Actions small widget with their /// configurations (string, backgrounds, images) as selected by the user in edit mode. @@ -17,11 +18,11 @@ enum QuickLink: Int { case .search: return "faviconFox" case .privateSearch: - return "privateModeLarge" + return StandardImageIdentifiers.Large.privateMode case .copiedLink: - return "tabTrayLarge" + return StandardImageIdentifiers.Large.tabTray case .closePrivateTabs: - return "deleteLarge" + return StandardImageIdentifiers.Large.delete } } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Application/AppSendTabDelegateTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Application/AppSendTabDelegateTests.swift index 5e70bbd32745..653ac2a74801 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Application/AppSendTabDelegateTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Application/AppSendTabDelegateTests.swift @@ -5,7 +5,7 @@ import XCTest @testable import Client -final class AppSendTabDelegateTests: XCTestCase { +final class AppFxACommandsTests: XCTestCase { private var applicationStateProvider: MockApplicationStateProvider! private var applicationHelper: MockApplicationHelper! @@ -57,12 +57,39 @@ final class AppSendTabDelegateTests: XCTestCase { XCTAssertEqual(applicationHelper.openURLCalled, 3) } + // MARK: - Close Remote Tabs Tests + func testCloseSendTabs_activeWithOneURL_callsDeeplink() async { + let url = URL(string: "https://mozilla.com", invalidCharacters: false)! + let subject = createSubject() + let expectation = XCTestExpectation(description: "Close tabs called") + subject.closeTabs(for: [url]) + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + expectation.fulfill() + XCTAssertEqual(self.applicationHelper.closeTabsCalled, 1) + } + await fulfillment(of: [expectation]) + } + + func testCloseSendTabs_activeWithMultipleURLs_callsDeeplink() async { + let url1 = URL(string: "https://example.com", invalidCharacters: false)! + let url2 = URL(string: "https://example.com/1", invalidCharacters: false)! + let url3 = URL(string: "https://example.com/2", invalidCharacters: false)! + let subject = createSubject() + let expectation = XCTestExpectation(description: "Close tabs called multiple times") + subject.closeTabs(for: [url1, url2, url3]) + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + expectation.fulfill() + XCTAssertEqual(self.applicationHelper.closeTabsCalled, 1) + } + await fulfillment(of: [expectation]) + } + // MARK: - Helper methods - func createSubject() -> AppSendTabDelegate { - let subject = AppSendTabDelegate(app: applicationStateProvider, - applicationHelper: applicationHelper, - mainQueue: MockDispatchQueue()) + func createSubject() -> AppFxACommandsDelegate { + let subject = AppFxACommandsDelegate(app: applicationStateProvider, + applicationHelper: applicationHelper, + mainQueue: MockDispatchQueue()) trackForMemoryLeaks(subject) return subject } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Autofill/AddressLocaleFeatureValidatorTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Autofill/AddressLocaleFeatureValidatorTests.swift new file mode 100644 index 000000000000..48f06a0a37d3 --- /dev/null +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Autofill/AddressLocaleFeatureValidatorTests.swift @@ -0,0 +1,43 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import XCTest +import Common + +@testable import Client + +class AddressLocaleFeatureValidatorTests: XCTestCase { + func testValidRegionCA() { + let locale = Locale(identifier: "en_CA") + XCTAssertTrue( + AddressLocaleFeatureValidator.isValidRegion(locale: locale), + "Region valid for CA" + ) + } + + func testValidRegionUS() { + let locale = Locale(identifier: "en_US") + XCTAssertTrue( + AddressLocaleFeatureValidator.isValidRegion(locale: locale), + "Region valid for US" + ) + } + + func testInvalidRegionFR() { + let locale = Locale(identifier: "fr_FR") + XCTAssertFalse( + AddressLocaleFeatureValidator.isValidRegion(locale: locale), + "Invalid region for FR" + ) + } + + func testInvalidRegionWithoutRegionCode() { + let locale = Locale(identifier: "") + XCTAssertFalse( + AddressLocaleFeatureValidator.isValidRegion(locale: locale), + "Invalid region for locale without region code" + ) + } +} diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Autofill/FormAutofillHelperTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Autofill/FormAutofillHelperTests.swift index 79329d836039..0f65ebe38163 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Autofill/FormAutofillHelperTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Autofill/FormAutofillHelperTests.swift @@ -51,7 +51,7 @@ class FormAutofillHelperTests: XCTestCase { profile = MockProfile() DependencyHelperMock().bootstrapDependencies() LegacyFeatureFlagsManager.shared.initializeDeveloperFeatures(with: profile) - tab = Tab(profile: profile, configuration: WKWebViewConfiguration(), windowUUID: windowUUID) + tab = Tab(profile: profile, windowUUID: windowUUID) formAutofillHelper = FormAutofillHelper(tab: tab) secureWebviewMock = WKWebViewMock(URL(string: "https://foo.com")!) secureFrameMock = WKFrameInfoMock(webView: secureWebviewMock, frameURL: URL(string: "https://foo.com")!) @@ -281,10 +281,10 @@ class FormAutofillHelperTests: XCTestCase { } func test_formAutofillHelper_foundFieldValuesClosure_doesntLeak() { - let tab = Tab(profile: profile, configuration: WKWebViewConfiguration(), windowUUID: windowUUID) + let tab = Tab(profile: profile, windowUUID: windowUUID) let subject = FormAutofillHelper(tab: tab) trackForMemoryLeaks(subject) - tab.createWebview() + tab.createWebview(configuration: .init()) tab.addContentScript(subject, name: FormAutofillHelper.name()) subject.foundFieldValues = { fieldValues, type, frame in diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Autofill/Mocks/MockCreditCardProvider.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Autofill/Mocks/MockCreditCardProvider.swift new file mode 100644 index 000000000000..f958d6e03d89 --- /dev/null +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Autofill/Mocks/MockCreditCardProvider.swift @@ -0,0 +1,48 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import MozillaAppServices +import Storage + +class MockCreditCardProvider: CreditCardProvider { + var addCreditCardCalledCount = 0 + var updateCreditCardCalledCount = 0 + var listCreditCardsCalledCount = 0 + + private var exampleCreditCard = CreditCard( + guid: "1", + ccName: "Allen Burges", + ccNumberEnc: "4111111111111111", + ccNumberLast4: "1111", + ccExpMonth: 3, + ccExpYear: 2043, + ccType: "VISA", + timeCreated: 1234678, + timeLastUsed: nil, + timeLastModified: 123123, + timesUsed: 123123 + ) + + func addCreditCard( + creditCard: UnencryptedCreditCardFields, + completion: @escaping (CreditCard?, Error?) -> Void + ) { + addCreditCardCalledCount += 1 + completion(exampleCreditCard, nil) + } + func decryptCreditCardNumber(encryptedCCNum: String?) -> String? { return "testCCNum" } + func listCreditCards(completion: @escaping ([CreditCard]?, Error?) -> Void) { + listCreditCardsCalledCount += 1 + completion([exampleCreditCard], nil) + } + func updateCreditCard( + id: String, + creditCard: UnencryptedCreditCardFields, + completion: @escaping (Bool?, Error?) -> Void + ) { + updateCreditCardCalledCount += 1 + completion(nil, nil) + } +} diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Autofill/Mocks/MockLoginProvider.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Autofill/Mocks/MockLoginProvider.swift new file mode 100644 index 000000000000..f18c00a99b5f --- /dev/null +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Autofill/Mocks/MockLoginProvider.swift @@ -0,0 +1,36 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import MozillaAppServices + +class MockLoginProvider: LoginProvider { + var searchLoginsWithQueryCalledCount = 0 + var addLoginCalledCount = 0 + func searchLoginsWithQuery( + _ query: String?, + completionHandler: @escaping ( + Result< + [MozillaAppServices.EncryptedLogin], + any Error + > + ) -> Void + ) { + searchLoginsWithQueryCalledCount += 1 + completionHandler(.success([])) + } + + func addLogin( + login: MozillaAppServices.LoginEntry, + completionHandler: @escaping ( + Result< + MozillaAppServices.EncryptedLogin?, + any Error + > + ) -> Void + ) { + addLoginCalledCount += 1 + completionHandler(.success(nil)) + } +} diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/BrowserViewControllerStateTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/BrowserViewControllerStateTests.swift index b3d6d032bc85..cc19dec30224 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/BrowserViewControllerStateTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/BrowserViewControllerStateTests.swift @@ -30,6 +30,30 @@ final class BrowserViewControllerStateTests: XCTestCase { XCTAssertEqual(newState.navigateTo, .newTab) } + func testShowNewTabLongpPressActions() { + let initialState = createSubject() + let reducer = browserViewControllerReducer() + + XCTAssertNil(initialState.displayView) + + let action = getAction(for: .showNewTabLongPressActions) + let newState = reducer(initialState, action) + + XCTAssertEqual(newState.displayView, .newTabLongPressActions) + } + + func testClearDataAction() { + let initialState = createSubject() + let reducer = browserViewControllerReducer() + + XCTAssertNil(initialState.displayView) + + let action = getAction(for: .clearData) + let newState = reducer(initialState, action) + + XCTAssertEqual(newState.displayView, .dataClearance) + } + // MARK: - Private private func createSubject() -> BrowserViewControllerState { return BrowserViewControllerState(windowUUID: .XCTestDefaultUUID) diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/BrowserViewControllerTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/BrowserViewControllerTests.swift index b07bdc091e73..f8b0d36a26f8 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/BrowserViewControllerTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/BrowserViewControllerTests.swift @@ -20,7 +20,6 @@ class BrowserViewControllerTests: XCTestCase { DependencyHelperMock().bootstrapDependencies() TelemetryContextualIdentifier.setupContextId() Glean.shared.resetGlean(clearStores: true) - Glean.shared.enableTestingMode() profile = MockProfile() tabManager = MockTabManager() diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/ContentBlockerTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/ContentBlockerTests.swift index 14a4fd31e687..56f5e2c978e8 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/ContentBlockerTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/ContentBlockerTests.swift @@ -20,12 +20,10 @@ final class ContentBlockerTests: XCTestCase { } func testCompileListsNotInStore_callsCompletionHandlerSuccessfully() { - measure { - let expectation = XCTestExpectation() - ContentBlocker.shared.compileListsNotInStore { - expectation.fulfill() - } - wait(for: [expectation], timeout: 1.0) + let expectation = XCTestExpectation() + ContentBlocker.shared.compileListsNotInStore { + expectation.fulfill() } + wait(for: [expectation], timeout: 5.0) } } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/ContextMenuHelperTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/ContextMenuHelperTests.swift index a9cd79587382..0d266cd7ab14 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/ContextMenuHelperTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/ContextMenuHelperTests.swift @@ -20,7 +20,6 @@ class ContextMenuHelperTests: XCTestCase { DependencyHelperMock().bootstrapDependencies() Glean.shared.resetGlean(clearStores: true) - Glean.shared.enableTestingMode() } override func tearDown() { diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/BrowserCoordinatorTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/BrowserCoordinatorTests.swift index c7a39122693b..89fb38c9310d 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/BrowserCoordinatorTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Coordinators/BrowserCoordinatorTests.swift @@ -10,7 +10,7 @@ import XCTest @testable import Client -final class BrowserCoordinatorTests: XCTestCase { +final class BrowserCoordinatorTests: XCTestCase, FeatureFlaggable { private var mockRouter: MockRouter! private var profile: MockProfile! private var overlayModeManager: MockOverlayModeManager! @@ -200,7 +200,12 @@ final class BrowserCoordinatorTests: XCTestCase { XCTAssertEqual(subject.childCoordinators.count, 1) XCTAssertNotNil(subject.childCoordinators[0] as? EnhancedTrackingProtectionCoordinator) XCTAssertEqual(mockRouter.presentCalled, 1) - XCTAssertTrue(mockRouter.presentedViewController is EnhancedTrackingProtectionMenuVC) + + if featureFlags.isFeatureEnabled(.trackingProtectionRefactor, checking: .buildOnly) { + XCTAssertTrue(mockRouter.presentedViewController is TrackingProtectionViewController) + } else { + XCTAssertTrue(mockRouter.presentedViewController is EnhancedTrackingProtectionMenuVC) + } } func testShowShareExtension_addsShareExtensionCoordinator() { @@ -281,9 +286,9 @@ final class BrowserCoordinatorTests: XCTestCase { } func testShowBackForwardList_presentsBackForwardListViewController() { - let mockTab = Tab(profile: profile, configuration: .init(), windowUUID: windowUUID) + let mockTab = Tab(profile: profile, windowUUID: windowUUID) mockTab.url = URL(string: "https://www.google.com") - mockTab.createWebview() + mockTab.createWebview(configuration: .init()) tabManager.selectedTab = mockTab let subject = createSubject() diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/CreditCard/CreditCardBottomSheetViewModelTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/CreditCard/CreditCardBottomSheetViewModelTests.swift index da6626bfff7c..01cb5c8afbf8 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/CreditCard/CreditCardBottomSheetViewModelTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/CreditCard/CreditCardBottomSheetViewModelTests.swift @@ -13,9 +13,7 @@ import XCTest class CreditCardBottomSheetViewModelTests: XCTestCase { private var profile: MockProfile! private var viewModel: CreditCardBottomSheetViewModel! - private var files: FileAccessor! - private var autofill: RustAutofill! - private var encryptionKey: String! + private var mockAutofill: MockCreditCardProvider! private var samplePlainTextCard = UnencryptedCreditCardFields(ccName: "Allen Burges", ccNumber: "4111111111111111", ccNumberLast4: "1111", @@ -41,105 +39,49 @@ class CreditCardBottomSheetViewModelTests: XCTestCase { timeLastModified: 123123, timesUsed: 123123) - private var invalidSampleCreditCard = CreditCard(guid: "1", - ccName: "Allen Burges", - ccNumberEnc: "", - ccNumberLast4: "", - ccExpMonth: 1, - ccExpYear: 0, - ccType: "", - timeCreated: 0, - timeLastUsed: nil, - timeLastModified: 2, - timesUsed: 0) - override func setUp() { super.setUp() - files = MockFiles() - - if let rootDirectory = try? files.getAndEnsureDirectory() { - let databasePath = URL(fileURLWithPath: rootDirectory, isDirectory: true) - .appendingPathComponent("testAutofill.db").path - try? files.remove("testAutofill.db") - - if let key = try? createAutofillKey() { - encryptionKey = key - } else { - XCTFail("Encryption key wasn't created") - } - - autofill = RustAutofill(databasePath: databasePath) - _ = autofill.reopenIfClosed() - } else { - XCTFail("Could not retrieve root directory") - } - - profile = MockProfile() - _ = profile.autofill.reopenIfClosed() - - viewModel = CreditCardBottomSheetViewModel(profile: profile, - creditCard: nil, - decryptedCreditCard: samplePlainTextCard, - state: .save) + mockAutofill = MockCreditCardProvider() + viewModel = CreditCardBottomSheetViewModel( + creditCardProvider: mockAutofill, + creditCard: nil, + decryptedCreditCard: samplePlainTextCard, + state: .save + ) } override func tearDown() { - super.tearDown() - profile.shutdown() - profile = nil - autofill = nil - files = nil + mockAutofill = nil viewModel = nil + super.tearDown() } // MARK: - Test Cases - func testSavingCard() { - viewModel.creditCard = sampleCreditCard + func test_saveCreditCard_callsAddCreditCard() { let expectation = expectation(description: "wait for credit card fields to be saved") let decryptedCreditCard = viewModel.getPlainCreditCardValues(bottomSheetState: .save) // Make sure the year saved is a 4 digit year and not 2 digit // 2000 because that is our current period XCTAssertTrue(decryptedCreditCard!.ccExpYear > 2000) viewModel.saveCreditCard(with: decryptedCreditCard) { creditCard, error in - guard error == nil, let creditCard = creditCard else { - XCTFail() - return - } - XCTAssertEqual(creditCard.ccName, self.viewModel.creditCard?.ccName) - // Note: the number for credit card is encrypted so that part - // will get added later and for now we will check the name only + XCTAssertEqual(self.mockAutofill.addCreditCardCalledCount, 1) expectation.fulfill() } waitForExpectations(timeout: 1.0) } - func testUpdatingCard() { + func test_saveAndUpdateCreditCard_callsProperAutofillMethods() { viewModel.state = .save - viewModel.decryptedCreditCard = samplePlainTextCard let expectationSave = expectation(description: "wait for credit card fields to be saved") let expectationUpdate = expectation(description: "wait for credit card fields to be updated") viewModel.saveCreditCard(with: samplePlainTextCard) { creditCard, error in - guard error == nil, let creditCard = creditCard else { - XCTFail() - return - } + XCTAssertEqual(self.mockAutofill.addCreditCardCalledCount, 1) expectationSave.fulfill() - XCTAssertEqual(creditCard.ccName, self.viewModel.decryptedCreditCard?.ccName) - // Note: the number for credit card is encrypted so that part - // will get added later and for now we will check the name only - - self.samplePlainTextCard.ccExpYear = 2045 - self.samplePlainTextCard.ccName = "Test" self.viewModel.state = .update - - self.viewModel.updateCreditCard(for: creditCard.guid, + self.viewModel.updateCreditCard(for: creditCard?.guid, with: self.samplePlainTextCard) { didUpdate, error in - XCTAssertNotNil(didUpdate) - if let updated = didUpdate { - XCTAssert(updated) - } - XCTAssertNil(error) + XCTAssertEqual(self.mockAutofill.updateCreditCardCalledCount, 1) expectationUpdate.fulfill() } } @@ -338,54 +280,58 @@ class CreditCardBottomSheetViewModelTests: XCTestCase { XCTAssertEqual(value!.ccNumber, sampleCreditCardVal.ccNumberEnc) } - func test_didTapMainButton() { + func test_didTapMainButton_withSaveState_callsAddCreditCard() { viewModel.state = .save viewModel.decryptedCreditCard = samplePlainTextCard let expectation = expectation(description: "wait for credit card fields to be saved") - viewModel.didTapMainButton { error in - XCTAssertNil(error) + viewModel.didTapMainButton { _ in + XCTAssertEqual(self.mockAutofill.addCreditCardCalledCount, 1) expectation.fulfill() } waitForExpectations(timeout: 1.0) } - func test_invalidCreditCardUpdateDidTapMainButton() { + func test_didTapMainButton_withUpdateState_callsAddCreditCard() { viewModel.state = .update - viewModel.creditCard = invalidSampleCreditCard viewModel.decryptedCreditCard = samplePlainTextCard let expectation = expectation(description: "wait for credit card fields to be updated") - viewModel.didTapMainButton { error in - XCTAssertNotNil(error) + viewModel.didTapMainButton { _ in + XCTAssertEqual(self.mockAutofill.updateCreditCardCalledCount, 1) expectation.fulfill() } waitForExpectations(timeout: 1.0) } - func test_updateCreditCardList() { + func test_updateCreditCardList_callsListCreditCards() { let expectation = expectation(description: "wait for credit card to be added") viewModel.creditCard = nil viewModel.decryptedCreditCard = nil - // Add a sample card to the storage - viewModel.saveCreditCard(with: samplePlainTextCard) { creditCard, error in - guard error == nil, creditCard != nil else { - XCTFail() - return - } - // Make the view model state selected card - self.viewModel.state = .selectSavedCard - // Perform update - self.viewModel.updateCreditCardList({ cards in - // Check if the view model updated the list - let cards = self.viewModel.creditCards - XCTAssertNotNil(cards) - XCTAssert(!cards!.isEmpty) - expectation.fulfill() - }) - } - waitForExpectations(timeout: 3.0) + viewModel.state = .selectSavedCard + + viewModel.updateCreditCardList({ cards in + XCTAssertEqual(self.viewModel.creditCards, cards) + XCTAssertEqual(cards?.count, 1) + XCTAssertEqual(cards?.first?.guid, "1") + XCTAssertEqual(cards?.first?.ccName, "Allen Burges") + XCTAssertEqual(self.mockAutofill.listCreditCardsCalledCount, 1) + expectation.fulfill() + }) + waitForExpectations(timeout: 1.0) + } + + func test_updateCreditCardList_withoutSelectedSavedCardState_doesNotCallListCreditCards() { + let expectation = expectation(description: "wait for credit card to be added") + expectation.isInverted = true + viewModel.creditCard = nil + viewModel.decryptedCreditCard = nil + + viewModel.updateCreditCardList({ cards in + expectation.fulfill() + }) + waitForExpectations(timeout: 1.0) } } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Browser/TabScrollControllerTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Browser/TabScrollControllerTests.swift index 6faa5d0d43ad..ba3c292735e2 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Browser/TabScrollControllerTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Browser/TabScrollControllerTests.swift @@ -22,7 +22,7 @@ final class TabScrollControllerTests: XCTestCase { self.mockProfile = MockProfile() self.subject = TabScrollingController(windowUUID: windowUUID) - self.tab = Tab(profile: mockProfile, configuration: WKWebViewConfiguration(), windowUUID: windowUUID) + self.tab = Tab(profile: mockProfile, windowUUID: windowUUID) LegacyFeatureFlagsManager.shared.initializeDeveloperFeatures(with: mockProfile) mockGesture = UIPanGestureRecognizerMock() DependencyHelperMock().bootstrapDependencies() @@ -92,7 +92,7 @@ final class TabScrollControllerTests: XCTestCase { } private func setupTabScroll() { - tab.createWebview() + tab.createWebview(configuration: .init()) tab.webView?.scrollView.contentSize = CGSize(width: 200, height: 2000) tab.webView?.scrollView.delegate = subject subject.tab = tab diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Browser/Tabs/Legacy/LegacyTabPeekPreviewActionBuilderTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Browser/Tabs/Legacy/LegacyTabPeekPreviewActionBuilderTests.swift index 9eb6498277ca..226c91e28c0a 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Browser/Tabs/Legacy/LegacyTabPeekPreviewActionBuilderTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Browser/Tabs/Legacy/LegacyTabPeekPreviewActionBuilderTests.swift @@ -35,7 +35,7 @@ final class LegacyTabPeekPreviewActionBuilderTests: XCTestCase { builder.addSendToDeviceTitle { _, __ in } let action = builder.build().first ?? UIPreviewAction() - XCTAssertEqual(action.title, String.AppMenu.TouchActions.SendToDeviceTitle) + XCTAssertEqual(action.title, String.LegacyAppMenu.TouchActions.SendToDeviceTitle) } func test_addCopyUrl_afterAdded_shouldContainsInActions() { diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/JumpBackIn/JumpBackInDataAdaptorTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/JumpBackIn/JumpBackInDataAdaptorTests.swift index e97ba4607df6..717d022fdef1 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/JumpBackIn/JumpBackInDataAdaptorTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/JumpBackIn/JumpBackInDataAdaptorTests.swift @@ -8,15 +8,10 @@ import XCTest import WebKit import Common -// NOTE: -// Tab groups not tested at the moment since it depends on RustPlaces concrete object. -// Need protocol to be able to fix this - -class JumpBackInDataAdaptorTests: XCTestCase { +final class JumpBackInDataAdaptorTests: XCTestCase { var mockTabManager: MockTabManager! var mockProfile: MockProfile! let sleepTime: UInt64 = 100_000_000 - let webViewConfig = WKWebViewConfiguration() let windowUUID: WindowUUID = .XCTestDefaultUUID override func setUp() { @@ -33,7 +28,7 @@ class JumpBackInDataAdaptorTests: XCTestCase { mockTabManager = nil } - func testEmptyData_tabTrayGroupsDisabled() async { + func testEmptyData() async { let subject = createSubject() try? await Task.sleep(nanoseconds: sleepTime) @@ -153,7 +148,7 @@ extension JumpBackInDataAdaptorTests { func createTab(profile: MockProfile, urlString: String? = "www.website.com") -> Tab { - let tab = Tab(profile: profile, configuration: webViewConfig, windowUUID: windowUUID) + let tab = Tab(profile: profile, windowUUID: windowUUID) if let urlString = urlString { tab.url = URL(string: urlString)! diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/JumpBackIn/JumpBackInViewModelTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/JumpBackIn/JumpBackInViewModelTests.swift index 64d07c2920fe..f18208cf7264 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/JumpBackIn/JumpBackInViewModelTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Frontend/Home/JumpBackIn/JumpBackInViewModelTests.swift @@ -586,9 +586,8 @@ extension JumpBackInViewModelTests { } func createTab(profile: MockProfile, - configuration: WKWebViewConfiguration = WKWebViewConfiguration(), urlString: String? = "www.website.com") -> Tab { - let tab = Tab(profile: profile, configuration: configuration, windowUUID: windowUUID) + let tab = Tab(profile: profile, windowUUID: windowUUID) if let urlString = urlString { tab.url = URL(string: urlString)! @@ -628,15 +627,6 @@ actor JumpBackInDataAdaptorMock: JumpBackInDataAdaptor { return recentTabs } - var recentGroups: [ASGroup]? - func setRecentGroups(recentGroups: [ASGroup]?) { - self.recentGroups = recentGroups - } - - func getGroupsData() -> [ASGroup]? { - return recentGroups - } - var mockHasSyncedTabFeatureEnabled = true func setMockHasSyncedTabFeatureEnabled(enabled: Bool) { mockHasSyncedTabFeatureEnabled = enabled diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Helpers/AccountSyncHandlerTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Helpers/AccountSyncHandlerTests.swift index 7831d5dbd272..7449e1743783 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Helpers/AccountSyncHandlerTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Helpers/AccountSyncHandlerTests.swift @@ -45,24 +45,21 @@ class AccountSyncHandlerTests: XCTestCase { XCTAssertEqual(syncManager.syncNamedCollectionsCalled, 1) } - func testTabDidGainFocus_highThrottleTime_doesntSync() { - let subject = AccountSyncHandler(with: profile, throttleTime: 1000, queue: DispatchQueue.global()) + func testTabDidGainFocus_highThrottleTime_executedAtMostOnce() { + let threshold: Double = 1000 + let stepWaitTime = 2.0 + let subject = AccountSyncHandler(with: profile, throttleTime: threshold, queue: DispatchQueue.global()) let tab = createTab(profile: profile) - subject.tabDidGainFocus(tab) - - XCTAssertEqual(syncManager.syncNamedCollectionsCalled, 0) - } - func testTabDidGainFocus_multipleThrottle_withoutWaitdoesntSync() { - let subject = AccountSyncHandler(with: profile, throttleTime: 0.2, queue: DispatchQueue.global()) - let tab = createTab(profile: profile) - subject.tabDidGainFocus(tab) - subject.tabDidGainFocus(tab) - subject.tabDidGainFocus(tab) subject.tabDidGainFocus(tab) subject.tabDidGainFocus(tab) - XCTAssertEqual(syncManager.syncNamedCollectionsCalled, 0) + let expectation = XCTestExpectation(description: "SyncNamedCollectionsCalled value expectation") + DispatchQueue.main.asyncAfter(deadline: .now() + stepWaitTime) { + XCTAssertEqual(self.syncManager.syncNamedCollectionsCalled, 1) + expectation.fulfill() + } + wait(for: [expectation], timeout: stepWaitTime * 2) } func testTabDidGainFocus_multipleThrottle_withWaitSyncOnce() { @@ -82,9 +79,8 @@ class AccountSyncHandlerTests: XCTestCase { // MARK: - Helper methods private extension AccountSyncHandlerTests { func createTab(profile: MockProfile, - configuration: WKWebViewConfiguration = WKWebViewConfiguration(), urlString: String? = "www.website.com") -> Tab { - let tab = Tab(profile: profile, configuration: configuration, windowUUID: windowUUID) + let tab = Tab(profile: profile, windowUUID: windowUUID) if let urlString = urlString { tab.url = URL(string: urlString)! diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Helpers/AdjustTelemetryHelperTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Helpers/AdjustTelemetryHelperTests.swift index d98012d3d3e1..22b0063c10a8 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Helpers/AdjustTelemetryHelperTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Helpers/AdjustTelemetryHelperTests.swift @@ -1,83 +1,85 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/ - -import XCTest - -@testable import Client - -class AdjustTelemetryHelperTests: XCTestCase { - var telemetryWrapper: MockAdjustWrapper! - var gleanWrapper: MockGleanWrapper! - - override func setUp() { - super.setUp() - - telemetryWrapper = MockAdjustWrapper() - gleanWrapper = MockGleanWrapper() - } - - override func tearDown() { - super.tearDown() - - telemetryWrapper = nil - gleanWrapper = nil - } - - func testFailSetAttribution_WithAllNilData() { - let subject = createSubject() - let attribution = MockAdjustTelemetryData(campaign: nil, - adgroup: nil, - creative: nil, - network: nil) - subject.setAttributionData(attribution) - - XCTAssertEqual(telemetryWrapper.recordDeeplinkCalled, 0) - XCTAssertEqual(telemetryWrapper.recordCampaignCalled, 0) - XCTAssertEqual(telemetryWrapper.recordNetworkCalled, 0) - XCTAssertEqual(telemetryWrapper.recordCreativeCalled, 0) - XCTAssertEqual(telemetryWrapper.recordAdGroupCalled, 0) - XCTAssertEqual(gleanWrapper.submitPingCalled, 1) - } - - func testSetAttribution_WithSomeNilData() { - let subject = createSubject() - let attribution = MockAdjustTelemetryData(campaign: nil) - - subject.setAttributionData(attribution) - - XCTAssertEqual(telemetryWrapper.recordCampaignCalled, 0) - XCTAssertEqual(telemetryWrapper.savedNetwork, "test_network") - XCTAssertEqual(telemetryWrapper.recordNetworkCalled, 1) - XCTAssertEqual(telemetryWrapper.savedCreative, "test_creative") - XCTAssertEqual(telemetryWrapper.recordCreativeCalled, 1) - XCTAssertEqual(telemetryWrapper.savedAdGroup, "test_adgroup") - XCTAssertEqual(telemetryWrapper.recordAdGroupCalled, 1) - XCTAssertEqual(gleanWrapper.submitPingCalled, 1) - } - - func testDeeplinkHandleEvent_GleanCalled() { - let subject = createSubject() - let url = URL(string: "https://testurl.com")! - let mockData = MockAdjustTelemetryData() - - subject.sendDeeplinkTelemetry(url: url, attribution: mockData) - - XCTAssertEqual(telemetryWrapper.savedDeeplink, url) - XCTAssertEqual(telemetryWrapper.recordDeeplinkCalled, 1) - XCTAssertEqual(telemetryWrapper.savedCampaign, mockData.campaign!) - XCTAssertEqual(telemetryWrapper.savedNetwork, mockData.network!) - XCTAssertEqual(telemetryWrapper.savedCreative, mockData.creative!) - XCTAssertEqual(telemetryWrapper.savedAdGroup, mockData.adgroup!) - XCTAssertEqual(gleanWrapper.submitPingCalled, 1) - } - +// swiftlint:disable comment_spacing file_header +//// This Source Code Form is subject to the terms of the Mozilla Public +//// License, v. 2.0. If a copy of the MPL was not distributed with this +//// file, You can obtain one at http://mozilla.org/MPL/2.0/ +// +//import XCTest +// +//@testable import Client +// +//class AdjustTelemetryHelperTests: XCTestCase { +// var telemetryWrapper: MockAdjustWrapper! +// var gleanWrapper: MockGleanWrapper! +// +// override func setUp() { +// super.setUp() +// +// telemetryWrapper = MockAdjustWrapper() +// gleanWrapper = MockGleanWrapper() +// } +// +// override func tearDown() { +// super.tearDown() +// +// telemetryWrapper = nil +// gleanWrapper = nil +// } +// +// func testFailSetAttribution_WithAllNilData() { +// let subject = createSubject() +// let attribution = MockAdjustTelemetryData(campaign: nil, +// adgroup: nil, +// creative: nil, +// network: nil) +// subject.setAttributionData(attribution) +// +// XCTAssertEqual(telemetryWrapper.recordDeeplinkCalled, 0) +// XCTAssertEqual(telemetryWrapper.recordCampaignCalled, 0) +// XCTAssertEqual(telemetryWrapper.recordNetworkCalled, 0) +// XCTAssertEqual(telemetryWrapper.recordCreativeCalled, 0) +// XCTAssertEqual(telemetryWrapper.recordAdGroupCalled, 0) +// XCTAssertEqual(gleanWrapper.submitPingCalled, 1) +// } +// +// func testSetAttribution_WithSomeNilData() { +// let subject = createSubject() +// let attribution = MockAdjustTelemetryData(campaign: nil) +// +// subject.setAttributionData(attribution) +// +// XCTAssertEqual(telemetryWrapper.recordCampaignCalled, 0) +// XCTAssertEqual(telemetryWrapper.savedNetwork, "test_network") +// XCTAssertEqual(telemetryWrapper.recordNetworkCalled, 1) +// XCTAssertEqual(telemetryWrapper.savedCreative, "test_creative") +// XCTAssertEqual(telemetryWrapper.recordCreativeCalled, 1) +// XCTAssertEqual(telemetryWrapper.savedAdGroup, "test_adgroup") +// XCTAssertEqual(telemetryWrapper.recordAdGroupCalled, 1) +// XCTAssertEqual(gleanWrapper.submitPingCalled, 1) +// } +// +// func testDeeplinkHandleEvent_GleanCalled() { +// let subject = createSubject() +// let url = URL(string: "https://testurl.com")! +// let mockData = MockAdjustTelemetryData() +// +// subject.sendDeeplinkTelemetry(url: url, attribution: mockData) +// +// XCTAssertEqual(telemetryWrapper.savedDeeplink, url) +// XCTAssertEqual(telemetryWrapper.recordDeeplinkCalled, 1) +// XCTAssertEqual(telemetryWrapper.savedCampaign, mockData.campaign!) +// XCTAssertEqual(telemetryWrapper.savedNetwork, mockData.network!) +// XCTAssertEqual(telemetryWrapper.savedCreative, mockData.creative!) +// XCTAssertEqual(telemetryWrapper.savedAdGroup, mockData.adgroup!) +// XCTAssertEqual(gleanWrapper.submitPingCalled, 1) +// } +// // MARK: - Helper - - func createSubject() -> AdjustTelemetryHelper { - let subject = AdjustTelemetryHelper(gleanWrapper: gleanWrapper, - telemetry: telemetryWrapper) - trackForMemoryLeaks(subject) - return subject - } -} +// +// func createSubject() -> AdjustTelemetryHelper { +// let subject = AdjustTelemetryHelper(gleanWrapper: gleanWrapper, +// telemetry: telemetryWrapper) +// trackForMemoryLeaks(subject) +// return subject +// } +//} +// swiftlint:enable comment_spacing file_header diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Library/LibraryViewModelTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Library/LibraryViewModelTests.swift index 100a1eb0390f..2f4f3d49fc2a 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Library/LibraryViewModelTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Library/LibraryViewModelTests.swift @@ -40,13 +40,13 @@ class LibraryViewModelTests: XCTestCase { for panel in subject.panelDescriptors { switch panel.panelType { case .bookmarks: - XCTAssertEqual(panel.panelType.title, .AppMenu.AppMenuBookmarksTitleString) + XCTAssertEqual(panel.panelType.title, .LegacyAppMenu.AppMenuBookmarksTitleString) case .history: - XCTAssertEqual(panel.panelType.title, .AppMenu.AppMenuHistoryTitleString) + XCTAssertEqual(panel.panelType.title, .LegacyAppMenu.AppMenuHistoryTitleString) case .downloads: - XCTAssertEqual(panel.panelType.title, .AppMenu.AppMenuDownloadsTitleString) + XCTAssertEqual(panel.panelType.title, .LegacyAppMenu.AppMenuDownloadsTitleString) case .readingList: - XCTAssertEqual(panel.panelType.title, .AppMenu.AppMenuReadingListTitleString) + XCTAssertEqual(panel.panelType.title, .LegacyAppMenu.AppMenuReadingListTitleString) } } } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Messaging/GleanPlumbMessageManagerTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Messaging/GleanPlumbMessageManagerTests.swift index 211953397aaa..b3192d352fc0 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Messaging/GleanPlumbMessageManagerTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Messaging/GleanPlumbMessageManagerTests.swift @@ -18,7 +18,6 @@ class GleanPlumbMessageManagerTests: XCTestCase { super.setUp() Glean.shared.resetGlean(clearStores: true) - Glean.shared.enableTestingMode() messagingStore = MockGleanPlumbMessageStore(messageId: messageId) applicationHelper = MockApplicationHelper() subject = GleanPlumbMessageManager( diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Microsurvey/MicrosurveyMiddlewareTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Microsurvey/MicrosurveyMiddlewareTests.swift index afb093491bfa..f0bc7a285ad7 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Microsurvey/MicrosurveyMiddlewareTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Microsurvey/MicrosurveyMiddlewareTests.swift @@ -12,13 +12,12 @@ final class MicrosurveyMiddlewareTests: XCTestCase { override func setUp() { super.setUp() Glean.shared.resetGlean(clearStores: true) - Glean.shared.enableTestingMode() DependencyHelperMock().bootstrapDependencies() } override func tearDown() { - super.tearDown() DependencyHelperMock().reset() + super.tearDown() } func testDismissSurveyAction() throws { diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockAdjustTelemetryData.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockAdjustTelemetryData.swift index aba6c4d379a5..0d2a1203b33a 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockAdjustTelemetryData.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockAdjustTelemetryData.swift @@ -1,24 +1,24 @@ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/ - -import Foundation - -@testable import Client - -class MockAdjustTelemetryData: AdjustTelemetryData { - var campaign: String? - var adgroup: String? - var creative: String? - var network: String? - - init(campaign: String? = "test_campaing", - adgroup: String? = "test_adgroup", - creative: String? = "test_creative", - network: String? = "test_network") { - self.campaign = campaign - self.adgroup = adgroup - self.creative = creative - self.network = network - } -} +// +// import Foundation +// +// @testable import Client +// +// class MockAdjustTelemetryData: AdjustTelemetryData { +// var campaign: String? +// var adgroup: String? +// var creative: String? +// var network: String? +// +// init(campaign: String? = "test_campaing", +// adgroup: String? = "test_adgroup", +// creative: String? = "test_creative", +// network: String? = "test_network") { +// self.campaign = campaign +// self.adgroup = adgroup +// self.creative = creative +// self.network = network +// } +// } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockAdjustWrapper.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockAdjustWrapper.swift index d96f596d5fef..97161b8c6a5e 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockAdjustWrapper.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockAdjustWrapper.swift @@ -1,44 +1,44 @@ // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/ - -@testable import Client - -class MockAdjustWrapper: AdjustWrapper { - var recordDeeplinkCalled = 0 - var recordCampaignCalled = 0 - var recordAdGroupCalled = 0 - var recordCreativeCalled = 0 - var recordNetworkCalled = 0 - - var savedDeeplink: URL? - var savedCampaign: String? - var savedAdGroup: String? - var savedCreative: String? - var savedNetwork: String? - - func recordDeeplink(url: URL) { - recordDeeplinkCalled += 1 - savedDeeplink = url - } - - func record(campaign: String) { - recordCampaignCalled += 1 - savedCampaign = campaign - } - - func record(adgroup: String) { - recordAdGroupCalled += 1 - savedAdGroup = adgroup - } - - func record(creative: String) { - recordCreativeCalled += 1 - savedCreative = creative - } - - func record(network: String) { - recordNetworkCalled += 1 - savedNetwork = network - } -} +// +// @testable import Client +// +// class MockAdjustWrapper: AdjustWrapper { +// var recordDeeplinkCalled = 0 +// var recordCampaignCalled = 0 +// var recordAdGroupCalled = 0 +// var recordCreativeCalled = 0 +// var recordNetworkCalled = 0 +// +// var savedDeeplink: URL? +// var savedCampaign: String? +// var savedAdGroup: String? +// var savedCreative: String? +// var savedNetwork: String? +// +// func recordDeeplink(url: URL) { +// recordDeeplinkCalled += 1 +// savedDeeplink = url +// } +// +// func record(campaign: String) { +// recordCampaignCalled += 1 +// savedCampaign = campaign +// } +// +// func record(adgroup: String) { +// recordAdGroupCalled += 1 +// savedAdGroup = adgroup +// } +// +// func record(creative: String) { +// recordCreativeCalled += 1 +// savedCreative = creative +// } +// +// func record(network: String) { +// recordNetworkCalled += 1 +// savedNetwork = network +// } +// } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockApplicationHelper.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockApplicationHelper.swift index 1b96b55ed81a..7eff386cae59 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockApplicationHelper.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockApplicationHelper.swift @@ -11,6 +11,7 @@ class MockApplicationHelper: ApplicationHelper { var openURLCalled = 0 var openURLInWindowCalled = 0 var lastOpenURL: URL? + var closeTabsCalled = 0 func openSettings() { openSettingsCalled += 1 @@ -25,4 +26,8 @@ class MockApplicationHelper: ApplicationHelper { openURLInWindowCalled += 1 lastOpenURL = url } + + func closeTabs(_ urls: [URL]) { + closeTabsCalled += 1 + } } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockBrowserViewController.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockBrowserViewController.swift index ba53d970f70f..d3fa826d348a 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockBrowserViewController.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockBrowserViewController.swift @@ -95,7 +95,7 @@ class MockBrowserViewController: BrowserViewController { openURLInNewTabURL = url openURLInNewTabIsPrivate = isPrivate openURLInNewTabCount += 1 - return Tab(profile: MockProfile(), configuration: .init(), windowUUID: windowUUID) + return Tab(profile: MockProfile(), windowUUID: windowUUID) } override func handleQRCode() { diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockDataCleaner.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockDataCleaner.swift new file mode 100644 index 000000000000..1258ecb24463 --- /dev/null +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockDataCleaner.swift @@ -0,0 +1,25 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Shared +@testable import Client + +class MockCookiesClearable: CookiesClearable { + var isSucceed: Success? + + override func clear() -> Success { + isSucceed = succeed() + return succeed() + } +} + +class MockSiteDataClearable: SiteDataClearable { + var isSucceed: Success? + + override func clear() -> Success { + isSucceed = succeed() + return succeed() + } +} diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockLoginViewModelDelegate.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockLoginViewModelDelegate.swift new file mode 100644 index 000000000000..733e38d66c2f --- /dev/null +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockLoginViewModelDelegate.swift @@ -0,0 +1,18 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +@testable import Client + +class MockLoginViewModelDelegate: LoginViewModelDelegate { + var loginSectionsDidUpdateCalledCount = 0 + var breachPathDidUpdateCalledCount = 0 + func loginSectionsDidUpdate() { + loginSectionsDidUpdateCalledCount += 1 + } + + func breachPathDidUpdate() { + breachPathDidUpdateCalledCount += 1 + } +} diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockTabManager.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockTabManager.swift index 1039cc45b45e..1c7dee21dc14 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockTabManager.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockTabManager.swift @@ -37,6 +37,8 @@ class MockTabManager: TabManager { var addTabsForURLsCalled = 0 var addTabsURLs: [URL] = [] + var removeTabsByURLCalled = 0 + init(windowUUID: WindowUUID = WindowUUID.XCTestDefaultUUID) { self.windowUUID = windowUUID } @@ -60,9 +62,8 @@ class MockTabManager: TabManager { } func addTab(_ request: URLRequest?, afterTab: Tab?, isPrivate: Bool) -> Tab { - let configuration = WKWebViewConfiguration() let profile = MockProfile() - let tab = Tab(profile: profile, configuration: configuration, isPrivate: isPrivate, windowUUID: windowUUID) + let tab = Tab(profile: profile, isPrivate: isPrivate, windowUUID: windowUUID) tabs.append(tab) return tab } @@ -92,6 +93,10 @@ class MockTabManager: TabManager { func removeAllTabs(isPrivateMode: Bool) async {} + func removeTabs(by urls: [URL]) async { + removeTabsByURLCalled += 1 + } + func undoCloseAllTabs() {} func undoCloseTab() {} @@ -131,7 +136,7 @@ class MockTabManager: TabManager { } func addPopupForParentTab(profile: Profile, parentTab: Tab, configuration: WKWebViewConfiguration) -> Tab { - return Tab(profile: MockProfile(), configuration: WKWebViewConfiguration(), windowUUID: windowUUID) + return Tab(profile: MockProfile(), windowUUID: windowUUID) } func makeToastFromRecentlyClosedUrls(_ recentlyClosedTabs: [Tab], @@ -142,12 +147,11 @@ class MockTabManager: TabManager { @discardableResult func addTab(_ request: URLRequest!, - configuration: WKWebViewConfiguration!, afterTab: Tab?, zombie: Bool, isPrivate: Bool ) -> Tab { - return Tab(profile: MockProfile(), configuration: WKWebViewConfiguration(), windowUUID: windowUUID) + return Tab(profile: MockProfile(), windowUUID: windowUUID) } func backgroundRemoveAllTabs(isPrivate: Bool, diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockWindowManager.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockWindowManager.swift index 96dfb2acd201..036ba392fff4 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockWindowManager.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Mocks/MockWindowManager.swift @@ -18,11 +18,6 @@ final class MockWindowManager: WindowManager { // MARK: - WindowManager Protocol - var activeWindow: WindowUUID { - get { wrappedManager.activeWindow } - set { wrappedManager.activeWindow = newValue } - } - var windows: [WindowUUID: AppWindowInfo] { wrappedManager.windows } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/OnboardingTests/OnboardingTelemetryDelegationTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/OnboardingTests/OnboardingTelemetryDelegationTests.swift index cd8789f5d5d0..29c272263810 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/OnboardingTests/OnboardingTelemetryDelegationTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/OnboardingTests/OnboardingTelemetryDelegationTests.swift @@ -20,9 +20,8 @@ class OnboardingTelemetryDelegationTests: XCTestCase { } override func tearDown() { - super.tearDown() - Glean.shared.resetGlean(clearStores: true) nimbusUtility = nil + super.tearDown() } func testOnboardingCard_viewSendsCardView() { diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/OnboardingTests/OnboardingTelemetryUtilityTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/OnboardingTests/OnboardingTelemetryUtilityTests.swift index 5f728d4cb61e..ddce3bcc9865 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/OnboardingTests/OnboardingTelemetryUtilityTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/OnboardingTests/OnboardingTelemetryUtilityTests.swift @@ -15,11 +15,6 @@ class OnboardingTelemetryUtilityTests: XCTestCase { Glean.shared.resetGlean(clearStores: true) } - override func tearDown() { - super.tearDown() - Glean.shared.resetGlean(clearStores: true) - } - // MARK: - Card View telemetry func testSendOnboardingCardView_WelcomeCard_Success() { let subject = createTelemetryUtility(for: .freshInstall) diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/PasswordManagerViewModelTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/PasswordManagerViewModelTests.swift index 8ac444cd29c8..48d03de300c3 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/PasswordManagerViewModelTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/PasswordManagerViewModelTests.swift @@ -7,83 +7,92 @@ import MozillaAppServices import Shared import Storage import XCTest +import Glean @testable import Client class PasswordManagerViewModelTests: XCTestCase { var viewModel: PasswordManagerViewModel! var dataSource: LoginDataSource! + var mockDelegate: MockLoginViewModelDelegate! + var mockLoginProvider: MockLoginProvider! override func setUp() { super.setUp() DependencyHelperMock().bootstrapDependencies() let mockProfile = MockProfile() + self.mockLoginProvider = MockLoginProvider() let searchController = UISearchController() self.viewModel = PasswordManagerViewModel( profile: mockProfile, searchController: searchController, - theme: LightTheme() + theme: LightTheme(), + loginProvider: mockLoginProvider ) - self.dataSource = LoginDataSource(viewModel: self.viewModel) + self.mockDelegate = MockLoginViewModelDelegate() + self.viewModel.delegate = mockDelegate self.viewModel.setBreachAlertsManager(MockBreachAlertsClient()) - self.addLogins() + Glean.shared.resetGlean(clearStores: true) } override func tearDown() { - super.tearDown() - DependencyHelperMock().reset() viewModel = nil - dataSource = nil - } - - private func addLogins() { - _ = self.viewModel.profile.logins.wipeLocalEngine() - - for i in (0..<10) { - let login = LoginEntry(fromJSONDict: [ - "hostname": "https://example\(i).com", - "formSubmitUrl": "https://example.com", - "username": "username\(i)", - "password": "password\(i)" - ]) - let addExp = expectation(description: "\(#function)\(#line)") - self.viewModel.profile.logins.addLogin(login: login).upon { addResult in - XCTAssertTrue(addResult.isSuccess) - XCTAssertNotNil(addResult.successValue) - addExp.fulfill() - } - } - - let logins = self.viewModel.profile.logins.listLogins().value - XCTAssertTrue(logins.isSuccess) - XCTAssertNotNil(logins.successValue) - - waitForExpectations(timeout: 10.0, handler: nil) + mockLoginProvider = nil + mockDelegate = nil + DependencyHelperMock().reset() + super.tearDown() } - func testQueryLogins() { - let expectation = XCTestExpectation() - viewModel.queryLogins("") { emptyQueryResult in - XCTAssertEqual(emptyQueryResult.count, 10) + func testaddLoginWithEmptyString() { + let login = LoginEntry(fromJSONDict: [ + "hostname": "https://example.com", + "formSubmitUrl": "https://example.com", + "username": "username", + "password": "password" + ]) + let expectation = XCTestExpectation(description: "Waiting for login query to complete") + viewModel.save(loginRecord: login) { exampleQueryResult in + XCTAssertEqual(self.mockLoginProvider.addLoginCalledCount, 1) expectation.fulfill() } + wait(for: [expectation], timeout: 1) + testCounterMetricRecordingSuccess(metric: GleanMetrics.Logins.saved) + } - viewModel.queryLogins("example") { exampleQueryResult in - XCTAssertEqual(exampleQueryResult.count, 10) + func testaddLoginWithString() { + let login = LoginEntry(fromJSONDict: [ + "hostname": "https://example.com", + "formSubmitUrl": "https://example.com", + "username": "username", + "password": "password" + ]) + let expectation = XCTestExpectation(description: "Waiting for login query to complete") + viewModel.save(loginRecord: login) { exampleQueryResult in + XCTAssertEqual(self.mockLoginProvider.addLoginCalledCount, 1) expectation.fulfill() } + wait(for: [expectation], timeout: 1) + testCounterMetricRecordingSuccess(metric: GleanMetrics.Logins.saved) + } - viewModel.queryLogins("3") { threeQueryResult in - XCTAssertEqual(threeQueryResult.count, 1) + func testQueryLoginsWithEmptyString() { + let expectation = XCTestExpectation(description: "Waiting for login query to complete") + viewModel.queryLogins("") { emptyQueryResult in + XCTAssertEqual(self.mockDelegate.loginSectionsDidUpdateCalledCount, 0) + XCTAssertEqual(self.mockLoginProvider.searchLoginsWithQueryCalledCount, 1) expectation.fulfill() } + wait(for: [expectation], timeout: 1) + } - viewModel.queryLogins("yxz") { zQueryResult in - XCTAssertEqual(zQueryResult.count, 0) + func testQueryLoginsWithExampleString() { + let expectation = XCTestExpectation(description: "Waiting for login query to complete") + viewModel.queryLogins("example") { exampleQueryResult in + XCTAssertEqual(self.mockDelegate.loginSectionsDidUpdateCalledCount, 0) + XCTAssertEqual(self.mockLoginProvider.searchLoginsWithQueryCalledCount, 1) expectation.fulfill() } - - wait(for: [expectation], timeout: 5) + wait(for: [expectation], timeout: 1) } func testIsDuringSearchControllerDismiss() { diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/PrivateBrowsingTelemetryTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/PrivateBrowsingTelemetryTests.swift index f513252fc3e3..22abd67c3669 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/PrivateBrowsingTelemetryTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/PrivateBrowsingTelemetryTests.swift @@ -11,7 +11,6 @@ final class PrivateBrowsingTelemetryTests: XCTestCase { override func setUp() { super.setUp() Glean.shared.resetGlean(clearStores: true) - Glean.shared.enableTestingMode() } func testDataClearanceConfirmed() throws { diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/SponsoredTileTelemetryTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/SponsoredTileTelemetryTests.swift index f1158268bd93..13dda51927db 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/SponsoredTileTelemetryTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/SponsoredTileTelemetryTests.swift @@ -107,7 +107,6 @@ class SponsoredTileTelemetryTests: XCTestCase { func clearTest() { Glean.shared.resetGlean(clearStores: true) - Glean.shared.enableTestingMode() TelemetryContextualIdentifier.clearUserDefaults() } } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabManagement/Legacy/TabEventHandlerTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabManagement/Legacy/TabEventHandlerTests.swift index 5b604249fb3a..3c587b564528 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabManagement/Legacy/TabEventHandlerTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabManagement/Legacy/TabEventHandlerTests.swift @@ -14,7 +14,6 @@ class TabEventHandlerTests: XCTestCase { let windowUUID: WindowUUID = .XCTestDefaultUUID func testEventDelivery() { let tab = Tab(profile: MockProfile(), - configuration: WKWebViewConfiguration(), windowUUID: windowUUID) let handler = DummyHandler() diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabManagement/Legacy/TabsTelemetryTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabManagement/Legacy/TabsTelemetryTests.swift index 410216fd7a86..ccf1df84e1e2 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabManagement/Legacy/TabsTelemetryTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabManagement/Legacy/TabsTelemetryTests.swift @@ -19,7 +19,6 @@ class TabsTelemetryTests: XCTestCase { inactiveTabsManager = MockInactiveTabsManager() LegacyFeatureFlagsManager.shared.initializeDeveloperFeatures(with: profile) Glean.shared.resetGlean(clearStores: true) - Glean.shared.enableTestingMode() DependencyHelperMock().bootstrapDependencies() } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabManagement/Mocks/MockTabSessionStore.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabManagement/Mocks/MockTabSessionStore.swift index 5f4a04301a4a..bd7c16c091b6 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabManagement/Mocks/MockTabSessionStore.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabManagement/Mocks/MockTabSessionStore.swift @@ -10,17 +10,17 @@ class MockTabSessionStore: TabSessionStore { var tabID: UUID? var sessionData: Data? - func saveTabSession(tabID: UUID, sessionData: Data) async { + func saveTabSession(tabID: UUID, sessionData: Data) { saveTabSessionCallCount += 1 self.tabID = tabID self.sessionData = sessionData } - func fetchTabSession(tabID: UUID) async -> Data? { + func fetchTabSession(tabID: UUID) -> Data? { return Data() } - func clearAllData() async {} + func clearAllData() {} - func deleteUnusedTabSessionData(keeping: [UUID]) async {} + func deleteUnusedTabSessionData(keeping: [UUID]) {} } diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabManagement/TabManagerTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabManagement/TabManagerTests.swift index 3bd77bd03f36..7b3b6e78bd95 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabManagement/TabManagerTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TabManagement/TabManagerTests.swift @@ -16,7 +16,6 @@ class TabManagerTests: XCTestCase { var mockSessionStore: MockTabSessionStore! var mockProfile: MockProfile! var mockDiskImageStore: MockDiskImageStore! - let webViewConfig = WKWebViewConfiguration() let sleepTime: UInt64 = 1 * NSEC_PER_SEC let windowUUID: WindowUUID = .XCTestDefaultUUID @@ -26,8 +25,7 @@ class TabManagerTests: XCTestCase { DependencyHelperMock().bootstrapDependencies() LegacyFeatureFlagsManager.shared.initializeDeveloperFeatures(with: MockProfile()) // For this test suite, use a consistent window UUID for all test cases - let windowManager: WindowManager = AppContainer.shared.resolve() - let uuid = windowManager.activeWindow + let uuid: WindowUUID = .XCTestDefaultUUID tabWindowUUID = uuid mockProfile = MockProfile() @@ -220,7 +218,7 @@ class TabManagerTests: XCTestCase { private func addTabs(to subject: TabManagerImplementation, count: Int) { for _ in 0.. [Tab] { var tabs = [Tab]() for _ in 0.. TabWebView { - let subject = TabWebView(frame: .zero, configuration: configuration, windowUUID: windowUUID) + let subject = TabWebView(frame: .zero, configuration: .init(), windowUUID: windowUUID) try await Task.sleep(nanoseconds: sleepTime) subject.configure(delegate: tabWebViewDelegate, navigationDelegate: navigationDelegate) trackForMemoryLeaks(subject) diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Telemetry/HomepageTelemetryTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Telemetry/HomepageTelemetryTests.swift index 78d2dd91e476..805c45a05068 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Telemetry/HomepageTelemetryTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Telemetry/HomepageTelemetryTests.swift @@ -11,7 +11,6 @@ final class HomepageTelemetryTests: XCTestCase { override func setUp() { super.setUp() Glean.shared.resetGlean(clearStores: true) - Glean.shared.enableTestingMode() } func testPrivateModeShortcutToggleTappedInNormalMode() throws { diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TelemetryWrapperTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TelemetryWrapperTests.swift index 1f4da7206d2a..c1b62fc4a098 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TelemetryWrapperTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TelemetryWrapperTests.swift @@ -13,14 +13,15 @@ class TelemetryWrapperTests: XCTestCase { override func setUp() { super.setUp() + DependencyHelperMock().bootstrapDependencies() Glean.shared.resetGlean(clearStores: true) Experiments.events.clearEvents() } override func tearDown() { - super.tearDown() - Glean.shared.resetGlean(clearStores: true) Experiments.events.clearEvents() + DependencyHelperMock().reset() + super.tearDown() } // MARK: - Bookmarks @@ -770,6 +771,7 @@ class TelemetryWrapperTests: XCTestCase { func test_backgroundWallpaperMetric_themedWallpaperIsSent() { let profile = MockProfile() + LegacyFeatureFlagsManager.shared.initializeDeveloperFeatures(with: profile) TelemetryWrapper.shared.setup(profile: profile) let themedWallpaper = Wallpaper(id: "amethyst", @@ -1446,7 +1448,7 @@ class TelemetryWrapperTests: XCTestCase { ) } - func test_syncLogin_NimbusIsCalled() throws { + func test_syncLogin_NimbusIsCalled() { XCTAssertFalse( try Experiments.createJexlHelper()!.evalJexl( expression: "'sync.login_completed_view'|eventSum('Days', 1, 0) > 0" diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Toolbar/AddressToolbarContainerModelTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Toolbar/AddressToolbarContainerModelTests.swift index 37247162abf8..e6b514971467 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Toolbar/AddressToolbarContainerModelTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Toolbar/AddressToolbarContainerModelTests.swift @@ -32,7 +32,9 @@ class AddressToolbarContainerModelTests: XCTestCase { navigationToolbar: navigationState, isShowingNavigationToolbar: true, isShowingTopTabs: true, - menuWarningBadge: nil, + readerModeState: nil, + badgeImageName: nil, + maskImageName: nil, canGoBack: true, canGoForward: true, numberOfTabs: 1) diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TrackingProtectionTests/CertificatesViewModelTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TrackingProtectionTests/CertificatesViewModelTests.swift new file mode 100644 index 000000000000..c912cb5bc02f --- /dev/null +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TrackingProtectionTests/CertificatesViewModelTests.swift @@ -0,0 +1,57 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import XCTest +import X509 +@testable import Client + +final class CertificatesViewModelTests: XCTestCase { + private var viewModel: CertificatesViewModel! + + override func setUp() { + super.setUp() + viewModel = CertificatesViewModel(topLevelDomain: "topLevelDomainTest.com", + title: "TitleTest", + URL: "https://google.com", + certificates: []) + } + + override func tearDown() { + super.tearDown() + viewModel = nil + } + + func testGetCertificateValues() { + let data = "CN=www.google.com, O=Google Trust Services, C=US" + let result = viewModel.getCertificateValues(from: data) + XCTAssertEqual(result["CN"], "www.google.com") + XCTAssertEqual(result["O"], "Google Trust Services") + XCTAssertEqual(result["C"], "US") + } + + func testGetCertificateFromInvalidData() { + let result = viewModel.getCertificateValues(from: "") + XCTAssertEqual(result, [:]) + } + + func testGetCertificateValuesWithMissingValue() { + let data = "CN=www.google.com, O=, C=US" + let result = viewModel.getCertificateValues(from: data) + XCTAssertEqual(result["CN"], "www.google.com") + XCTAssertEqual(result["O"], "") + XCTAssertEqual(result["C"], "US") + } + + func testGetDNSNamesList() { + let input = #"DNSName("www.google.com"), DNSName("*www.google.com")"# + let result = viewModel.getDNSNamesList(from: input) + XCTAssertEqual(result, ["www.google.com", "*www.google.com"]) + } + + func testGetDNSNamesFromInvalidInput() { + let result = viewModel.getDNSNamesList(from: "") + XCTAssertEqual(result, []) + } +} diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/TrackingProtectionTests/TrackingProtectionModelTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TrackingProtectionTests/TrackingProtectionModelTests.swift new file mode 100644 index 000000000000..d800b7adbb14 --- /dev/null +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/TrackingProtectionTests/TrackingProtectionModelTests.swift @@ -0,0 +1,25 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import XCTest +@testable import Client + +final class TrackingProtectionModelTests: XCTestCase { + func testClearCookiesAndSiteData() { + let cookiesClearable = MockCookiesClearable() + let siteDataClearable = MockSiteDataClearable() + + let trackingProtectionModel = TrackingProtectionModel(url: URL(string: "https://www.google.com")!, + displayTitle: "TitleTest", + connectionSecure: false, + globalETPIsEnabled: false, + contentBlockerStatus: .disabled, + contentBlockerStats: nil, + selectedTab: nil) + trackingProtectionModel.clearCookiesAndSiteData(cookiesClearable: cookiesClearable, + siteDataClearable: siteDataClearable) + XCTAssertNotNil(cookiesClearable.isSucceed) + XCTAssertNotNil(siteDataClearable.isSucceed) + } +} diff --git a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Utils/SponsoredContentFilterUtilityTests.swift b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Utils/SponsoredContentFilterUtilityTests.swift index 6844bdba1feb..59ec8b8735ad 100644 --- a/firefox-ios/firefox-ios-tests/Tests/ClientTests/Utils/SponsoredContentFilterUtilityTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/ClientTests/Utils/SponsoredContentFilterUtilityTests.swift @@ -150,18 +150,18 @@ extension SponsoredContentFilterUtilityTests { sponsoredTabsCount: Int) -> [Tab] { var tabs = [Tab]() (0..= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.8.7" + "version": "==1.8.8" }, "wsproto": { "hashes": [ diff --git a/firefox-ios/firefox-ios-tests/Tests/UnitTest.xctestplan b/firefox-ios/firefox-ios-tests/Tests/UnitTest.xctestplan index 6b1d809ddb5f..27bc57e3417b 100644 --- a/firefox-ios/firefox-ios-tests/Tests/UnitTest.xctestplan +++ b/firefox-ios/firefox-ios-tests/Tests/UnitTest.xctestplan @@ -94,6 +94,7 @@ }, { "skippedTests" : [ + "ContentBlockerTests", "ETPCoverSheetTests", "GleanPlumbMessageManagerTests\/testManagerOnMessagePressed_withMalformedURL()", "HistoryHighlightsDataAdaptorTests", @@ -205,6 +206,13 @@ "identifier" : "ContentBlockingGeneratorTests", "name" : "ContentBlockingGeneratorTests" } + }, + { + "target" : { + "containerPath" : "container:..\/BrowserKit", + "identifier" : "MenuKitTests", + "name" : "MenuKitTests" + } } ], "version" : 1 diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ActivityStreamTest.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ActivityStreamTest.swift index 0f715fe260b1..1800d4e76614 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ActivityStreamTest.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ActivityStreamTest.swift @@ -10,7 +10,7 @@ let newTopSite = [ "topSiteLabel": "Mozilla", "bookmarkLabel": "Internet for people, not profit — Mozilla (US)" ] -let allDefaultTopSites = ["Facebook", "YouTube", "Amazon", "Wikipedia", "Twitter"] +let allDefaultTopSites = ["Facebook", "YouTube", "Amazon", "Wikipedia", "X"] class ActivityStreamTest: BaseTestCase { typealias TopSites = AccessibilityIdentifiers.FirefoxHomepage.TopSites @@ -43,24 +43,24 @@ class ActivityStreamTest: BaseTestCase { super.tearDown() } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2273342 + // https://mozilla.testrail.io/index.php?/cases/view/2273342 // Smoketest func testDefaultSites() throws { XCTExpectFailure("The app was not launched", strict: false) { - waitForExistence(TopSiteCellgroup, timeout: 60) + waitForExistence(TopSiteCellgroup, timeout: TIMEOUT_LONG) } - XCTAssertTrue(app.collectionViews[AccessibilityIdentifiers.FirefoxHomepage.collectionView].exists) + mozWaitForElementToExist(app.collectionViews[AccessibilityIdentifiers.FirefoxHomepage.collectionView]) // There should be 5 top sites by default checkNumberOfExpectedTopSites(numberOfExpectedTopSites: 5) // Check their names so that test is added to Smoketest - XCTAssertTrue(app.collectionViews.cells.staticTexts["Twitter"].exists) - XCTAssertTrue(app.collectionViews.cells.staticTexts["Amazon"].exists) - XCTAssertTrue(app.collectionViews.cells.staticTexts["Wikipedia"].exists) - XCTAssertTrue(app.collectionViews.cells.staticTexts["YouTube"].exists) - XCTAssertTrue(app.collectionViews.cells.staticTexts["Facebook"].exists) + mozWaitForElementToExist(app.collectionViews.cells.staticTexts["X"]) + mozWaitForElementToExist(app.collectionViews.cells.staticTexts["Amazon"]) + mozWaitForElementToExist(app.collectionViews.cells.staticTexts["Wikipedia"]) + mozWaitForElementToExist(app.collectionViews.cells.staticTexts["YouTube"]) + mozWaitForElementToExist(app.collectionViews.cells.staticTexts["Facebook"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2272218 + // https://mozilla.testrail.io/index.php?/cases/view/2272218 func testTopSites2Add() { if iPad() { checkNumberOfExpectedTopSites(numberOfExpectedTopSites: 12) @@ -69,10 +69,9 @@ class ActivityStreamTest: BaseTestCase { } } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2272219 + // https://mozilla.testrail.io/index.php?/cases/view/2272219 func testTopSitesRemoveAllExceptDefaultClearPrivateData() { - waitForExistence(app.cells.staticTexts[newTopSite["bookmarkLabel"]!], timeout: 15) - XCTAssertTrue(app.cells.staticTexts[newTopSite["bookmarkLabel"]!].exists) + waitForExistence(app.cells.staticTexts[newTopSite["bookmarkLabel"]!], timeout: TIMEOUT_LONG) // A new site has been added to the top sites if iPad() { checkNumberOfExpectedTopSites(numberOfExpectedTopSites: 12) @@ -89,12 +88,12 @@ class ActivityStreamTest: BaseTestCase { navigator.goto(HomePanelsScreen) } checkNumberOfExpectedTopSites(numberOfExpectedTopSites: 5) - XCTAssertFalse(app.cells.staticTexts[newTopSite["bookmarkLabel"]!].exists) + mozWaitForElementToNotExist(app.cells.staticTexts[newTopSite["bookmarkLabel"]!]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2272220 + // https://mozilla.testrail.io/index.php?/cases/view/2272220 func testTopSitesRemoveAllExceptPinnedClearPrivateData() { - waitForExistence(TopSiteCellgroup, timeout: TIMEOUT) + waitForExistence(TopSiteCellgroup) if iPad() { app.textFields.element(boundBy: 0).tap() app.typeText("mozilla.org\n") @@ -112,16 +111,14 @@ class ActivityStreamTest: BaseTestCase { navigator.performAction(Action.OpenNewTabFromTabTray) let topSitesCells = app.collectionViews.cells["TopSitesCell"] - waitForExistence(topSitesCells.staticTexts[newTopSite["bookmarkLabel"]!], timeout: TIMEOUT) - XCTAssertTrue(topSitesCells.staticTexts[newTopSite["bookmarkLabel"]!].exists) + waitForExistence(topSitesCells.staticTexts[newTopSite["bookmarkLabel"]!], timeout: TIMEOUT_LONG) checkNumberOfExpectedTopSites(numberOfExpectedTopSites: 6) topSitesCells.staticTexts[newTopSite["bookmarkLabel"]!].press(forDuration: 1) selectOptionFromContextMenu(option: "Pin") - waitForExistence(topSitesCells.staticTexts[newTopSite["bookmarkLabel"]!], timeout: TIMEOUT) - XCTAssertTrue(topSitesCells.staticTexts[newTopSite["bookmarkLabel"]!].exists) + waitForExistence(topSitesCells.staticTexts[newTopSite["bookmarkLabel"]!], timeout: TIMEOUT_LONG) - waitForExistence(app.buttons["urlBar-cancel"], timeout: TIMEOUT) + waitForExistence(app.buttons["urlBar-cancel"]) navigator.performAction(Action.CloseURLBarOpen) navigator.nowAt(NewTabScreen) navigator.goto(SettingsScreen) @@ -129,22 +126,22 @@ class ActivityStreamTest: BaseTestCase { navigator.performAction(Action.AcceptClearPrivateData) navigator.goto(HomePanelsScreen) waitForExistence(topSitesCells.staticTexts[newTopSite["bookmarkLabel"]!]) - XCTAssertTrue(topSitesCells.staticTexts[newTopSite["bookmarkLabel"]!].exists) checkNumberOfExpectedTopSites(numberOfExpectedTopSites: 6) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2272514 + // https://mozilla.testrail.io/index.php?/cases/view/2272514 func testTopSitesShiftAfterRemovingOne() { // Check top site in first and second cell let allTopSites = app.collectionViews.cells.matching(identifier: "TopSitesCell") let topSiteFirstCell = allTopSites.element(boundBy: 0).label let topSiteSecondCell = allTopSites.element(boundBy: 1).label + mozWaitForElementToExist(allTopSites.firstMatch) XCTAssertTrue(topSiteFirstCell == allDefaultTopSites[0]) XCTAssertTrue(topSiteSecondCell == allDefaultTopSites[1]) // Remove facebook top sites, first cell - waitForExistence(allTopSites.element(boundBy: 0), timeout: TIMEOUT) + waitForExistence(allTopSites.element(boundBy: 0)) allTopSites.element(boundBy: 0).press(forDuration: 1) selectOptionFromContextMenu(option: "Remove") @@ -155,51 +152,52 @@ class ActivityStreamTest: BaseTestCase { waitForExistence(updatedAllTopSites.element(boundBy: 0)) let topSiteCells = updatedAllTopSites.staticTexts let topSiteFirstCellAfter = updatedAllTopSites.element(boundBy: 0).label + mozWaitForElementToExist(updatedAllTopSites.element(boundBy: 0)) XCTAssertTrue( topSiteFirstCellAfter == topSiteCells[allDefaultTopSites[1]].label, "First top site does not match" ) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2273338 + // https://mozilla.testrail.io/index.php?/cases/view/2273338 // Smoketest func testTopSitesOpenInNewPrivateTab() throws { XCTExpectFailure("The app was not launched", strict: false) { - waitForExistence(TopSiteCellgroup, timeout: 60) + waitForExistence(TopSiteCellgroup, timeout: TIMEOUT_LONG) } - waitForExistence(app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton], timeout: 5) + waitForExistence(app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton]) // Long tap on Wikipedia top site - waitForExistence(app.collectionViews.cells.staticTexts["Wikipedia"], timeout: 3) + waitForExistence(app.collectionViews.cells.staticTexts["Wikipedia"]) app.collectionViews.cells.staticTexts["Wikipedia"].press(forDuration: 1) + mozWaitForElementToExist(app.tables["Context Menu"].cells.otherElements["Open in a Private Tab"]) app.tables["Context Menu"].cells.otherElements["Open in a Private Tab"].tap() - XCTAssert(TopSiteCellgroup.exists) + mozWaitForElementToExist(TopSiteCellgroup) navigator.toggleOn(userState.isPrivate, withAction: Action.TogglePrivateMode) navigator.goto(TabTray) - waitForExistence(app.cells.staticTexts.element(boundBy: 0), timeout: 10) + waitForExistence(app.cells.staticTexts.element(boundBy: 0)) navigator.nowAt(TabTray) - waitForExistence(app.otherElements["Tabs Tray"].collectionViews.cells["Wikipedia"], timeout: TIMEOUT) + waitForExistence(app.otherElements["Tabs Tray"].collectionViews.cells["Wikipedia"]) app.otherElements["Tabs Tray"].collectionViews.cells["Wikipedia"].tap() // The website is open - XCTAssertFalse(TopSiteCellgroup.exists) - XCTAssertTrue(app.textFields["url"].exists) + mozWaitForElementToNotExist(TopSiteCellgroup) waitForValueContains(app.textFields["url"], value: "wikipedia.org") } // Smoketest func testTopSitesOpenInNewPrivateTabDefaultTopSite() { XCTExpectFailure("The app was not launched", strict: false) { - waitForExistence(TopSiteCellgroup, timeout: 60) + waitForExistence(TopSiteCellgroup, timeout: TIMEOUT_LONG) } - waitForExistence(app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton], timeout: 5) + waitForExistence(app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton]) navigator.nowAt(NewTabScreen) // Open one of the sites from Topsites and wait until page is loaded // Long tap on apple top site, second cell waitForExistence(app.collectionViews["FxCollectionView"].cells - .staticTexts[defaultTopSite["bookmarkLabel"]!], timeout: 3) + .staticTexts[defaultTopSite["bookmarkLabel"]!]) app.collectionViews["FxCollectionView"].cells.staticTexts[defaultTopSite["bookmarkLabel"]!].press(forDuration: 1) selectOptionFromContextMenu(option: "Open in a Private Tab") @@ -216,25 +214,31 @@ class ActivityStreamTest: BaseTestCase { if iPad() { navigator.goto(TabTray) numTabsOpen = app.otherElements["Tabs Tray"].collectionViews.cells.count + waitForExistence(app.otherElements["Tabs Tray"].collectionViews.cells.firstMatch) + } else { + waitForExistence(app.collectionViews.element(boundBy: 1).cells.firstMatch) } XCTAssertEqual(numTabsOpen, 1, "New tab not open") } private func checkNumberOfExpectedTopSites(numberOfExpectedTopSites: Int) { - waitForExistence(app.cells[TopSites.itemCell]) - XCTAssertTrue(app.cells[TopSites.itemCell].exists) + mozWaitForElementToExist(app.cells[TopSites.itemCell]) + mozWaitForElementToExist(app.cells[TopSites.itemCell]) let numberOfTopSites = app.collectionViews.cells.matching(identifier: TopSites.itemCell).count + mozWaitForElementToExist(app.collectionViews.cells.matching(identifier: TopSites.itemCell).firstMatch) XCTAssertEqual(numberOfTopSites, numberOfExpectedTopSites, "The number of Top Sites is not correct") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2273339 + // https://mozilla.testrail.io/index.php?/cases/view/2273339 func testContextMenuInLandscape() { // For iPhone test is failing to find top sites in landscape // can't scroll only to that area. Needs investigation if iPad() { XCUIDevice.shared.orientation = .landscapeLeft - waitForExistence(TopSiteCellgroup, timeout: TIMEOUT) + waitForExistence(TopSiteCellgroup) app.collectionViews.cells.staticTexts["Wikipedia"].press(forDuration: 1) + mozWaitForElementToExist(app.tables["Context Menu"]) + mozWaitForElementToExist(app.otherElements["Action Sheet"]) let contextMenuHeight = app.tables["Context Menu"].frame.size.height let parentViewHeight = app.otherElements["Action Sheet"].frame.size.height @@ -246,7 +250,7 @@ class ActivityStreamTest: BaseTestCase { } } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2436086 + // https://mozilla.testrail.io/index.php?/cases/view/2436086 func testLongTapOnTopSiteOptions() { waitForExistence(app.cells[TopSites.itemCell]) app.collectionViews.cells.element(boundBy: 3).press(forDuration: 1) diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/AuthenticationTest.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/AuthenticationTest.swift index b7782dcf8b42..67daef37dfe0 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/AuthenticationTest.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/AuthenticationTest.swift @@ -7,9 +7,9 @@ import XCTest let testBasicHTTPAuthURL = "https://jigsaw.w3.org/HTTP/Basic/" class AuthenticationTest: BaseTestCase { - // https://testrail.stage.mozaws.net/index.php?/cases/view/2360560 + // https://mozilla.testrail.io/index.php?/cases/view/2360560 func testBasicHTTPAuthenticationPromptVisible() { - mozWaitForElementToExist(app.textFields["url"], timeout: 5) + mozWaitForElementToExist(app.textFields["url"]) navigator.nowAt(NewTabScreen) navigator.openURL(testBasicHTTPAuthURL) mozWaitForElementToExist(app.staticTexts["Authentication required"], timeout: 100) @@ -17,11 +17,11 @@ class AuthenticationTest: BaseTestCase { "A username and password are being requested by jigsaw.w3.org. The site says: test" ]) - let placeholderValueUsername = app.alerts.textFields.element(boundBy: 0).value as! String - let placeholderValuePassword = app.alerts.secureTextFields.element(boundBy: 0).value as! String + let placeholderValueUsername = app.alerts.textFields.element(boundBy: 0) + let placeholderValuePassword = app.alerts.secureTextFields.element(boundBy: 0) - XCTAssertEqual(placeholderValueUsername, "Username") - XCTAssertEqual(placeholderValuePassword, "Password") + mozWaitForValueContains(placeholderValueUsername, value: "Username") + mozWaitForValueContains(placeholderValuePassword, value: "Password") mozWaitForElementToExist(app.alerts.buttons["Cancel"]) mozWaitForElementToExist(app.alerts.buttons["Log in"]) diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/BaseTestCase.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/BaseTestCase.swift index b3cf6a3a2c0f..1ae4465013ca 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/BaseTestCase.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/BaseTestCase.swift @@ -15,7 +15,7 @@ func path(forTestPage page: String) -> String { } // Extended timeout values for mozWaitForElementToExist and mozWaitForElementToNotExist -let TIMEOUT: TimeInterval = 15 +let TIMEOUT: TimeInterval = 20 let TIMEOUT_LONG: TimeInterval = 45 class BaseTestCase: XCTestCase { @@ -47,7 +47,7 @@ class BaseTestCase: XCTestCase { // Let's be sure the app is backgrounded _ = app.wait(for: XCUIApplication.State.runningBackgroundSuspended, timeout: TIMEOUT) let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard") - mozWaitForElementToExist(springboard.icons["XCUITests-Runner"], timeout: 10) + mozWaitForElementToExist(springboard.icons["XCUITests-Runner"]) app.activate() } @@ -57,7 +57,7 @@ class BaseTestCase: XCTestCase { sleep(2) swipeStart.press(forDuration: 0.1, thenDragTo: swipeEnd) let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard") - mozWaitForElementToExist(springboard.icons["XCUITests-Runner"], timeout: 10) + mozWaitForElementToExist(springboard.icons["XCUITests-Runner"]) app.activate() } @@ -86,6 +86,7 @@ class BaseTestCase: XCTestCase { app.activate() } app.launch() + mozWaitForElementToExist(app.windows.otherElements.firstMatch) } func setUpLaunchArguments() { @@ -140,7 +141,7 @@ class BaseTestCase: XCTestCase { func waitForExistence( _ element: XCUIElement, - timeout: TimeInterval = 5.0, + timeout: TimeInterval = TIMEOUT, file: String = #file, line: UInt = #line ) { @@ -162,7 +163,7 @@ class BaseTestCase: XCTestCase { func waitForNoExistence( _ element: XCUIElement, - timeoutValue: TimeInterval = 5.0, + timeoutValue: TimeInterval = TIMEOUT, file: String = #file, line: UInt = #line ) { @@ -204,7 +205,7 @@ class BaseTestCase: XCTestCase { _ element: XCUIElement, with predicateString: String, description: String? = nil, - timeout: TimeInterval = 5.0, + timeout: TimeInterval = TIMEOUT, file: String, line: UInt ) { @@ -259,7 +260,9 @@ class BaseTestCase: XCTestCase { .otherElements .otherElements .count - let numberOfExpectedRecentlyVisitedBookmarks = 3 + let numberOfExpectedRecentlyVisitedBookmarks = 2 + mozWaitForElementToExist(app.scrollViews + .cells[AccessibilityIdentifiers.FirefoxHomepage.Bookmarks.itemCell].firstMatch) XCTAssertEqual(numberOfRecentlyVisitedBookmarks, numberOfExpectedRecentlyVisitedBookmarks) } @@ -273,6 +276,8 @@ class BaseTestCase: XCTestCase { .otherElements .count let numberOfExpectedRecentlyVisitedBookmarks = 1 + mozWaitForElementToExist(app.scrollViews + .cells[AccessibilityIdentifiers.FirefoxHomepage.Bookmarks.itemCell].firstMatch) XCTAssertEqual(numberOfRecentlyVisitedBookmarks, numberOfExpectedRecentlyVisitedBookmarks) } @@ -287,7 +292,7 @@ class BaseTestCase: XCTestCase { userState.url = path(forTestPage: "test-mozilla-book.html") navigator.openURL(path(forTestPage: "test-mozilla-book.html")) waitUntilPageLoad() - mozWaitForElementToExist(app.buttons["Reader View"], timeout: TIMEOUT) + mozWaitForElementToExist(app.buttons["Reader View"]) app.buttons["Reader View"].tap() waitUntilPageLoad() mozWaitForElementToExist(app.buttons["Add to Reading List"]) @@ -307,7 +312,7 @@ class BaseTestCase: XCTestCase { } func selectOptionFromContextMenu(option: String) { - XCTAssertTrue(app.tables["Context Menu"].cells.otherElements[option].exists) + mozWaitForElementToExist(app.tables["Context Menu"].cells.otherElements[option]) app.tables["Context Menu"].cells.otherElements[option].tap() mozWaitForElementToNotExist(app.tables["Context Menu"]) } @@ -345,7 +350,7 @@ class BaseTestCase: XCTestCase { } func waitForTabsButton() { - mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton], timeout: TIMEOUT) + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton]) } func unlockLoginsView() { @@ -355,7 +360,7 @@ class BaseTestCase: XCTestCase { } let passcodeInput = springboard.otherElements.secureTextFields.firstMatch - mozWaitForElementToExist(passcodeInput, timeout: 20) + mozWaitForElementToExist(passcodeInput) passcodeInput.tap() passcodeInput.typeText("foo\n") mozWaitForElementToNotExist(passcodeInput) diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/BookmarksTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/BookmarksTests.swift index 795bba38f189..4b32dc66290e 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/BookmarksTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/BookmarksTests.swift @@ -27,7 +27,7 @@ class BookmarksTests: BaseTestCase { mozWaitForElementToExist(app.tables.otherElements[StandardImageIdentifiers.Large.bookmarkSlash]) app.otherElements[StandardImageIdentifiers.Large.bookmarkSlash].tap() navigator.nowAt(BrowserTab) - mozWaitForElementToExist(app.buttons["Undo"], timeout: 3) + mozWaitForElementToExist(app.buttons["Undo"]) app.buttons["Undo"].tap() } @@ -42,7 +42,7 @@ class BookmarksTests: BaseTestCase { } } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306905 + // https://mozilla.testrail.io/index.php?/cases/view/2306905 func testBookmarkingUI() { // Go to a webpage, and add to bookmarks, check it's added navigator.nowAt(NewTabScreen) @@ -85,34 +85,34 @@ class BookmarksTests: BaseTestCase { } private func checkEmptyBookmarkList() { - mozWaitForElementToExist(app.tables["Bookmarks List"], timeout: 5) + mozWaitForElementToExist(app.tables["Bookmarks List"]) let list = app.tables["Bookmarks List"].cells.count XCTAssertEqual(list, 0, "There should not be any entry in the bookmarks list") } private func checkItemInBookmarkList(oneItemBookmarked: Bool) { - mozWaitForElementToExist(app.tables["Bookmarks List"], timeout: 5) + mozWaitForElementToExist(app.tables["Bookmarks List"]) let bookmarksList = app.tables["Bookmarks List"] let list = bookmarksList.cells.count if oneItemBookmarked == true { XCTAssertEqual(list, 2, "There should be an entry in the bookmarks list") - XCTAssertTrue(bookmarksList.cells.element(boundBy: 0).staticTexts["Desktop Bookmarks"].exists) - XCTAssertTrue(bookmarksList.cells.element(boundBy: 1).staticTexts[url_2["bookmarkLabel"]!].exists) + mozWaitForElementToExist(bookmarksList.cells.element(boundBy: 0).staticTexts["Desktop Bookmarks"]) + mozWaitForElementToExist(bookmarksList.cells.element(boundBy: 1).staticTexts[url_2["bookmarkLabel"]!]) } else { XCTAssertEqual(list, 3, "There should be an entry in the bookmarks list") - XCTAssertTrue(bookmarksList.cells.element(boundBy: 1).staticTexts[urlLabelExample_3].exists) - XCTAssertTrue(bookmarksList.cells.element(boundBy: 2).staticTexts[url_2["bookmarkLabel"]!].exists) + mozWaitForElementToExist(bookmarksList.cells.element(boundBy: 1).staticTexts[urlLabelExample_3]) + mozWaitForElementToExist(bookmarksList.cells.element(boundBy: 2).staticTexts[url_2["bookmarkLabel"]!]) } } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306906 + // https://mozilla.testrail.io/index.php?/cases/view/2306906 func testAccessBookmarksFromContextMenu() { // Add a bookmark navigator.nowAt(NewTabScreen) navigator.openURL(path(forTestPage: url_2["url"]!)) waitUntilPageLoad() navigator.nowAt(BrowserTab) - mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton], timeout: 10) + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton]) bookmark() // There should be a bookmark @@ -120,16 +120,15 @@ class BookmarksTests: BaseTestCase { checkItemInBookmarkList(oneItemBookmarked: true) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306907 + // https://mozilla.testrail.io/index.php?/cases/view/2306907 // Smoketest func testBookmarksAwesomeBar() { XCTExpectFailure("The app was not launched", strict: false) { - mozWaitForElementToExist(app.textFields["url"], timeout: 60) + mozWaitForElementToExist(app.textFields["url"], timeout: TIMEOUT_LONG) } typeOnSearchBar(text: "www.google") mozWaitForElementToExist(app.tables["SiteTable"]) - mozWaitForElementToExist(app.tables["SiteTable"].cells.staticTexts["www.google"], timeout: 5) - XCTAssertTrue(app.tables["SiteTable"].cells.staticTexts["www.google"].exists) + mozWaitForElementToExist(app.tables["SiteTable"].cells.staticTexts["www.google"]) app.textFields["address"].typeText(".com") app.textFields["address"].typeText("\r") navigator.nowAt(BrowserTab) @@ -152,7 +151,7 @@ class BookmarksTests: BaseTestCase { bookmark() // Now the site should be suggested - mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton], timeout: 10) + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton]) navigator.performAction(Action.AcceptClearPrivateData) navigator.goto(BrowserTab) typeOnSearchBar(text: "mozilla.org") @@ -161,15 +160,15 @@ class BookmarksTests: BaseTestCase { XCTAssertNotEqual(app.tables["SiteTable"].cells.count, 0) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306913 + // https://mozilla.testrail.io/index.php?/cases/view/2306913 func testAddBookmark() { addNewBookmark() // Verify that clicking on bookmark opens the website app.tables["Bookmarks List"].cells.element(boundBy: 1).tap() - mozWaitForElementToExist(app.textFields["url"], timeout: 5) + mozWaitForElementToExist(app.textFields["url"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306914 + // https://mozilla.testrail.io/index.php?/cases/view/2306914 func testAddNewFolder() { navigator.goto(LibraryPanel_Bookmarks) navigator.nowAt(MobileBookmarks) @@ -194,7 +193,7 @@ class BookmarksTests: BaseTestCase { checkItemsInBookmarksList(items: 1) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306915 + // https://mozilla.testrail.io/index.php?/cases/view/2306915 func testAddNewMarker() { navigator.goto(LibraryPanel_Bookmarks) navigator.nowAt(MobileBookmarks) @@ -212,7 +211,7 @@ class BookmarksTests: BaseTestCase { checkItemsInBookmarksList(items: 1) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306916 + // https://mozilla.testrail.io/index.php?/cases/view/2306916 // Test failing in M1s because the swipe gesture. Needs work to run only on Intel. func testDeleteBookmarkSwiping() { addNewBookmark() @@ -224,7 +223,7 @@ class BookmarksTests: BaseTestCase { checkItemsInBookmarksList(items: 1) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306917 + // https://mozilla.testrail.io/index.php?/cases/view/2306917 func testDeleteBookmarkContextMenu() { addNewBookmark() // Remove by long press and select option from context menu @@ -235,7 +234,7 @@ class BookmarksTests: BaseTestCase { checkItemsInBookmarksList(items: 1) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306908 + // https://mozilla.testrail.io/index.php?/cases/view/2306908 // Smoketest func testUndoDeleteBookmark() { navigator.openURL(path(forTestPage: url_1)) @@ -251,7 +250,7 @@ class BookmarksTests: BaseTestCase { navigator.goto(LibraryPanel_Bookmarks) navigator.nowAt(MobileBookmarks) navigator.performAction(Action.AddNewBookmark) - mozWaitForElementToExist(app.navigationBars["Bookmarks"], timeout: 3) + mozWaitForElementToExist(app.navigationBars["Bookmarks"]) // Enter the bookmarks details app.tables.cells.textFields.element(boundBy: 0).tap() app.tables.cells.textFields.element(boundBy: 0).typeText("BBC") @@ -265,28 +264,28 @@ class BookmarksTests: BaseTestCase { } private func checkItemsInBookmarksList(items: Int) { - mozWaitForElementToExist(app.tables["Bookmarks List"], timeout: 3) + mozWaitForElementToExist(app.tables["Bookmarks List"]) XCTAssertEqual(app.tables["Bookmarks List"].cells.count, items) } private func typeOnSearchBar(text: String) { - mozWaitForElementToExist(app.textFields["url"], timeout: 5) + mozWaitForElementToExist(app.textFields["url"]) app.textFields["url"].tap() app.textFields["address"].typeText(text) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306909 + // https://mozilla.testrail.io/index.php?/cases/view/2306909 // Smoketest func testBookmarkLibraryAddDeleteBookmark() { // Verify that there are only 1 cell (desktop bookmark folder) XCTExpectFailure("The app was not launched", strict: false) { - mozWaitForElementToExist(app.textFields["url"], timeout: 60) + mozWaitForElementToExist(app.textFields["url"], timeout: TIMEOUT_LONG) } navigator.nowAt(NewTabScreen) waitForTabsButton() navigator.goto(LibraryPanel_Bookmarks) // There is only one row in the bookmarks panel, which is the desktop folder - mozWaitForElementToExist(app.tables["Bookmarks List"], timeout: 5) + mozWaitForElementToExist(app.tables["Bookmarks List"]) XCTAssertEqual(app.tables["Bookmarks List"].cells.count, 1) // Add a bookmark @@ -299,19 +298,15 @@ class BookmarksTests: BaseTestCase { // Check that it appears in Bookmarks panel navigator.goto(LibraryPanel_Bookmarks) - mozWaitForElementToExist(app.tables["Bookmarks List"], timeout: 5) + mozWaitForElementToExist(app.tables["Bookmarks List"]) // Delete the Bookmark added, check it is removed app.tables["Bookmarks List"].cells.staticTexts["Example Domain"].swipeLeft() app.buttons["Delete"].tap() - mozWaitForElementToNotExist(app.tables["Bookmarks List"].cells.staticTexts["Example Domain"], timeout: 10) - XCTAssertFalse( - app.tables["Bookmarks List"].cells.staticTexts["Example Domain"].exists, - "Bookmark not removed successfully" - ) + mozWaitForElementToNotExist(app.tables["Bookmarks List"].cells.staticTexts["Example Domain"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306910 + // https://mozilla.testrail.io/index.php?/cases/view/2306910 // Smoketest func testDesktopFoldersArePresent() { // Verify that there are only 1 cell (desktop bookmark folder) @@ -319,16 +314,16 @@ class BookmarksTests: BaseTestCase { waitForTabsButton() navigator.goto(LibraryPanel_Bookmarks) // There is only one folder at the root of the bookmarks - mozWaitForElementToExist(app.tables["Bookmarks List"], timeout: 5) + mozWaitForElementToExist(app.tables["Bookmarks List"]) XCTAssertEqual(app.tables["Bookmarks List"].cells.count, 1) // There is only three folders inside the desktop bookmarks app.tables["Bookmarks List"].cells.firstMatch.tap() - mozWaitForElementToExist(app.tables["Bookmarks List"], timeout: 5) + mozWaitForElementToExist(app.tables["Bookmarks List"]) XCTAssertEqual(app.tables["Bookmarks List"].cells.count, 3) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306911 + // https://mozilla.testrail.io/index.php?/cases/view/2306911 func testRecentlyBookmarked() { navigator.openURL(path(forTestPage: url_2["url"]!)) waitForTabsButton() @@ -344,7 +339,7 @@ class BookmarksTests: BaseTestCase { checkItemInBookmarkList(oneItemBookmarked: false) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306866 + // https://mozilla.testrail.io/index.php?/cases/view/2306866 func testEditBookmark() { navigator.openURL(path(forTestPage: url_2["url"]!)) waitForTabsButton() @@ -358,7 +353,7 @@ class BookmarksTests: BaseTestCase { checkItemInBookmarkList(oneItemBookmarked: true) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2445808 + // https://mozilla.testrail.io/index.php?/cases/view/2445808 func testLongTapRecentlySavedLink() { // Go to "Recently saved" section and long tap on one of the links navigator.openURL(path(forTestPage: url_2["url"]!)) @@ -374,10 +369,10 @@ class BookmarksTests: BaseTestCase { mozWaitForElementToExist(ContextMenuTable.cells.otherElements[StandardImageIdentifiers.Large.plus]) mozWaitForElementToExist(ContextMenuTable.cells.otherElements[StandardImageIdentifiers.Large.privateMode]) mozWaitForElementToExist(ContextMenuTable.cells.otherElements[StandardImageIdentifiers.Large.bookmarkSlash]) - mozWaitForElementToExist(ContextMenuTable.cells.otherElements[StandardImageIdentifiers.Large.shareApple]) + mozWaitForElementToExist(ContextMenuTable.cells.otherElements[StandardImageIdentifiers.Large.share]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307054 + // https://mozilla.testrail.io/index.php?/cases/view/2307054 func testBookmark() { navigator.openURL(url_3) waitForTabsButton() diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/BrowsingPDFTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/BrowsingPDFTests.swift index a3e95c7ddf28..004428fcc970 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/BrowsingPDFTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/BrowsingPDFTests.swift @@ -13,7 +13,7 @@ let PDF_website = [ "longUrlValue": "http://www.education.gov.yk.ca/" ] class BrowsingPDFTests: BaseTestCase { - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307116 + // https://mozilla.testrail.io/index.php?/cases/view/2307116 func testOpenPDFViewer() { navigator.openURL(PDF_website["url"]!) waitUntilPageLoad() @@ -25,7 +25,7 @@ class BrowsingPDFTests: BaseTestCase { mozWaitForElementToExist(app.staticTexts["1 of 1"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307117 + // https://mozilla.testrail.io/index.php?/cases/view/2307117 // Smoketest func testOpenLinkFromPDF() { navigator.openURL(PDF_website["url"]!) @@ -42,7 +42,7 @@ class BrowsingPDFTests: BaseTestCase { mozWaitForValueContains(app.textFields["url"], value: PDF_website["pdfValue"]!) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307118 + // https://mozilla.testrail.io/index.php?/cases/view/2307118 func testLongPressOnPDFLink() { navigator.openURL(PDF_website["url"]!) waitUntilPageLoad() @@ -60,7 +60,7 @@ class BrowsingPDFTests: BaseTestCase { mozWaitForElementToExist(app.buttons["Share…"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307119 + // https://mozilla.testrail.io/index.php?/cases/view/2307119 func testLongPressOnPDFLinkToAddToReadingList() { navigator.openURL(PDF_website["url"]!) waitUntilPageLoad() @@ -75,19 +75,17 @@ class BrowsingPDFTests: BaseTestCase { navigator.goto(LibraryPanel_ReadingList) let savedToReadingList = app.tables["ReadingTable"].cells.staticTexts[PDF_website["longUrlValue"]!] mozWaitForElementToExist(savedToReadingList) - XCTAssertTrue(savedToReadingList.exists) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307120 + // https://mozilla.testrail.io/index.php?/cases/view/2307120 // Smoketest func testPinPDFtoTopSites() { navigator.openURL(PDF_website["url"]!) waitUntilPageLoad() navigator.performAction(Action.PinToTopSitesPAM) navigator.performAction(Action.OpenNewTabFromTabTray) - mozWaitForElementToExist(app.cells[AccessibilityIdentifiers.FirefoxHomepage.TopSites.itemCell], timeout: 10) + mozWaitForElementToExist(app.cells[AccessibilityIdentifiers.FirefoxHomepage.TopSites.itemCell]) mozWaitForElementToExist(app.collectionViews.cells.staticTexts[PDF_website["bookmarkLabel"]!]) - XCTAssertTrue(app.collectionViews.cells.staticTexts[PDF_website["bookmarkLabel"]!].exists) // Open pdf from pinned site let pdfTopSite = app @@ -108,10 +106,10 @@ class BrowsingPDFTests: BaseTestCase { mozWaitForElementToExist(app.tables.cells.otherElements[StandardImageIdentifiers.Large.pinSlash]) app.tables.cells.otherElements[StandardImageIdentifiers.Large.pinSlash].tap() mozWaitForElementToExist(app.cells[AccessibilityIdentifiers.FirefoxHomepage.TopSites.itemCell]) - XCTAssertTrue(app.collectionViews.cells.staticTexts[PDF_website["bookmarkLabel"]!].exists) + mozWaitForElementToExist(app.collectionViews.cells.staticTexts[PDF_website["bookmarkLabel"]!]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307121 + // https://mozilla.testrail.io/index.php?/cases/view/2307121 // Smoketest func testBookmarkPDF() { navigator.openURL(PDF_website["url"]!) @@ -121,6 +119,6 @@ class BrowsingPDFTests: BaseTestCase { navigator.goto(BrowserTabMenu) navigator.goto(LibraryPanel_Bookmarks) mozWaitForElementToExist(app.tables["Bookmarks List"]) - XCTAssertTrue(app.tables["Bookmarks List"].staticTexts[PDF_website["bookmarkLabel"]!].exists) + mozWaitForElementToExist(app.tables["Bookmarks List"].staticTexts[PDF_website["bookmarkLabel"]!]) } } diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ClipBoardTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ClipBoardTests.swift index a6e3fa2ffba7..f3b649f1c4c0 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ClipBoardTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ClipBoardTests.swift @@ -34,7 +34,7 @@ class ClipBoardTests: BaseTestCase { if let myString = UIPasteboard.general.string { let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard") let allowBtn = springboard.buttons["Allow Paste"] - if allowBtn.waitForExistence(timeout: 10) { + if allowBtn.waitForExistence(timeout: TIMEOUT) { allowBtn.tap() } @@ -49,7 +49,7 @@ class ClipBoardTests: BaseTestCase { } // This test is disabled in release, but can still run on master - // https://testrail.stage.mozaws.net/index.php?/cases/view/2325688 + // https://mozilla.testrail.io/index.php?/cases/view/2325688 func testClipboard() { navigator.nowAt(NewTabScreen) navigator.openURL(url) @@ -67,7 +67,7 @@ class ClipBoardTests: BaseTestCase { mozWaitForValueContains(app.textFields["address"], value: "www.example.com") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307051 + // https://mozilla.testrail.io/index.php?/cases/view/2307051 func testCopyLink() { // Tap on "Copy Link navigator.openURL(url_3) @@ -87,7 +87,7 @@ class ClipBoardTests: BaseTestCase { mozWaitForElementToExist(app.staticTexts["Example Domain"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2325691 + // https://mozilla.testrail.io/index.php?/cases/view/2325691 // Smoketest func testClipboardPasteAndGo() { // Temporarily disabled until url bar redesign work FXIOS-8172 diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/CreditCardsTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/CreditCardsTests.swift index 68889a091c29..81d92f3797f9 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/CreditCardsTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/CreditCardsTests.swift @@ -14,47 +14,43 @@ class CreditCardsTests: BaseTestCase { var expiration: XCUIElement! func initCardFields() { - nameOnCard = app.otherElements.textFields.element(boundBy: 0) - cardNr = app.otherElements.textFields.element(boundBy: 1) - expiration = app.otherElements.textFields.element(boundBy: 2) - if !iPad() { - nameOnCard = app.otherElements.textFields.element(boundBy: 1) - cardNr = app.otherElements.textFields.element(boundBy: 2) - expiration = app.otherElements.textFields.element(boundBy: 3) - } + nameOnCard = app.buttons["name"] + cardNr = app.buttons["number"] + expiration = app.buttons["expiration"] } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306967 + // https://mozilla.testrail.io/index.php?/cases/view/2306967 // SmokeTest func testAccessingTheCreditCardsSection() { navigator.nowAt(NewTabScreen) + waitForTabsButton() navigator.goto(CreditCardsSettings) unlockLoginsView() // Autofill Credit cards section displays let addCardButton = app.buttons[creditCardsStaticTexts.AutoFillCreditCard.addCard] mozWaitForElementToExist(app.staticTexts[creditCardsStaticTexts.AutoFillCreditCard.autoFillCreditCards]) - XCTAssertTrue(addCardButton.exists) - XCTAssertTrue(app.switches[creditCardsStaticTexts.AutoFillCreditCard.saveAutofillCards].exists) + mozWaitForElementToExist(addCardButton) + mozWaitForElementToExist(app.switches[creditCardsStaticTexts.AutoFillCreditCard.saveAutofillCards]) addCardButton.tap() // Add Credit Card page is displayed mozWaitForElementToExist(app.staticTexts[creditCardsStaticTexts.AddCreditCard.addCreditCard]) - XCTAssertTrue(app.staticTexts[creditCardsStaticTexts.AddCreditCard.nameOnCard].exists) - XCTAssertTrue(app.staticTexts[creditCardsStaticTexts.AddCreditCard.cardNumber].exists) - XCTAssertTrue(app.staticTexts[creditCardsStaticTexts.AddCreditCard.expiration].exists) - XCTAssertTrue(app.buttons[creditCardsStaticTexts.AddCreditCard.close].exists) - XCTAssertTrue(app.buttons[creditCardsStaticTexts.AddCreditCard.save].exists) + mozWaitForElementToExist(app.staticTexts[creditCardsStaticTexts.AddCreditCard.nameOnCard]) + mozWaitForElementToExist(app.staticTexts[creditCardsStaticTexts.AddCreditCard.cardNumber]) + mozWaitForElementToExist(app.staticTexts[creditCardsStaticTexts.AddCreditCard.expiration]) + mozWaitForElementToExist(app.buttons[creditCardsStaticTexts.AddCreditCard.close]) + mozWaitForElementToExist(app.buttons[creditCardsStaticTexts.AddCreditCard.save]) // Add, and save a valid credit card addCreditCard(name: "Test", cardNumber: cards[0], expirationDate: "0540") mozWaitForElementToExist(app.staticTexts[creditCardsStaticTexts.AutoFillCreditCard.savedCards]) - XCTAssertTrue(app.staticTexts.containingText("New").element.exists) - XCTAssertTrue(app.tables.cells.element(boundBy: 1).staticTexts.elementContainingText("1252").exists) + mozWaitForElementToExist(app.staticTexts.containingText("New").element) + mozWaitForElementToExist(app.tables.cells.element(boundBy: 1).buttons.elementContainingText("1252")) let cardDetails = ["Test", "Expires", "5/40"] for i in cardDetails { - XCTAssertTrue(app.tables.cells.element(boundBy: 1).staticTexts[i].exists, "\(i) does not exists") + mozWaitForElementToExist(app.tables.cells.element(boundBy: 1).buttons[i]) } } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306978 + // https://mozilla.testrail.io/index.php?/cases/view/2306978 // SmokeTest func testDeleteButtonFromEditCard() { addCardAndReachViewCardPage() @@ -71,8 +67,8 @@ class CreditCardsTests: BaseTestCase { creditCardsStaticTexts.EditCreditCard.remove ] mozWaitForElementToExist(removeThisCardAlert) - XCTAssertTrue(cancelButton.exists) - XCTAssertTrue(removeButton.exists) + mozWaitForElementToExist(cancelButton) + mozWaitForElementToExist(removeButton) // Tap on "CANCEL" cancelButton.tap() // The prompt is dismissed, the "Edit card" page is displayed @@ -87,10 +83,10 @@ class CreditCardsTests: BaseTestCase { // The credit card is deleted. The user is redirected to the "Saved cards" page mozWaitForElementToExist(app.staticTexts[creditCardsStaticTexts.AutoFillCreditCard.autoFillCreditCards]) mozWaitForElementToNotExist(app.staticTexts[creditCardsStaticTexts.AutoFillCreditCard.savedCards]) - XCTAssertFalse(app.tables.cells.element(boundBy: 1).exists) + mozWaitForElementToNotExist(app.tables.cells.element(boundBy: 1)) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306975 + // https://mozilla.testrail.io/index.php?/cases/view/2306975 // SmokeTest func testEditSavedCardsUI() { addCardAndReachViewCardPage() @@ -102,7 +98,7 @@ class CreditCardsTests: BaseTestCase { mozWaitForElementToExist(app.staticTexts[creditCardsStaticTexts.AutoFillCreditCard.savedCards]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306972 + // https://mozilla.testrail.io/index.php?/cases/view/2306972 func testManageCreditCardsOption() throws { if #unavailable(iOS 16) { throw XCTSkip("addCreditCardAndReachAutofillWebsite() does not work on iOS 15") @@ -123,10 +119,11 @@ class CreditCardsTests: BaseTestCase { mozWaitForElementToNotExist(app.buttons[useSavedCard]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306969 + // https://mozilla.testrail.io/index.php?/cases/view/2306969 // Smoketest - func testAutofillCreditCardsToggleOnOoff() { + func testAutofillCreditCardsToggleOnOff() { navigator.nowAt(NewTabScreen) + waitForTabsButton() navigator.goto(CreditCardsSettings) unlockLoginsView() mozWaitForElementToExist(app.staticTexts[creditCardsStaticTexts.AutoFillCreditCard.autoFillCreditCards]) @@ -179,7 +176,7 @@ class CreditCardsTests: BaseTestCase { } } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306971 + // https://mozilla.testrail.io/index.php?/cases/view/2306971 func testCreditCardsAutofill() throws { if #unavailable(iOS 16) { throw XCTSkip("addCreditCardAndReachAutofillWebsite() does not work on iOS 15") @@ -190,6 +187,7 @@ class CreditCardsTests: BaseTestCase { dismissSavedCardsPrompt() // The credit card's number and name are imported correctly on the designated fields let contentView = app.webViews["contentView"].webViews.textFields + mozWaitForElementToExist(contentView["Card number"]) XCTAssertEqual(contentView["Card number"].value! as! String, "2720 9943 2658 1252") XCTAssertEqual(contentView["Expiration"].value! as! String, "05 / 40") XCTAssertEqual(contentView["Full name on card"].value! as! String, "Test") @@ -197,7 +195,7 @@ class CreditCardsTests: BaseTestCase { XCTAssertEqual(contentView["ZIP"].value! as! String, "ZIP") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306976 + // https://mozilla.testrail.io/index.php?/cases/view/2306976 func testVerifyThatTheEditedCreditCardIsSaved() { // Go to a saved credit card and change the name on card let updatedName = "Firefox" @@ -208,7 +206,7 @@ class CreditCardsTests: BaseTestCase { typeCardName(name: updatedName) app.buttons["Save"].tap() // The name of the card is saved without issues - XCTAssertTrue(app.tables.cells.element(boundBy: 1).staticTexts[updatedName].exists, "\(updatedName) does not exists") + mozWaitForElementToExist(app.tables.cells.element(boundBy: 1).buttons[updatedName]) // Go to an saved credit card and change the credit card number app.tables.cells.element(boundBy: 1).tap() app.buttons[creditCardsStaticTexts.ViewCreditCard.edit].tap() @@ -217,7 +215,7 @@ class CreditCardsTests: BaseTestCase { typeCardNr(cardNo: cards[1]) app.buttons["Save"].tap() // The credit card number is saved without issues - XCTAssertTrue(app.tables.cells.element(boundBy: 1).staticTexts.elementContainingText("1111").exists) + mozWaitForElementToExist(app.tables.cells.element(boundBy: 1).buttons.elementContainingText("1111")) // Reach autofill website // reachAutofillWebsite() does not work on iOS 15 if #available(iOS 16, *) { @@ -228,11 +226,12 @@ class CreditCardsTests: BaseTestCase { } } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306974 + // https://mozilla.testrail.io/index.php?/cases/view/2306974 func testVerifyThatMultipleCardsCanBeAdded() { // Add multiple credit cards let expectedCards = 3 navigator.nowAt(NewTabScreen) + waitForTabsButton() navigator.goto(CreditCardsSettings) unlockLoginsView() mozWaitForElementToExist(app.staticTexts[creditCardsStaticTexts.AutoFillCreditCard.autoFillCreditCards]) @@ -250,11 +249,12 @@ class CreditCardsTests: BaseTestCase { ["1111", "Test2", "6/40"], ["9631", "Test3", "7/40"]] for i in 1...3 { - XCTAssertTrue(app.tables.cells.element(boundBy: i).staticTexts.elementContainingText(cardsInfo[i-1][0]).exists, + mozWaitForElementToExist(app.tables.cells.element(boundBy: i).buttons.firstMatch) + XCTAssertTrue(app.tables.cells.element(boundBy: i).buttons.elementContainingText(cardsInfo[i-1][0]).exists, "\(cardsInfo[i-1][0]) info is not displayed") - XCTAssertTrue(app.tables.cells.element(boundBy: i).staticTexts[cardsInfo[i-1][1]].exists, + XCTAssertTrue(app.tables.cells.element(boundBy: i).buttons[cardsInfo[i-1][1]].exists, "\(cardsInfo[i-1][1]) info is not displayed") - XCTAssertTrue(app.tables.cells.element(boundBy: i).staticTexts[cardsInfo[i-1][2]].exists, + XCTAssertTrue(app.tables.cells.element(boundBy: i).buttons[cardsInfo[i-1][2]].exists, "\(cardsInfo[i-1][2]) info is not displayed") } // reachAutofillWebsite() not working on iOS 15 @@ -289,7 +289,7 @@ class CreditCardsTests: BaseTestCase { } } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306977 + // https://mozilla.testrail.io/index.php?/cases/view/2306977 func testErrorStatesCreditCards() { // Go to a saved credit card and delete the name addCardAndReachViewCardPage() @@ -300,47 +300,51 @@ class CreditCardsTests: BaseTestCase { tapCardNr() // Error message is displayed mozWaitForElementToExist(app.otherElements.staticTexts["Add a name"]) + mozWaitForElementToExist(saveButton) XCTAssertFalse(saveButton.isEnabled) // Fill in the name on card, and delete the credit card number tapCardName() typeCardName(name: "Test") tapCardNr() mozWaitForElementToNotExist(app.otherElements.staticTexts["Add a name"]) + mozWaitForElementToExist(saveButton) XCTAssertTrue(saveButton.isEnabled) pressDelete() tapCardName() // Error message is displayed mozWaitForElementToExist(app.otherElements.staticTexts["Enter a valid card number"]) + mozWaitForElementToExist(saveButton) XCTAssertFalse(saveButton.isEnabled) // Fill in the name on the card and the credit card number, delete the Expiration date tapCardNr() typeCardNr(cardNo: cards[0]) tapExpiration() mozWaitForElementToNotExist(app.otherElements.staticTexts["Enter a valid card number"]) + mozWaitForElementToExist(saveButton) XCTAssertTrue(saveButton.isEnabled) pressDelete() tapCardNr() // Error message is displayed mozWaitForElementToExist(app.otherElements.staticTexts["Enter a valid expiration date"]) + mozWaitForElementToExist(saveButton) XCTAssertFalse(saveButton.isEnabled) // Add the credit card number back and save it tapExpiration() typeExpirationDate(exprDate: "0540") tapCardNr() mozWaitForElementToNotExist(app.otherElements.staticTexts["Enter a valid expiration date"]) + mozWaitForElementToExist(saveButton) XCTAssertTrue(saveButton.isEnabled) saveButton.tap() // The credit card is saved let cardsInfo = ["Test", "5/40"] - XCTAssertTrue(app.tables.cells.element(boundBy: 1).staticTexts.elementContainingText("1252").exists, - "1252 info is not displayed") + mozWaitForElementToExist(app.tables.cells.element(boundBy: 1).buttons.elementContainingText("1252")) for i in cardsInfo { - XCTAssertTrue(app.tables.cells.element(boundBy: 1).staticTexts[i].exists, - "\(i) info is not displayed") + mozWaitForElementToExist(app.tables.cells.element(boundBy: 1).buttons[i]) } } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306979 + // https://mozilla.testrail.io/index.php?/cases/view/2306979 func testSaveThisCardPrompt() { navigator.goto(NewTabScreen) navigator.openURL("https://checkout.stripe.dev/preview") @@ -394,14 +398,14 @@ class CreditCardsTests: BaseTestCase { unlockLoginsView() // The credit card is saved and displayed in the Credit cards section mozWaitForElementToExist(app.staticTexts[creditCardsStaticTexts.AutoFillCreditCard.savedCards]) - XCTAssertTrue(app.tables.cells.element(boundBy: 1).staticTexts.elementContainingText("1111").exists) + mozWaitForElementToExist(app.tables.cells.element(boundBy: 1).buttons.elementContainingText("1111")) let cardDetails = ["Test", "Expires", "5/40"] for i in cardDetails { - XCTAssertTrue(app.tables.cells.element(boundBy: 1).staticTexts[i].exists, "\(i) does not exists") + XCTAssertTrue(app.tables.cells.element(boundBy: 1).buttons[i].exists, "\(i) does not exists") } } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306980 + // https://mozilla.testrail.io/index.php?/cases/view/2306980 func testUpdatePrompt() throws { if #unavailable(iOS 16) { throw XCTSkip("addCreditCardAndReachAutofillWebsite() does not work on iOS 15") @@ -432,10 +436,10 @@ class CreditCardsTests: BaseTestCase { navigator.goto(CreditCardsSettings) unlockLoginsView() // Credit cards details did not change - XCTAssertTrue(app.tables.cells.element(boundBy: 1).staticTexts.elementContainingText("1252").exists) + mozWaitForElementToExist(app.tables.cells.element(boundBy: 1).staticTexts.elementContainingText("1252")) var cardDetails = ["Test", "Expires", "5/40"] for i in cardDetails { - XCTAssertTrue(app.tables.cells.element(boundBy: 1).staticTexts[i].exists, "\(i) does not exists") + mozWaitForElementToExist(app.tables.cells.element(boundBy: 1).staticTexts[i]) } // Repeat above steps and tap on "Save" navigator.goto(NewTabScreen) @@ -475,23 +479,24 @@ class CreditCardsTests: BaseTestCase { navigator.goto(CreditCardsSettings) unlockLoginsView() // Credit cards details changed - XCTAssertTrue(app.tables.cells.element(boundBy: 1).staticTexts.elementContainingText("1252").exists) + mozWaitForElementToExist(app.tables.cells.element(boundBy: 1).buttons.elementContainingText("1252")) cardDetails = ["TestTest2", "Expires", "5/40"] for i in cardDetails { - XCTAssertTrue(app.tables.cells.element(boundBy: 1).staticTexts[i].exists, "\(i) does not exists") + mozWaitForElementToExist(app.tables.cells.element(boundBy: 1).buttons[i]) } } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306970 + // https://mozilla.testrail.io/index.php?/cases/view/2306970 func testRedirectionToCreditCardsSection() { navigator.nowAt(NewTabScreen) + waitForTabsButton() navigator.goto(CreditCardsSettings) unlockLoginsView() mozWaitForElementToExist(app.staticTexts[creditCardsStaticTexts.AutoFillCreditCard.autoFillCreditCards]) restartInBackground() unlockLoginsView() let addCardButton = app.buttons[creditCardsStaticTexts.AutoFillCreditCard.addCard] - XCTAssertTrue(addCardButton.exists) + mozWaitForElementToExist(addCardButton) addCardButton.tap() addCreditCard(name: "Test", cardNumber: cards[0], expirationDate: "0540") restartInBackground() @@ -500,9 +505,9 @@ class CreditCardsTests: BaseTestCase { } private func selectCreditCardOnFormWebsite() { - mozWaitForElementToExist(app.scrollViews.otherElements.tables.staticTexts["Test"]) + mozWaitForElementToExist(app.scrollViews.otherElements.tables.buttons["Test"]) var attempts = 4 - while app.scrollViews.otherElements.tables.staticTexts["Test"].isHittable && attempts > 0 { + while app.scrollViews.otherElements.tables.buttons["Test"].isHittable && attempts > 0 { app.scrollViews.otherElements.tables.cells.firstMatch.tapOnApp() attempts -= 1 } @@ -640,6 +645,7 @@ class CreditCardsTests: BaseTestCase { private func addCreditCardAndReachAutofillWebsite() { // Access any website with a credit card form and tap on the credit card number/ credit card name navigator.nowAt(NewTabScreen) + waitForTabsButton() navigator.goto(CreditCardsSettings) unlockLoginsView() mozWaitForElementToExist(app.staticTexts[creditCardsStaticTexts.AutoFillCreditCard.autoFillCreditCards]) @@ -667,6 +673,7 @@ class CreditCardsTests: BaseTestCase { private func addCardAndReachViewCardPage() { navigator.nowAt(NewTabScreen) + waitForTabsButton() navigator.goto(CreditCardsSettings) unlockLoginsView() mozWaitForElementToExist(app.staticTexts[creditCardsStaticTexts.AutoFillCreditCard.autoFillCreditCards]) @@ -677,11 +684,11 @@ class CreditCardsTests: BaseTestCase { app.tables.cells.element(boundBy: 1).tap() // The "View card" page is displayed with all the details of the card mozWaitForElementToExist(app.navigationBars[creditCardsStaticTexts.ViewCreditCard.viewCard]) - XCTAssertTrue(app.tables.cells.element(boundBy: 1).staticTexts.elementContainingText("1252").exists) + mozWaitForElementToExist(app.tables.cells.element(boundBy: 1).buttons.elementContainingText("1252")) let cardDetails = ["Test", "05 / 40"] for i in cardDetails { if #available(iOS 16, *) { - XCTAssertTrue(app.textFields[i].exists, "\(i) does not exists") + XCTAssertTrue(app.buttons[i].exists, "\(i) does not exists") } else { mozWaitForElementToExist(app.staticTexts[i]) } diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/DataManagementTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/DataManagementTests.swift index b3dc06977241..85b0cf7d4161 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/DataManagementTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/DataManagementTests.swift @@ -6,12 +6,12 @@ import XCTest class DataManagementTests: BaseTestCase { // Testing the search bar, and clear website data option - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307015 + // https://mozilla.testrail.io/index.php?/cases/view/2307015 func testWebSiteDataOptions() { navigator.nowAt(NewTabScreen) waitForTabsButton() navigator.goto(WebsiteDataSettings) - mozWaitForElementToExist(app.tables.otherElements["Website Data"], timeout: 3) + mozWaitForElementToExist(app.tables.otherElements["Website Data"]) app.tables.otherElements["Website Data"].swipeDown() mozWaitForElementToExist(app.searchFields["Filter Sites"]) navigator.performAction(Action.TapOnFilterWebsites) @@ -23,7 +23,7 @@ class DataManagementTests: BaseTestCase { navigator.performAction(Action.TapOnFilterWebsites) app.buttons["Cancel"].tap() - mozWaitForElementToExist(app.tables.otherElements["Website Data"], timeout: 3) + mozWaitForElementToExist(app.tables.otherElements["Website Data"]) navigator.performAction(Action.AcceptClearAllWebsiteData) mozWaitForElementToExist(app.tables.cells["ClearAllWebsiteData"].staticTexts["Clear All Website Data"]) @@ -31,14 +31,18 @@ class DataManagementTests: BaseTestCase { XCTAssertEqual(expectedWebsitesCleared, 1) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307017 + // https://mozilla.testrail.io/index.php?/cases/view/2307017 // Smoketest func testWebSiteDataEnterFirstTime() { navigator.openURL("example.com") waitUntilPageLoad() navigator.goto(WebsiteDataSettings) - mozWaitForElementToExist(app.tables.otherElements["Website Data"], timeout: 3) - XCTAssertTrue(app.staticTexts["example.com"].exists, "The website visited is not displayed on Website Data") + mozWaitForElementToExist(app.tables.otherElements["Website Data"]) + mozWaitForElementToExist(app.tables.buttons.images["circle"].firstMatch) + if app.cells["ShowMoreWebsiteData"].exists { + app.cells["ShowMoreWebsiteData"].tap() + } + mozWaitForElementToExist(app.staticTexts["example.com"]) // There should be 4 entries. One is the website visited and 3 for extrainfo from the page. // This assert will remain commented until a way is found of having website data clean on the first run // This is to avoid intermittent failings diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/DatabaseFixtureTest.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/DatabaseFixtureTest.swift index 3e2bb5177dc2..088a0a940b38 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/DatabaseFixtureTest.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/DatabaseFixtureTest.swift @@ -23,7 +23,7 @@ class DatabaseFixtureTest: BaseTestCase { super.setUp() } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2458579 + // https://mozilla.testrail.io/index.php?/cases/view/2458579 func testBookmarksDatabaseFixture() { // Warning: Avoid using mozWaitForElementToExist as it is up to 25x less performant let tabsButton = app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton] @@ -51,7 +51,7 @@ class DatabaseFixtureTest: BaseTestCase { app.terminate() } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2459133 + // https://mozilla.testrail.io/index.php?/cases/view/2459133 func testHistoryDatabaseFixture() throws { let tabsButton = app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton] mozWaitForElementToExist(tabsButton) diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/DesktopModeTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/DesktopModeTests.swift index 78e7cbccc590..8284a1df5401 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/DesktopModeTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/DesktopModeTests.swift @@ -8,7 +8,7 @@ import XCTest // swiftlint:disable empty_count // Tests for both platforms class DesktopModeTestsIpad: IpadOnlyTestCase { - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306852 + // https://mozilla.testrail.io/index.php?/cases/view/2306852 // smoketest func testLongPressReload() { if skipPlatform { return } @@ -43,7 +43,7 @@ class DesktopModeTestsIpad: IpadOnlyTestCase { } class DesktopModeTestsIphone: IphoneOnlyTestCase { - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306853 + // https://mozilla.testrail.io/index.php?/cases/view/2306853 func testClearPrivateData() { if skipPlatform { return } @@ -68,7 +68,7 @@ class DesktopModeTestsIphone: IphoneOnlyTestCase { XCTAssert(app.webViews.staticTexts.matching(identifier: "MOBILE_UA").count > 0) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306855 + // https://mozilla.testrail.io/index.php?/cases/view/2306855 func testSameHostInMultipleTabs() { if skipPlatform { return } @@ -99,7 +99,7 @@ class DesktopModeTestsIphone: IphoneOnlyTestCase { XCTAssert(app.webViews.staticTexts.matching(identifier: "MOBILE_UA").count > 0) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306854 + // https://mozilla.testrail.io/index.php?/cases/view/2306854 // Smoketest func testChangeModeInSameTab() { if skipPlatform { return } @@ -123,7 +123,7 @@ class DesktopModeTestsIphone: IphoneOnlyTestCase { XCTAssert(app.webViews.staticTexts.matching(identifier: "MOBILE_UA").count > 0) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306856 + // https://mozilla.testrail.io/index.php?/cases/view/2306856 func testPrivateModeOffAlsoRemovesFromNormalMode() { if skipPlatform { return } @@ -142,7 +142,7 @@ class DesktopModeTestsIphone: IphoneOnlyTestCase { navigator.openURL(path(forTestPage: "test-user-agent.html")) // Workaround to be sure the snackbar disappears waitUntilPageLoad() - mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.reloadButton], timeout: 5) + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.reloadButton]) app.buttons[AccessibilityIdentifiers.Toolbar.reloadButton].tap() navigator.goto(BrowserTabMenu) navigator.goto(RequestMobileSite) // toggle off @@ -158,7 +158,7 @@ class DesktopModeTestsIphone: IphoneOnlyTestCase { XCTAssert(app.webViews.staticTexts.matching(identifier: "MOBILE_UA").count > 0) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306857 + // https://mozilla.testrail.io/index.php?/cases/view/2306857 func testPrivateModeOnHasNoAffectOnNormalMode() { if skipPlatform { return } @@ -185,13 +185,13 @@ class DesktopModeTestsIphone: IphoneOnlyTestCase { } } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306852 + // https://mozilla.testrail.io/index.php?/cases/view/2306852 // smoketest func testLongPressReload() { if skipPlatform { return } navigator.openURL(path(forTestPage: "test-user-agent.html")) waitUntilPageLoad() - mozWaitForElementToExist(app.webViews.staticTexts.firstMatch, timeout: 5) + mozWaitForElementToExist(app.webViews.staticTexts.firstMatch) XCTAssert(app.webViews.staticTexts.matching(identifier: "MOBILE_UA").count > 0) navigator.nowAt(BrowserTab) diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/DisplaySettingsTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/DisplaySettingsTests.swift index dff396002f75..d758043245cd 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/DisplaySettingsTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/DisplaySettingsTests.swift @@ -5,17 +5,17 @@ import XCTest class DisplaySettingTests: BaseTestCase { - // https://testrail.stage.mozaws.net/index.php?/cases/view/2337485 + // https://mozilla.testrail.io/index.php?/cases/view/2337485 func testCheckDisplaySettingsDefault() { navigator.nowAt(NewTabScreen) navigator.goto(DisplaySettings) mozWaitForElementToExist(app.navigationBars["Theme"]) - XCTAssertTrue(app.tables["DisplayTheme.Setting.Options"].exists) + mozWaitForElementToExist(app.tables["DisplayTheme.Setting.Options"]) let switchValue = app.switches["SystemThemeSwitchValue"].value! XCTAssertEqual(switchValue as! String, "1") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2337487 + // https://mozilla.testrail.io/index.php?/cases/view/2337487 func testCheckSystemThemeChanges() { navigator.nowAt(NewTabScreen) navigator.goto(DisplaySettings) @@ -30,27 +30,26 @@ class DisplaySettingTests: BaseTestCase { mozWaitForElementToExist(app.switches["SystemThemeSwitchValue"]) let switchValue = app.switches["SystemThemeSwitchValue"].value! XCTAssertEqual(switchValue as! String, "0") - XCTAssertTrue(app.cells.staticTexts["Light"].exists) - XCTAssertTrue(app.cells.staticTexts["Dark"].exists) + mozWaitForElementToExist(app.cells.staticTexts["Light"]) + mozWaitForElementToExist(app.cells.staticTexts["Dark"]) // Select the Automatic mode navigator.performAction(Action.SelectAutomatically) - - XCTAssertTrue(app.tables.otherElements["THRESHOLD"].exists) - XCTAssertFalse(app.cells.staticTexts["Light"].exists) - XCTAssertFalse(app.cells.staticTexts["Dark"].exists) + mozWaitForElementToExist(app.tables.otherElements["THRESHOLD"]) + mozWaitForElementToNotExist(app.cells.staticTexts["Light"]) + mozWaitForElementToNotExist(app.cells.staticTexts["Dark"]) // Now select the Manual mode navigator.performAction(Action.SelectManually) - XCTAssertFalse(app.tables.otherElements["THRESHOLD"].exists) - XCTAssertTrue(app.cells.staticTexts["Light"].exists) - XCTAssertTrue(app.cells.staticTexts["Dark"].exists) + mozWaitForElementToNotExist(app.tables.otherElements["THRESHOLD"]) + mozWaitForElementToExist(app.cells.staticTexts["Light"]) + mozWaitForElementToExist(app.cells.staticTexts["Dark"]) // Enable back system theme navigator.performAction(Action.SystemThemeSwitch) let switchValueAfter = app.switches["SystemThemeSwitchValue"].value! XCTAssertEqual(switchValueAfter as! String, "1") - XCTAssertFalse(app.tables["DisplayTheme.Setting.Options"].otherElements.staticTexts["SWITCH MODE"].exists) - XCTAssertFalse(app.tables["DisplayTheme.Setting.Options"].otherElements.staticTexts["THEME PICKER"].exists) + mozWaitForElementToNotExist(app.tables["DisplayTheme.Setting.Options"].otherElements.staticTexts["SWITCH MODE"]) + mozWaitForElementToNotExist(app.tables["DisplayTheme.Setting.Options"].otherElements.staticTexts["THEME PICKER"]) } } diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/DomainAutocompleteTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/DomainAutocompleteTests.swift index d1cda653f897..bd58ad14f0fa 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/DomainAutocompleteTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/DomainAutocompleteTests.swift @@ -43,7 +43,7 @@ class DomainAutocompleteTests: BaseTestCase { super.setUp() } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2334558 + // https://mozilla.testrail.io/index.php?/cases/view/2334558 func test1Autocomplete() { // Basic autocompletion cases // The autocomplete does not display the history item from the DB. Workaround is to manually visit "mozilla.org". @@ -71,7 +71,7 @@ class DomainAutocompleteTests: BaseTestCase { } // Test that deleting characters works correctly with autocomplete - // https://testrail.stage.mozaws.net/index.php?/cases/view/2334647 + // https://mozilla.testrail.io/index.php?/cases/view/2334647 func test3AutocompleteDeletingChars() { // The autocomplete does not display the history item from the DB. Workaround is to manually visit "mozilla.org". navigator.openURL("mozilla.org") @@ -101,7 +101,7 @@ class DomainAutocompleteTests: BaseTestCase { } } // Delete the entire string and verify that the home panels are shown again. - // https://testrail.stage.mozaws.net/index.php?/cases/view/2334648 + // https://mozilla.testrail.io/index.php?/cases/view/2334648 func test6DeleteEntireString() { navigator.goto(URLBarOpen) app.textFields["address"].typeText("www.moz") @@ -116,7 +116,7 @@ class DomainAutocompleteTests: BaseTestCase { } // Ensure that the scheme is included in the autocompletion. - // https://testrail.stage.mozaws.net/index.php?/cases/view/2334649 + // https://mozilla.testrail.io/index.php?/cases/view/2334649 func test4EnsureSchemeIncludedAutocompletion() { navigator.openURL(websiteExample["url"]!) waitUntilPageLoad() @@ -130,7 +130,7 @@ class DomainAutocompleteTests: BaseTestCase { } // Non-matches. - // https://testrail.stage.mozaws.net/index.php?/cases/view/2334650 + // https://mozilla.testrail.io/index.php?/cases/view/2334650 func test5NoMatches() { navigator.openURL("twitter.com/login") waitUntilPageLoad() @@ -177,7 +177,7 @@ class DomainAutocompleteTests: BaseTestCase { } // Test default domains. - // https://testrail.stage.mozaws.net/index.php?/cases/view/2334651 + // https://mozilla.testrail.io/index.php?/cases/view/2334651 func test2DefaultDomains() { navigator.goto(URLBarOpen) app.textFields["address"].typeText("a") @@ -199,7 +199,7 @@ class DomainAutocompleteTests: BaseTestCase { } // Test mixed case autocompletion. - // https://testrail.stage.mozaws.net/index.php?/cases/view/2334653 + // https://mozilla.testrail.io/index.php?/cases/view/2334653 func testMixedCaseAutocompletion() { navigator.goto(URLBarOpen) app.textFields["address"].typeText("MoZ") diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/DownloadsTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/DownloadsTests.swift index d6a4079e7cd3..ae8e55cabe59 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/DownloadsTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/DownloadsTests.swift @@ -15,12 +15,17 @@ let testBLOBFileSize = "35 bytes" class DownloadsTests: BaseTestCase { override func tearDown() { // The downloaded file has to be removed between tests + app.terminate() + app.activate() + waitForTabsButton() + navigator.nowAt(NewTabScreen) + navigator.goto(LibraryPanel_Downloads) mozWaitForElementToExist(app.tables["DownloadsTable"]) let list = app.tables["DownloadsTable"].cells.count if list != 0 { for _ in 0...list-1 { mozWaitForElementToExist(app.tables["DownloadsTable"].cells.element(boundBy: 0)) - app.tables["DownloadsTable"].cells.element(boundBy: 0).swipeLeft() + app.tables["DownloadsTable"].cells.element(boundBy: 0).swipeLeft(velocity: 200) mozWaitForElementToExist(app.tables.cells.buttons["Delete"]) app.tables.cells.buttons["Delete"].tap() } @@ -29,23 +34,22 @@ class DownloadsTests: BaseTestCase { } private func deleteItem(itemName: String) { - app.tables.cells.staticTexts[itemName].swipeLeft() - mozWaitForElementToExist(app.tables.cells.buttons["Delete"], timeout: TIMEOUT) + app.tables.cells.staticTexts[itemName].swipeLeft(velocity: 200) + mozWaitForElementToExist(app.tables.cells.buttons["Delete"]) app.tables.cells.buttons["Delete"].tap() } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306896 + // https://mozilla.testrail.io/index.php?/cases/view/2306896 func testDownloadFilesAppMenuFirstTime() { navigator.nowAt(NewTabScreen) navigator.goto(LibraryPanel_Downloads) - mozWaitForElementToExist(app.tables["DownloadsTable"], timeout: TIMEOUT) - XCTAssertTrue(app.tables["DownloadsTable"].exists) + mozWaitForElementToExist(app.tables["DownloadsTable"]) // Check that there is not any items and the default text shown is correct checkTheNumberOfDownloadedItems(items: 0) mozWaitForElementToExist(app.staticTexts["Downloaded files will show up here."]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306897 + // https://mozilla.testrail.io/index.php?/cases/view/2306897 func testDownloadFileContextMenu() { navigator.openURL(testURL) waitUntilPageLoad() @@ -56,7 +60,7 @@ class DownloadsTests: BaseTestCase { } app.webViews.links[testFileName].firstMatch.tap() - mozWaitForElementToExist(app.tables["Context Menu"], timeout: TIMEOUT) + mozWaitForElementToExist(app.tables["Context Menu"]) mozWaitForElementToExist(app.tables["Context Menu"].staticTexts[testFileNameDownloadPanel]) mozWaitForElementToExist(app.tables["Context Menu"].otherElements[StandardImageIdentifiers.Large.download]) app.buttons["Cancel"].tap() @@ -65,21 +69,21 @@ class DownloadsTests: BaseTestCase { checkTheNumberOfDownloadedItems(items: 0) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306898 + // https://mozilla.testrail.io/index.php?/cases/view/2306898 // Smoketest func testDownloadFile() { downloadFile(fileName: testFileName, numberOfDownloads: 1) navigator.goto(BrowserTabMenu) navigator.goto(LibraryPanel_Downloads) - mozWaitForElementToExist(app.tables["DownloadsTable"], timeout: TIMEOUT) + mozWaitForElementToExist(app.tables["DownloadsTable"]) // There should be one item downloaded. It's name and size should be shown checkTheNumberOfDownloadedItems(items: 1) mozWaitForElementToExist(app.tables.cells.staticTexts[testFileNameDownloadPanel]) mozWaitForElementToExist(app.tables.cells.staticTexts[testFileSize]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306899 + // https://mozilla.testrail.io/index.php?/cases/view/2306899 func testDownloadBLOBFile() { downloadBLOBFile() mozWaitForElementToExist(app.buttons["Downloads"]) @@ -93,7 +97,7 @@ class DownloadsTests: BaseTestCase { mozWaitForElementToExist(app.tables.cells.staticTexts[testBLOBFileSize]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306900 + // https://mozilla.testrail.io/index.php?/cases/view/2306900 func testDeleteDownloadedFile() throws { downloadFile(fileName: testFileName, numberOfDownloads: 1) navigator.goto(BrowserTabMenu) @@ -105,21 +109,22 @@ class DownloadsTests: BaseTestCase { checkTheNumberOfDownloadedItems(items: 0) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306901 + // https://mozilla.testrail.io/index.php?/cases/view/2306901 func testShareDownloadedFile() throws { downloadFile(fileName: testFileName, numberOfDownloads: 1) navigator.goto(BrowserTabMenu) navigator.goto(LibraryPanel_Downloads) let shareButton = app.tables.buttons.staticTexts["Share"] - app.tables.cells.staticTexts[testFileNameDownloadPanel].swipeLeft() + app.tables.cells.staticTexts[testFileNameDownloadPanel].swipeLeft(velocity: 200) mozWaitForElementToExist(shareButton) - XCTAssertTrue(shareButton.exists) mozWaitForElementToExist(app.tables.buttons.staticTexts["Delete"]) shareButton.tap(force: true) mozWaitForElementToExist(app.tables["DownloadsTable"]) mozWaitForElementToExist(app.tables["DownloadsTable"].staticTexts[testFileNameDownloadPanel]) if #available(iOS 16, *) { mozWaitForElementToExist(app.collectionViews.cells["Copy"]) + mozWaitForElementToExist(app.collectionViews.cells["Add Tags"]) + mozWaitForElementToExist(app.collectionViews.cells["Save to Files"]) } else { mozWaitForElementToExist(app.collectionViews.buttons["Copy"]) } @@ -132,7 +137,7 @@ class DownloadsTests: BaseTestCase { } } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306902 + // https://mozilla.testrail.io/index.php?/cases/view/2306902 func testLongPressOnDownloadedFile() { downloadFile(fileName: testFileName, numberOfDownloads: 1) navigator.goto(BrowserTabMenu) @@ -141,10 +146,12 @@ class DownloadsTests: BaseTestCase { mozWaitForElementToExist(app.tables["DownloadsTable"]) // Commenting out until share sheet can be managed with automated tests issue #5477 app.tables.cells.staticTexts[testFileNameDownloadPanel].press(forDuration: 2) - mozWaitForElementToExist(app.otherElements["ActivityListView"], timeout: TIMEOUT) + mozWaitForElementToExist(app.otherElements["ActivityListView"]) mozWaitForElementToExist(app.tables["DownloadsTable"].staticTexts[testFileNameDownloadPanel]) if #available(iOS 16, *) { mozWaitForElementToExist(app.collectionViews.cells["Copy"]) + mozWaitForElementToExist(app.collectionViews.cells["Add Tags"]) + mozWaitForElementToExist(app.collectionViews.cells["Save to Files"]) } else { mozWaitForElementToExist(app.collectionViews.buttons["Copy"]) } @@ -162,27 +169,27 @@ class DownloadsTests: BaseTestCase { waitUntilPageLoad() app.webViews.firstMatch.swipeLeft() for _ in 0.. MMScr // There is no Cancel option in iPad. app.otherElements["PopoverDismissRegion"].tap() } else { - app.buttons["PhotonMenu.close"].tap() + app.buttons["Close"].tap() } } } @@ -688,7 +688,7 @@ func createScreenGraph(for test: XCTestCase, with app: XCUIApplication) -> MMScr app.webViews.buttons[AccessibilityIdentifiers.Settings.FirefoxAccount.continueButton].tap() } screenState.gesture(forAction: Action.FxATapOnSignInButton) { userState in - app.webViews.buttons.element(boundBy: 0).tap() + app.webViews.buttons[AccessibilityIdentifiers.Settings.FirefoxAccount.signInButton].tap() } screenState.tap(app.webViews.links["Create an account"].firstMatch, to: FxCreateAccount) } @@ -770,7 +770,7 @@ func createScreenGraph(for test: XCTestCase, with app: XCUIApplication) -> MMScr } screenState.gesture(forAction: Action.ToggleRecentlySaved) { userState in - app.tables.cells.switches["Recently Saved"].tap() + app.tables.cells.switches["Bookmarks"].tap() } screenState.backAction = navigationControllerBackAction @@ -934,7 +934,6 @@ func createScreenGraph(for test: XCTestCase, with app: XCUIApplication) -> MMScr func makeURLBarAvailable(_ screenState: MMScreenStateNode) { screenState.tap(app.textFields["url"], to: URLBarOpen) screenState.gesture(to: URLBarLongPressMenu) { - sleep(1) app.textFields["url"].press(forDuration: 1.0) } } @@ -1086,6 +1085,7 @@ func createScreenGraph(for test: XCTestCase, with app: XCUIApplication) -> MMScr } map.addScreenState(BrowserTabMenu) { screenState in + sleep(1) screenState.tap( app.tables.otherElements[StandardImageIdentifiers.Large.settings], to: SettingsScreen @@ -1140,7 +1140,7 @@ func createScreenGraph(for test: XCTestCase, with app: XCUIApplication) -> MMScr } screenState.tap( - app.tables.otherElements[StandardImageIdentifiers.Large.shareApple], + app.tables.otherElements[StandardImageIdentifiers.Large.share], forAction: Action.ShareBrowserTabMenuOption ) { userState in } diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/HistoryTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/HistoryTests.swift index bf6525232d96..bc3d6148b215 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/HistoryTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/HistoryTests.swift @@ -55,36 +55,36 @@ class HistoryTests: BaseTestCase { super.setUp() } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307300 + // https://mozilla.testrail.io/index.php?/cases/view/2307300 func testEmptyHistoryListFirstTime() { navigator.nowAt(NewTabScreen) // Go to History List from Top Sites and check it is empty navigator.goto(LibraryPanel_History) mozWaitForElementToExist(app.tables.cells[HistoryPanelA11y.recentlyClosedCell]) - XCTAssertTrue(app.tables.cells[HistoryPanelA11y.recentlyClosedCell].staticTexts["Recently Closed"].exists) - XCTAssertTrue(app.tables[HistoryPanelA11y.tableView].staticTexts[emptyRecentlyClosedMesg].exists) + mozWaitForElementToExist(app.tables.cells[HistoryPanelA11y.recentlyClosedCell].staticTexts["Recently Closed"]) + mozWaitForElementToExist(app.tables[HistoryPanelA11y.tableView].staticTexts[emptyRecentlyClosedMesg]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307301 + // https://mozilla.testrail.io/index.php?/cases/view/2307301 func testOpenSyncDevices() { // Firefox sync page should be available navigator.nowAt(NewTabScreen) navigator.goto(TabTray) navigator.performAction(Action.ToggleSyncMode) mozWaitForElementToExist(app.otherElements.staticTexts["Firefox Sync"]) - XCTAssertTrue(app.otherElements.buttons["Sync and Save Data"].exists, "Sign in button does not appear") + mozWaitForElementToExist(app.otherElements.buttons["Sync and Save Data"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307487 + // https://mozilla.testrail.io/index.php?/cases/view/2307487 func testClearHistoryFromSettings() throws { XCTExpectFailure("The app was not launched", strict: false) { navigator.nowAt(NewTabScreen) // Browse to have an item in history list navigator.goto(LibraryPanel_History) - mozWaitForElementToExist(app.tables.cells[HistoryPanelA11y.recentlyClosedCell], timeout: TIMEOUT) - XCTAssertTrue(app.tables.cells.staticTexts[oldHistoryEntries[0]].exists) - XCTAssertFalse(app.tables[HistoryPanelA11y.tableView].staticTexts[emptyRecentlyClosedMesg].exists) + mozWaitForElementToExist(app.tables.cells[HistoryPanelA11y.recentlyClosedCell]) + mozWaitForElementToExist(app.tables.cells.staticTexts[oldHistoryEntries[0]]) + mozWaitForElementToNotExist(app.tables[HistoryPanelA11y.tableView].staticTexts[emptyRecentlyClosedMesg]) // Clear all private data via the settings navigator.goto(HomePanelsScreen) @@ -97,25 +97,25 @@ class HistoryTests: BaseTestCase { navigator.goto(LibraryPanel_History) mozWaitForElementToExist(app.tables[HistoryPanelA11y.tableView]) mozWaitForElementToExist(app.tables.cells[HistoryPanelA11y.recentlyClosedCell]) - XCTAssertFalse(app.tables.cells.staticTexts[oldHistoryEntries[0]].exists) - XCTAssertTrue(app.tables[HistoryPanelA11y.tableView].staticTexts[emptyRecentlyClosedMesg].exists) + mozWaitForElementToNotExist(app.tables.cells.staticTexts[oldHistoryEntries[0]]) + mozWaitForElementToExist(app.tables[HistoryPanelA11y.tableView].staticTexts[emptyRecentlyClosedMesg]) } } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307014 + // https://mozilla.testrail.io/index.php?/cases/view/2307014 // Smoketest func testClearPrivateData() throws { XCTExpectFailure("The app was not launched", strict: false) { navigator.nowAt(NewTabScreen) - mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton], timeout: TIMEOUT) + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton]) // Clear private data from settings and confirm navigator.goto(ClearPrivateDataSettings) app.tables.cells["ClearPrivateData"].tap() - mozWaitForElementToExist(app.tables.cells["ClearPrivateData"], timeout: TIMEOUT) + mozWaitForElementToExist(app.tables.cells["ClearPrivateData"]) app.alerts.buttons["OK"].tap() // Wait for OK pop-up to disappear after confirming - mozWaitForElementToNotExist(app.alerts.buttons["OK"], timeout: TIMEOUT) + mozWaitForElementToNotExist(app.alerts.buttons["OK"]) // Try to tap on the disabled Clear Private Data button app.tables.cells["ClearPrivateData"].tap() @@ -127,7 +127,7 @@ class HistoryTests: BaseTestCase { } } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307357 + // https://mozilla.testrail.io/index.php?/cases/view/2307357 func testRecentlyClosedWebsiteOpen() { // Open "Book of Mozilla" openBookOfMozilla() @@ -136,11 +136,11 @@ class HistoryTests: BaseTestCase { navigator.goto(BrowserTabMenu) navigator.goto(LibraryPanel_History) mozWaitForElementToExist(app.tables[HistoryPanelA11y.tableView]) - XCTAssertFalse(app.tables.cells.staticTexts[bookOfMozilla["label"]!].exists) - XCTAssertTrue(app.tables[HistoryPanelA11y.tableView].staticTexts[emptyRecentlyClosedMesg].exists) + mozWaitForElementToNotExist(app.tables.cells.staticTexts[bookOfMozilla["label"]!]) + mozWaitForElementToExist(app.tables[HistoryPanelA11y.tableView].staticTexts[emptyRecentlyClosedMesg]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307463 + // https://mozilla.testrail.io/index.php?/cases/view/2307463 func testRecentlyClosedWebsiteClosed() { // Open "Book of Mozilla" and close the tab openBookOfMozilla() @@ -149,19 +149,19 @@ class HistoryTests: BaseTestCase { // On regular mode, the closed tab is listed in "Recently Closed" list navigator.nowAt(NewTabScreen) navigator.goto(HistoryRecentlyClosed) - mozWaitForElementToExist(app.tables["Recently Closed Tabs List"], timeout: TIMEOUT) - XCTAssertTrue(app.tables.cells.staticTexts[bookOfMozilla["label"]!].exists) + mozWaitForElementToExist(app.tables["Recently Closed Tabs List"]) + mozWaitForElementToExist(app.tables.cells.staticTexts[bookOfMozilla["label"]!]) // On private mode, the closed tab on regular mode is listed in "Recently Closed" list as well navigator.toggleOn(userState.isPrivate, withAction: Action.TogglePrivateMode) navigator.performAction(Action.OpenNewTabFromTabTray) closeKeyboard() navigator.goto(HistoryRecentlyClosed) - mozWaitForElementToExist(app.tables["Recently Closed Tabs List"], timeout: TIMEOUT) - XCTAssertTrue(app.tables.cells.staticTexts[bookOfMozilla["label"]!].exists) + mozWaitForElementToExist(app.tables["Recently Closed Tabs List"]) + mozWaitForElementToExist(app.tables.cells.staticTexts[bookOfMozilla["label"]!]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307475 + // https://mozilla.testrail.io/index.php?/cases/view/2307475 func testRecentlyClosedPrivateMode() { // Open "Book of Mozilla" on private mode and close the tab waitForTabsButton() @@ -177,11 +177,11 @@ class HistoryTests: BaseTestCase { closeKeyboard() navigator.goto(LibraryPanel_History) mozWaitForElementToExist(app.tables[HistoryPanelA11y.tableView]) - XCTAssertTrue(app.tables[HistoryPanelA11y.tableView].staticTexts[emptyRecentlyClosedMesg].exists) - XCTAssertFalse(app.tables.cells.staticTexts[bookOfMozilla["label"]!].exists) + mozWaitForElementToExist(app.tables[HistoryPanelA11y.tableView].staticTexts[emptyRecentlyClosedMesg]) + mozWaitForElementToNotExist(app.tables.cells.staticTexts[bookOfMozilla["label"]!]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307479 + // https://mozilla.testrail.io/index.php?/cases/view/2307479 func testRemoveAllTabsButtonRecentlyClosedHistory() { // Open "Book of Mozilla" openBookOfMozilla() @@ -196,11 +196,11 @@ class HistoryTests: BaseTestCase { navigator.nowAt(NewTabScreen) navigator.goto(LibraryPanel_History) navigator.goto(HistoryRecentlyClosed) - mozWaitForElementToExist(app.tables["Recently Closed Tabs List"], timeout: TIMEOUT) - XCTAssertTrue(app.tables.cells.staticTexts[bookOfMozilla["label"]!].exists) + mozWaitForElementToExist(app.tables["Recently Closed Tabs List"]) + mozWaitForElementToExist(app.tables.cells.staticTexts[bookOfMozilla["label"]!]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307482 + // https://mozilla.testrail.io/index.php?/cases/view/2307482 func testClearRecentlyClosedHistory() { // Open "Book of Mozilla" and close the tab openBookOfMozilla() @@ -210,7 +210,7 @@ class HistoryTests: BaseTestCase { navigator.nowAt(NewTabScreen) navigator.goto(HistoryRecentlyClosed) mozWaitForElementToExist(app.tables["Recently Closed Tabs List"]) - XCTAssertTrue(app.tables.cells.staticTexts[bookOfMozilla["label"]!].exists) + mozWaitForElementToExist(app.tables.cells.staticTexts[bookOfMozilla["label"]!]) // Clear all private data via the settings navigator.goto(HomePanelsScreen) @@ -222,11 +222,11 @@ class HistoryTests: BaseTestCase { // The closed tab is *not* listed in "Recently Closed Tabs List" navigator.goto(LibraryPanel_History) mozWaitForElementToExist(app.tables[HistoryPanelA11y.tableView]) - XCTAssertTrue(app.tables[HistoryPanelA11y.tableView].staticTexts[emptyRecentlyClosedMesg].exists) - XCTAssertFalse(app.tables.cells.staticTexts[bookOfMozilla["label"]!].exists) + mozWaitForElementToExist(app.tables[HistoryPanelA11y.tableView].staticTexts[emptyRecentlyClosedMesg]) + mozWaitForElementToNotExist(app.tables.cells.staticTexts[bookOfMozilla["label"]!]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307483 + // https://mozilla.testrail.io/index.php?/cases/view/2307483 func testLongTapOptionsRecentlyClosedItem() { // Open "Book of Mozilla" and close the tab openBookOfMozilla() @@ -236,14 +236,14 @@ class HistoryTests: BaseTestCase { navigator.nowAt(NewTabScreen) navigator.goto(HistoryRecentlyClosed) mozWaitForElementToExist(app.tables["Recently Closed Tabs List"]) - XCTAssertTrue(app.tables.cells.staticTexts[bookOfMozilla["label"]!].exists) + mozWaitForElementToExist(app.tables.cells.staticTexts[bookOfMozilla["label"]!]) app.tables.cells.staticTexts[bookOfMozilla["label"]!].press(forDuration: 1) mozWaitForElementToExist(app.tables["Context Menu"]) - XCTAssertTrue(app.tables.otherElements[StandardImageIdentifiers.Large.plus].exists) - XCTAssertTrue(app.tables.otherElements[StandardImageIdentifiers.Large.privateMode].exists) + mozWaitForElementToExist(app.tables.otherElements[StandardImageIdentifiers.Large.plus]) + mozWaitForElementToExist(app.tables.otherElements[StandardImageIdentifiers.Large.privateMode]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307484 + // https://mozilla.testrail.io/index.php?/cases/view/2307484 func testOpenInNewTabRecentlyClosedItem() { // Open "Book of Mozilla" and close the tab openBookOfMozilla() @@ -253,7 +253,7 @@ class HistoryTests: BaseTestCase { navigator.nowAt(NewTabScreen) navigator.goto(HistoryRecentlyClosed) mozWaitForElementToExist(app.tables["Recently Closed Tabs List"]) - XCTAssertTrue(app.tables.cells.staticTexts[bookOfMozilla["label"]!].exists) + mozWaitForElementToExist(app.tables.cells.staticTexts[bookOfMozilla["label"]!]) XCTAssertEqual(userState.numTabs, 1) app.tables.cells.staticTexts[bookOfMozilla["label"]!].press(forDuration: 1) mozWaitForElementToExist(app.tables["Context Menu"]) @@ -267,14 +267,14 @@ class HistoryTests: BaseTestCase { } else { mozWaitForElementToExist(app.navigationBars.staticTexts["Open Tabs"]) } - XCTAssertTrue(app.staticTexts[bookOfMozilla["title"]!].exists) + mozWaitForElementToExist(app.staticTexts[bookOfMozilla["title"]!]) // userState.numTabs does not work on iOS 15 if #available(iOS 16, *) { XCTAssertEqual(userState.numTabs, 2) } } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307485 + // https://mozilla.testrail.io/index.php?/cases/view/2307485 func testOpenInNewPrivateTabRecentlyClosedItem() { // Open "Book of Mozilla" and close the tab openBookOfMozilla() @@ -284,7 +284,7 @@ class HistoryTests: BaseTestCase { navigator.nowAt(NewTabScreen) navigator.goto(HistoryRecentlyClosed) mozWaitForElementToExist(app.tables["Recently Closed Tabs List"]) - XCTAssertTrue(app.tables.cells.staticTexts[bookOfMozilla["label"]!].exists) + mozWaitForElementToExist(app.tables.cells.staticTexts[bookOfMozilla["label"]!]) app.tables.cells.staticTexts[bookOfMozilla["label"]!].press(forDuration: 1) mozWaitForElementToExist(app.tables["Context Menu"]) app.tables.otherElements[StandardImageIdentifiers.Large.privateMode].tap() @@ -304,11 +304,11 @@ class HistoryTests: BaseTestCase { } else { mozWaitForElementToExist(app.staticTexts["Private Browsing"]) } - XCTAssertTrue(app.staticTexts[bookOfMozilla["title"]!].exists) + mozWaitForElementToExist(app.staticTexts[bookOfMozilla["title"]!]) XCTAssertEqual(userState.numTabs, 1) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307486 + // https://mozilla.testrail.io/index.php?/cases/view/2307486 func testPrivateClosedSiteDoesNotAppearOnRecentlyClosed() { navigator.nowAt(NewTabScreen) @@ -334,7 +334,7 @@ class HistoryTests: BaseTestCase { mozWaitForElementToExist(app.tables[HistoryPanelA11y.tableView]) mozWaitForElementToNotExist(app.tables["Recently Closed Tabs List"]) XCTAssertFalse(app.cells.staticTexts["Recently Closed"].isSelected) - XCTAssertTrue(app.tables[HistoryPanelA11y.tableView].staticTexts[emptyRecentlyClosedMesg].exists) + mozWaitForElementToExist(app.tables[HistoryPanelA11y.tableView].staticTexts[emptyRecentlyClosedMesg]) XCTAssertFalse(app.tables.cells.staticTexts[bookOfMozilla["label"]!].exists) // On regular mode, the "Recently Closed Tabs List" is empty, too @@ -345,11 +345,11 @@ class HistoryTests: BaseTestCase { mozWaitForElementToExist(app.tables[HistoryPanelA11y.tableView]) mozWaitForElementToNotExist(app.tables["Recently Closed Tabs List"]) XCTAssertFalse(app.cells.staticTexts["Recently Closed"].isSelected) - XCTAssertTrue(app.tables[HistoryPanelA11y.tableView].staticTexts[emptyRecentlyClosedMesg].exists) + mozWaitForElementToExist(app.tables[HistoryPanelA11y.tableView].staticTexts[emptyRecentlyClosedMesg]) XCTAssertFalse(app.tables.cells.staticTexts[bookOfMozilla["label"]!].exists) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307025 + // https://mozilla.testrail.io/index.php?/cases/view/2307025 // Smoke func testTabHistory() { navigator.nowAt(NewTabScreen) @@ -357,19 +357,19 @@ class HistoryTests: BaseTestCase { let urlBarBackButton = app.windows.otherElements.buttons[AccessibilityIdentifiers.Toolbar.backButton] let urlBarForwardButton = app.windows.otherElements.buttons[AccessibilityIdentifiers.Toolbar.forwardButton] urlBarBackButton.press(forDuration: 1) - XCTAssertTrue(app.tables.staticTexts["The Book of Mozilla"].exists) + mozWaitForElementToExist(app.tables.staticTexts["The Book of Mozilla"]) app.tables.staticTexts["The Book of Mozilla"].tap() - XCTAssertFalse(app.tables.staticTexts["The Book of Mozilla"].exists) + mozWaitForElementToNotExist(app.tables.staticTexts["The Book of Mozilla"]) navigator.toggleOn(userState.isPrivate, withAction: Action.TogglePrivateMode) navigator.performAction(Action.OpenNewTabFromTabTray) openBookOfMozilla() urlBarBackButton.press(forDuration: 1) - XCTAssertTrue(app.tables.staticTexts["The Book of Mozilla"].exists) + mozWaitForElementToExist(app.tables.staticTexts["The Book of Mozilla"]) app.tables.staticTexts["The Book of Mozilla"].tap() urlBarBackButton.tap() XCTAssertFalse(urlBarBackButton.isEnabled) urlBarForwardButton.press(forDuration: 1) - XCTAssertTrue(app.tables.staticTexts["The Book of Mozilla"].exists) + mozWaitForElementToExist(app.tables.staticTexts["The Book of Mozilla"]) app.tables.staticTexts["The Book of Mozilla"].tap() mozWaitForValueContains(app.textFields["url"], value: "test-fixture/test-mozilla-book.html") } @@ -392,8 +392,8 @@ class HistoryTests: BaseTestCase { waitForTabsButton() navigator.nowAt(NewTabScreen) navigator.goto(LibraryPanel_History) - mozWaitForElementToExist(app.tables[HistoryPanelA11y.tableView], timeout: TIMEOUT) - XCTAssertTrue(app.tables.cells.staticTexts["Example Domain"].exists) + mozWaitForElementToExist(app.tables[HistoryPanelA11y.tableView]) + mozWaitForElementToExist(app.tables.cells.staticTexts["Example Domain"]) } private func openBookOfMozilla() { @@ -421,12 +421,12 @@ class HistoryTests: BaseTestCase { } private func closeKeyboard() { - mozWaitForElementToExist(app.buttons["urlBar-cancel"], timeout: TIMEOUT) + mozWaitForElementToExist(app.buttons["urlBar-cancel"]) navigator.performAction(Action.CloseURLBarOpen) navigator.nowAt(NewTabScreen) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306894 + // https://mozilla.testrail.io/index.php?/cases/view/2306894 // Smoke func testClearRecentHistory() { // Visit a page to create a recent history entry. @@ -436,10 +436,10 @@ class HistoryTests: BaseTestCase { // Older data will not be removed tapOnClearRecentHistoryOption(optionSelected: "Today") for entry in oldHistoryEntries { - XCTAssertTrue(app.tables.cells.staticTexts[entry].exists) + mozWaitForElementToExist(app.tables.cells.staticTexts[entry]) } - XCTAssertFalse(app.staticTexts["Today"].exists) - XCTAssertTrue(app.staticTexts["Older"].exists) + mozWaitForElementToNotExist(app.staticTexts["Today"]) + mozWaitForElementToExist(app.staticTexts["Older"]) // Begin Test for Today and Yesterday // Visit a page to create a recent history entry. @@ -451,8 +451,8 @@ class HistoryTests: BaseTestCase { for entry in oldHistoryEntries { XCTAssertTrue(app.tables.cells.staticTexts[entry].exists) } - XCTAssertFalse(app.staticTexts["Today"].exists) - XCTAssertTrue(app.staticTexts["Older"].exists) + mozWaitForElementToNotExist(app.staticTexts["Today"]) + mozWaitForElementToExist(app.staticTexts["Older"]) // Begin Test for Everything // Visit a page to create a recent history entry. @@ -462,26 +462,24 @@ class HistoryTests: BaseTestCase { tapOnClearRecentHistoryOption(optionSelected: "Everything") for entry in oldHistoryEntries { mozWaitForElementToNotExist(app.tables.cells.staticTexts[entry]) - - XCTAssertFalse(app.tables.cells.staticTexts[entry].exists, "History not removed") } - XCTAssertFalse(app.staticTexts["Today"].exists) - XCTAssertFalse(app.staticTexts["Older"].exists) + mozWaitForElementToNotExist(app.staticTexts["Today"]) + mozWaitForElementToNotExist(app.staticTexts["Older"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306890 + // https://mozilla.testrail.io/index.php?/cases/view/2306890 // Smoketest func testDeleteHistoryEntryBySwiping() { navigateToPage() navigator.goto(LibraryPanel_History) - waitForExistence(app.cells.staticTexts["http://example.com/"], timeout: TIMEOUT) + waitForExistence(app.cells.staticTexts["http://example.com/"]) navigateToPage() navigator.goto(LibraryPanel_History) - mozWaitForElementToExist(app.cells.staticTexts["http://example.com/"], timeout: TIMEOUT) + mozWaitForElementToExist(app.cells.staticTexts["http://example.com/"]) app.cells.staticTexts["http://example.com/"].firstMatch.swipeLeft() - mozWaitForElementToExist(app.buttons["Delete"], timeout: TIMEOUT) + mozWaitForElementToExist(app.buttons["Delete"]) app.buttons["Delete"].tap() mozWaitForElementToNotExist(app.staticTexts["http://example.com"]) - XCTAssertTrue(app.tables[HistoryPanelA11y.tableView].staticTexts[emptyRecentlyClosedMesg].exists) + mozWaitForElementToExist(app.tables[HistoryPanelA11y.tableView].staticTexts[emptyRecentlyClosedMesg]) } } diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/HomeButtonTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/HomeButtonTests.swift index a25307b3d5e0..0caf8ee5b1b0 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/HomeButtonTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/HomeButtonTests.swift @@ -10,15 +10,14 @@ class HomeButtonTests: BaseTestCase { super.tearDown() } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306925 + // https://mozilla.testrail.io/index.php?/cases/view/2306925 func testGoHome() throws { if iPad() { waitForTabsButton() navigator.nowAt(NewTabScreen) } navigator.openURL(path(forTestPage: "test-mozilla-org.html"), waitForLoading: true) - mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.homeButton], timeout: 10) - XCTAssertTrue(app.buttons[AccessibilityIdentifiers.Toolbar.homeButton].exists) + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.homeButton]) app.buttons[AccessibilityIdentifiers.Toolbar.homeButton].tap() navigator.nowAt(NewTabScreen) waitForTabsButton() @@ -27,8 +26,7 @@ class HomeButtonTests: BaseTestCase { navigator.nowAt(NewTabScreen) } navigator.openURL(path(forTestPage: "test-mozilla-book.html"), waitForLoading: true) - mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.homeButton], timeout: 5) - XCTAssertTrue(app.buttons[AccessibilityIdentifiers.Toolbar.homeButton].exists) + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.homeButton]) XCUIDevice.shared.orientation = .landscapeRight XCTAssertTrue(app.buttons["Home"].exists) @@ -36,22 +34,22 @@ class HomeButtonTests: BaseTestCase { navigator.nowAt(NewTabScreen) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306883 + // https://mozilla.testrail.io/index.php?/cases/view/2306883 func testSwitchHomepageKeyboardNotRaisedUp() { // Open a new tab and load a web page navigator.openURL("http://localhost:\(serverPort)/test-fixture/find-in-page-test.html") waitUntilPageLoad() // Switch to Homepage by taping the home button - mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.homeButton], timeout: 10) + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.homeButton]) app.buttons[AccessibilityIdentifiers.Toolbar.homeButton].tap() validateHomePageAndKeyboardNotRaisedUp() } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306881 + // https://mozilla.testrail.io/index.php?/cases/view/2306881 func testAppLaunchKeyboardNotRaisedUp() { - mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton], timeout: 5) + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton]) validateHomePageAndKeyboardNotRaisedUp() } diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/HomePageSettingsUITest.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/HomePageSettingsUITest.swift index fb472a05caad..a41f22693934 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/HomePageSettingsUITest.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/HomePageSettingsUITest.swift @@ -38,7 +38,7 @@ class HomePageSettingsUITests: BaseTestCase { super.setUp() } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2339256 + // https://mozilla.testrail.io/index.php?/cases/view/2339256 func testCheckHomeSettingsByDefault() { navigator.nowAt(NewTabScreen) navigator.goto(HomeSettings) @@ -54,7 +54,7 @@ class HomePageSettingsUITests: BaseTestCase { XCTAssertTrue(app.tables.cells["StartAtHomeAfterFourHours"].isSelected) // Include on Homepage - XCTAssertTrue(app.tables.cells["TopSitesSettings"].staticTexts["On"].exists) + mozWaitForElementToExist(app.tables.cells["TopSitesSettings"].staticTexts["On"]) let jumpBackIn = app.tables.cells.switches["Jump Back In"].value XCTAssertEqual("1", jumpBackIn as? String) let bookmarks = app.tables.cells.switches["Bookmarks"].value @@ -68,10 +68,10 @@ class HomePageSettingsUITests: BaseTestCase { // Current Homepage XCTAssertTrue(app.tables.cells["Firefox Home"].isSelected) - XCTAssertTrue(app.tables.cells["HomeAsCustomURL"].exists) + mozWaitForElementToExist(app.tables.cells["HomeAsCustomURL"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2339257 + // https://mozilla.testrail.io/index.php?/cases/view/2339257 func testTyping() { waitForTabsButton() navigator.nowAt(NewTabScreen) @@ -82,8 +82,7 @@ class HomePageSettingsUITests: BaseTestCase { // Check if it is saved going back and then again to home settings menu navigator.goto(SettingsScreen) navigator.goto(HomeSettings) - let valueAfter = app.textFields["HomeAsCustomURLTextField"].value - XCTAssertEqual(valueAfter as? String, "http://example.com") + mozWaitForValueContains(app.textFields["HomeAsCustomURLTextField"], value: "http://example.com") // Check that it is actually set by opening a different website and going to Home navigator.openURL(path(forTestPage: "test-mozilla-org.html")) @@ -91,13 +90,13 @@ class HomePageSettingsUITests: BaseTestCase { // Now check open home page should load the previously saved home page let homePageMenuItem = app.buttons[AccessibilityIdentifiers.Toolbar.homeButton] - mozWaitForElementToExist(homePageMenuItem, timeout: TIMEOUT) + mozWaitForElementToExist(homePageMenuItem) homePageMenuItem.tap() waitUntilPageLoad() mozWaitForValueContains(app.textFields["url"], value: "example") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2339258 + // https://mozilla.testrail.io/index.php?/cases/view/2339258 func testClipboard() { navigator.nowAt(NewTabScreen) // Check that what's in clipboard is copied @@ -119,11 +118,10 @@ class HomePageSettingsUITests: BaseTestCase { app.menuItems["Paste"].tap() mozWaitForValueContains(app.textFields["HomeAsCustomURLTextField"], value: "mozilla") // Check that the webpage has been correctly copied into the correct field - let value = app.textFields["HomeAsCustomURLTextField"].value as! String - XCTAssertEqual(value, websiteUrl1) + mozWaitForValueContains(app.textFields["HomeAsCustomURLTextField"], value: websiteUrl1) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2339260 + // https://mozilla.testrail.io/index.php?/cases/view/2339260 func testSetFirefoxHomeAsHome() { // Start by setting to History since FF Home is default waitForTabsButton() @@ -136,7 +134,7 @@ class HomePageSettingsUITests: BaseTestCase { waitUntilPageLoad() navigator.nowAt(BrowserTab) navigator.performAction(Action.GoToHomePage) - mozWaitForElementToExist(app.textFields["url"], timeout: TIMEOUT) + mozWaitForElementToExist(app.textFields["url"]) // Now after setting History, make sure FF home is set navigator.goto(SettingsScreen) @@ -146,7 +144,7 @@ class HomePageSettingsUITests: BaseTestCase { mozWaitForElementToExist(app.cells[AccessibilityIdentifiers.FirefoxHomepage.TopSites.itemCell]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307031 + // https://mozilla.testrail.io/index.php?/cases/view/2307031 func testSetCustomURLAsHome() { waitForTabsButton() navigator.nowAt(NewTabScreen) @@ -163,15 +161,14 @@ class HomePageSettingsUITests: BaseTestCase { // Workaround needed after Xcode 11.3 update Issue 5937 // Lets check only that website is open - mozWaitForElementToExist(app.textFields["url"], timeout: TIMEOUT) + mozWaitForElementToExist(app.textFields["url"]) mozWaitForValueContains(app.textFields["url"], value: "mozilla") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2339489 + // https://mozilla.testrail.io/index.php?/cases/view/2339489 func testDisableTopSitesSettingsRemovesSection() { mozWaitForElementToExist( - app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton], - timeout: TIMEOUT + app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton] ) navigator.nowAt(NewTabScreen) navigator.goto(HomeSettings) @@ -186,7 +183,7 @@ class HomePageSettingsUITests: BaseTestCase { mozWaitForElementToNotExist(app.collectionViews.cells.staticTexts["YouTube"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2339491 + // https://mozilla.testrail.io/index.php?/cases/view/2339491 func testChangeHomeSettingsLabel() { // Go to New Tab settings and select Custom URL option navigator.performAction(Action.SelectHomeAsCustomURL) @@ -195,11 +192,13 @@ class HomePageSettingsUITests: BaseTestCase { enterWebPageAsHomepage(text: websiteUrl1) mozWaitForValueContains(app.textFields["HomeAsCustomURLTextField"], value: "mozilla") navigator.goto(SettingsScreen) + mozWaitForElementToExist(app.tables.cells["Home"]) XCTAssertEqual(app.tables.cells["Home"].label, "Homepage, Custom") // Switch to FXHome and check label navigator.performAction(Action.SelectHomeAsFirefoxHomePage) navigator.nowAt(HomeSettings) navigator.goto(SettingsScreen) + mozWaitForElementToExist(app.tables.cells["Home"]) XCTAssertEqual(app.tables.cells["Home"].label, "Homepage, Firefox Home") } @@ -215,7 +214,7 @@ class HomePageSettingsUITests: BaseTestCase { XCTAssertEqual(numberOfTopSites, numberOfExpectedTopSites) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307033 + // https://mozilla.testrail.io/index.php?/cases/view/2307033 func testJumpBackIn() { navigator.openURL(path(forTestPage: exampleUrl)) waitUntilPageLoad() @@ -223,21 +222,17 @@ class HomePageSettingsUITests: BaseTestCase { navigator.performAction(Action.OpenNewTabFromTabTray) navigator.nowAt(NewTabScreen) if !iPad() { - mozWaitForElementToExist(app.buttons["urlBar-cancel"], timeout: 5) + mozWaitForElementToExist(app.buttons["urlBar-cancel"]) navigator.performAction(Action.CloseURLBarOpen) } mozWaitForElementToExist( - app.buttons[AccessibilityIdentifiers.FirefoxHomepage.MoreButtons.jumpBackIn], - timeout: 5 - ) - XCTAssertTrue( - app.otherElements - .cells[AccessibilityIdentifiers.FirefoxHomepage.JumpBackIn.itemCell] - .staticTexts[urlExampleLabel].exists + app.buttons[AccessibilityIdentifiers.FirefoxHomepage.MoreButtons.jumpBackIn] ) + mozWaitForElementToExist(app.otherElements + .cells[AccessibilityIdentifiers.FirefoxHomepage.JumpBackIn.itemCell] + .staticTexts[urlExampleLabel]) mozWaitForElementToExist( - app.buttons[AccessibilityIdentifiers.FirefoxHomepage.MoreButtons.jumpBackIn], - timeout: 5 + app.buttons[AccessibilityIdentifiers.FirefoxHomepage.MoreButtons.jumpBackIn] ) app.buttons[AccessibilityIdentifiers.FirefoxHomepage.MoreButtons.jumpBackIn].tap() // Tab tray is open with recently open tab @@ -245,8 +240,7 @@ class HomePageSettingsUITests: BaseTestCase { mozWaitForElementToExist( app.otherElements .cells[AccessibilityIdentifiers.FirefoxHomepage.JumpBackIn.itemCell] - .staticTexts[urlExampleLabel], - timeout: 3) + .staticTexts[urlExampleLabel]) } else { mozWaitForElementToExist(app.otherElements.cells[urlExampleLabel]) } @@ -260,28 +254,28 @@ class HomePageSettingsUITests: BaseTestCase { mozWaitForElementToNotExist(app.buttons[AccessibilityIdentifiers.FirefoxHomepage.MoreButtons.jumpBackIn]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307034 + // https://mozilla.testrail.io/index.php?/cases/view/2307034 func testRecentlySaved() { // Preconditons: Create 6 bookmarks & add 1 items to reading list bookmarkPages() addContentToReaderView() navigator.performAction(Action.GoToHomePage) - mozWaitForElementToExist(app.staticTexts["Recently Saved"]) + mozWaitForElementToExist(app.staticTexts["Bookmarks"]) navigator.performAction(Action.ToggleRecentlySaved) // On iPad we have the homepage button always present, // on iPhone we have the search button instead when we're on a new tab page navigator.performAction(Action.ClickSearchButton) - XCTAssertFalse( - app.scrollViews.cells[AccessibilityIdentifiers.FirefoxHomepage.MoreButtons.bookmarks].exists + mozWaitForElementToNotExist( + app.scrollViews.cells[AccessibilityIdentifiers.FirefoxHomepage.MoreButtons.bookmarks] ) - mozWaitForElementToExist(app.buttons["urlBar-cancel"], timeout: 3) + mozWaitForElementToExist(app.buttons["urlBar-cancel"]) navigator.performAction(Action.CloseURLBarOpen) navigator.nowAt(NewTabScreen) navigator.performAction(Action.ToggleRecentlySaved) navigator.nowAt(HomeSettings) navigator.performAction(Action.OpenNewTabFromTabTray) if !iPad() { - mozWaitForElementToExist(app.buttons["urlBar-cancel"], timeout: 3) + mozWaitForElementToExist(app.buttons["urlBar-cancel"]) navigator.performAction(Action.CloseURLBarOpen) } checkBookmarks() @@ -301,7 +295,7 @@ class HomePageSettingsUITests: BaseTestCase { checkBookmarksUpdated() } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306923 + // https://mozilla.testrail.io/index.php?/cases/view/2306923 // Smoketest // FXIOS-8107: Disabled test as history highlights has been disabled to fix app hangs / slowness // Reloads for notification @@ -357,16 +351,15 @@ class HomePageSettingsUITests: BaseTestCase { // swiftlint:enable line_length // } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306871 + // https://mozilla.testrail.io/index.php?/cases/view/2306871 // Smoketest func testCustomizeHomepage() { if !iPad() { - mozWaitForElementToExist(app.collectionViews["FxCollectionView"], timeout: TIMEOUT) + mozWaitForElementToExist(app.collectionViews["FxCollectionView"]) app.collectionViews["FxCollectionView"].swipeUp() app.collectionViews["FxCollectionView"].swipeUp() mozWaitForElementToExist( - app.cells.otherElements.buttons[AccessibilityIdentifiers.FirefoxHomepage.MoreButtons.customizeHomePage], - timeout: TIMEOUT + app.cells.otherElements.buttons[AccessibilityIdentifiers.FirefoxHomepage.MoreButtons.customizeHomePage] ) } app.cells.otherElements.buttons[AccessibilityIdentifiers.FirefoxHomepage.MoreButtons.customizeHomePage].tap() @@ -375,14 +368,14 @@ class HomePageSettingsUITests: BaseTestCase { app.navigationBars[AccessibilityIdentifiers.Settings.Homepage.homePageNavigationBar], timeout: TIMEOUT_LONG ) - XCTAssertTrue( - app.tables.cells[AccessibilityIdentifiers.Settings.Homepage.StartAtHome.always].exists + mozWaitForElementToExist( + app.tables.cells[AccessibilityIdentifiers.Settings.Homepage.StartAtHome.always] ) - XCTAssertTrue( - app.tables.cells[AccessibilityIdentifiers.Settings.Homepage.StartAtHome.disabled].exists + mozWaitForElementToExist( + app.tables.cells[AccessibilityIdentifiers.Settings.Homepage.StartAtHome.disabled] ) - XCTAssertTrue( - app.tables.cells[AccessibilityIdentifiers.Settings.Homepage.StartAtHome.afterFourHours].exists + mozWaitForElementToExist( + app.tables.cells[AccessibilityIdentifiers.Settings.Homepage.StartAtHome.afterFourHours] ) // Commented due to experimental features // XCTAssertEqual( @@ -406,7 +399,7 @@ class HomePageSettingsUITests: BaseTestCase { ) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307032 + // https://mozilla.testrail.io/index.php?/cases/view/2307032 func testShortcutsRows() { addWebsitesToShortcut(website: path(forTestPage: url_1)) addWebsitesToShortcut(website: path(forTestPage: url_2["url"]!)) diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/IntegrationTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/IntegrationTests.swift index 79d2ef4fab06..dd3a3f8fde1e 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/IntegrationTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/IntegrationTests.swift @@ -63,9 +63,10 @@ class IntegrationTests: BaseTestCase { userState.fxaPassword = ProcessInfo.processInfo.environment["FXA_PASSWORD"]! navigator.performAction(Action.FxATypeEmail) navigator.performAction(Action.FxATapOnContinueButton) + mozWaitForElementToExist(app.staticTexts["Enter your password"], timeout: TIMEOUT_LONG) navigator.performAction(Action.FxATypePasswordExistingAccount) navigator.performAction(Action.FxATapOnSignInButton) - sleep(3) + mozWaitForElementToNotExist(app.staticTexts["Enter your password"], timeout: TIMEOUT_LONG) waitForTabsButton() allowNotifications() } @@ -111,9 +112,9 @@ class IntegrationTests: BaseTestCase { // Bookmark is added by the DB // Sign into Mozilla Account navigator.openURL("example.com") - mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.trackingProtection], timeout: 5) + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.trackingProtection]) navigator.goto(BrowserTabMenu) - mozWaitForElementToExist(app.tables.otherElements[StandardImageIdentifiers.Large.bookmark], timeout: 15) + mozWaitForElementToExist(app.tables.otherElements[StandardImageIdentifiers.Large.bookmark]) app.tables.otherElements[StandardImageIdentifiers.Large.bookmark].tap() navigator.nowAt(BrowserTab) signInFxAccounts() @@ -129,7 +130,7 @@ class IntegrationTests: BaseTestCase { // Wait for initial sync to complete waitForInitialSyncComplete() navigator.goto(LibraryPanel_Bookmarks) - mozWaitForElementToExist(app.tables["Bookmarks List"].cells.staticTexts["Example Domain"], timeout: 5) + mozWaitForElementToExist(app.tables["Bookmarks List"].cells.staticTexts["Example Domain"]) } func testFxASyncTabs () { @@ -142,9 +143,9 @@ class IntegrationTests: BaseTestCase { navigator.nowAt(BrowserTab) // This is only to check that the device's name changed navigator.goto(SettingsScreen) - mozWaitForElementToExist(app.tables.cells.element(boundBy: 1), timeout: 10) + mozWaitForElementToExist(app.tables.cells.element(boundBy: 1)) app.tables.cells.element(boundBy: 1).tap() - mozWaitForElementToExist(app.cells["DeviceNameSetting"].textFields["DeviceNameSettingTextField"], timeout: 10) + mozWaitForElementToExist(app.cells["DeviceNameSetting"].textFields["DeviceNameSettingTextField"]) XCTAssertEqual( app.cells["DeviceNameSetting"].textFields["DeviceNameSettingTextField"].value! as! String, "Fennec (administrator) on iOS" @@ -152,7 +153,7 @@ class IntegrationTests: BaseTestCase { // Sync again just to make sure to sync after new name is shown app.buttons["Settings"].tap() - mozWaitForElementToExist(app.staticTexts["ACCOUNT"], timeout: TIMEOUT) + mozWaitForElementToExist(app.staticTexts["ACCOUNT"]) app.tables.cells.element(boundBy: 2).tap() mozWaitForElementToExist(app.tables.staticTexts["Sync Now"], timeout: TIMEOUT_LONG) } @@ -191,7 +192,7 @@ class IntegrationTests: BaseTestCase { // Check synced History navigator.goto(LibraryPanel_History) - mozWaitForElementToExist(app.tables.cells.staticTexts[historyItemSavedOnDesktop], timeout: 5) + mozWaitForElementToExist(app.tables.cells.staticTexts[historyItemSavedOnDesktop]) } func testFxASyncPasswordDesktop () { @@ -213,7 +214,7 @@ class IntegrationTests: BaseTestCase { passcodeInput.typeText("foo\n") navigator.goto(LoginsSettings) - mozWaitForElementToExist(app.tables["Login List"], timeout: 5) + mozWaitForElementToExist(app.tables["Login List"]) XCTAssertTrue(app.tables.cells.staticTexts[loginEntry].exists, "The login saved on desktop is not synced") } @@ -232,7 +233,7 @@ class IntegrationTests: BaseTestCase { // Need to swipe to get the data on the screen on focus app.swipeDown() - mozWaitForElementToExist(app.tables.otherElements["profile1"], timeout: TIMEOUT) + mozWaitForElementToExist(app.tables.otherElements["profile1"]) XCTAssertTrue(app.tables.staticTexts[tabOpenInDesktop].exists, "The tab is not synced") } @@ -245,8 +246,8 @@ class IntegrationTests: BaseTestCase { navigator.nowAt(SettingsScreen) // Check Bookmarks navigator.goto(LibraryPanel_Bookmarks) - mozWaitForElementToExist(app.tables["Bookmarks List"], timeout: 3) - mozWaitForElementToExist(app.tables["Bookmarks List"].cells.staticTexts["Example Domain"], timeout: 5) + mozWaitForElementToExist(app.tables["Bookmarks List"]) + mozWaitForElementToExist(app.tables["Bookmarks List"].cells.staticTexts["Example Domain"]) // Check Login navigator.performAction(Action.CloseBookmarkPanel) @@ -264,18 +265,18 @@ class IntegrationTests: BaseTestCase { passcodeInput.tap() passcodeInput.typeText("foo\n") - mozWaitForElementToExist(app.tables["Login List"], timeout: 3) + mozWaitForElementToExist(app.tables["Login List"]) // Verify the login mozWaitForElementToExist(app.staticTexts["https://accounts.google.com"]) // Disconnect account navigator.goto(SettingsScreen) app.tables.cells.element(boundBy: 1).tap() - mozWaitForElementToExist(app.cells["DeviceNameSetting"].textFields["DeviceNameSettingTextField"], timeout: 10) + mozWaitForElementToExist(app.cells["DeviceNameSetting"].textFields["DeviceNameSettingTextField"]) app.cells["SignOut"].tap() - mozWaitForElementToExist(app.buttons["Disconnect"], timeout: 5) + mozWaitForElementToExist(app.buttons["Disconnect"]) app.buttons["Disconnect"].tap() sleep(3) @@ -284,21 +285,21 @@ class IntegrationTests: BaseTestCase { app.tables.cells["SignInToSync"].tap() app.buttons["EmailSignIn.button"].tap() - mozWaitForElementToExist(app.secureTextFields.element(boundBy: 0), timeout: 10) + mozWaitForElementToExist(app.secureTextFields.element(boundBy: 0)) app.secureTextFields.element(boundBy: 0).tap() app.secureTextFields.element(boundBy: 0).typeText(userState.fxaPassword!) - mozWaitForElementToExist(app.webViews.buttons.element(boundBy: 0), timeout: 5) + mozWaitForElementToExist(app.webViews.buttons.element(boundBy: 0)) app.webViews.buttons.element(boundBy: 0).tap() navigator.nowAt(SettingsScreen) mozWaitForElementToExist(app.staticTexts["GENERAL"]) app.swipeDown() - mozWaitForElementToExist(app.staticTexts["ACCOUNT"], timeout: TIMEOUT) - mozWaitForElementToExist(app.tables.staticTexts["Sync Now"], timeout: 35) + mozWaitForElementToExist(app.staticTexts["ACCOUNT"]) + mozWaitForElementToExist(app.tables.staticTexts["Sync Now"], timeout: TIMEOUT_LONG) // Check Bookmarks navigator.goto(LibraryPanel_Bookmarks) - mozWaitForElementToExist(app.tables["Bookmarks List"].cells.staticTexts["Example Domain"], timeout: 5) + mozWaitForElementToExist(app.tables["Bookmarks List"].cells.staticTexts["Example Domain"]) // Check Logins navigator.performAction(Action.CloseBookmarkPanel) @@ -310,7 +311,7 @@ class IntegrationTests: BaseTestCase { passcodeInput.tap() passcodeInput.typeText("foo\n") - mozWaitForElementToExist(app.tables["Login List"], timeout: 10) - mozWaitForElementToExist(app.staticTexts["https://accounts.google.com"], timeout: 10) + mozWaitForElementToExist(app.tables["Login List"]) + mozWaitForElementToExist(app.staticTexts["https://accounts.google.com"]) } } diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/JumpBackInTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/JumpBackInTests.swift index 51888b4837ed..5a1336a5e5a1 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/JumpBackInTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/JumpBackInTests.swift @@ -26,12 +26,13 @@ class JumpBackInTests: BaseTestCase { // "Jump Back In" is enabled by default. See Settings -> Homepage navigator.goto(HomeSettings) + mozWaitForElementToExist(app.switches["Jump Back In"]) XCTAssertEqual(app.switches["Jump Back In"].value as! String, "1") navigator.goto(NewTabScreen) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306922 + // https://mozilla.testrail.io/index.php?/cases/view/2306922 func testJumpBackInSection() { // Open a tab and visit a page navigator.openURL("https://www.example.com") @@ -43,12 +44,12 @@ class JumpBackInTests: BaseTestCase { closeKeyboard() // "Jump Back In" section is displayed - mozWaitForElementToExist(app.cells["JumpBackInCell"].firstMatch, timeout: TIMEOUT) + mozWaitForElementToExist(app.cells["JumpBackInCell"].firstMatch) // The contextual hint box is not displayed consistently, so // I don't test for its existence. } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306920 + // https://mozilla.testrail.io/index.php?/cases/view/2306920 // Smoketest func testPrivateTab() throws { throw XCTSkip("This test is flaky") @@ -146,13 +147,13 @@ class JumpBackInTests: BaseTestCase { // mozWaitForElementToNotExist(app.cells["JumpBackInCell"].staticTexts["YouTube"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2445811 + // https://mozilla.testrail.io/index.php?/cases/view/2445811 func testLongTapOnJumpBackInLink() { // On homepage, go to the "Jump back in" section and long tap on one of the links navigator.openURL(path(forTestPage: "test-example.html")) mozWaitForElementToExist(app.webViews.links[website_2["link"]!], timeout: TIMEOUT_LONG) app.webViews.links[website_2["link"]!].press(forDuration: 2) - mozWaitForElementToExist(app.otherElements.collectionViews.element(boundBy: 0), timeout: TIMEOUT) + mozWaitForElementToExist(app.otherElements.collectionViews.element(boundBy: 0)) mozWaitForElementToExist(app.buttons["Open in New Tab"]) app.buttons["Open in New Tab"].tap() waitUntilPageLoad() @@ -166,6 +167,6 @@ class JumpBackInTests: BaseTestCase { mozWaitForElementToExist(ContextMenuTable.cells.otherElements[StandardImageIdentifiers.Large.plus]) mozWaitForElementToExist(ContextMenuTable.cells.otherElements[StandardImageIdentifiers.Large.privateMode]) mozWaitForElementToExist(ContextMenuTable.cells.otherElements[StandardImageIdentifiers.Large.bookmark]) - mozWaitForElementToExist(ContextMenuTable.cells.otherElements[StandardImageIdentifiers.Large.shareApple]) + mozWaitForElementToExist(ContextMenuTable.cells.otherElements[StandardImageIdentifiers.Large.share]) } } diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/LoginsTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/LoginsTests.swift index 6bc75170b8fb..eafe906dd237 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/LoginsTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/LoginsTests.swift @@ -28,15 +28,15 @@ class LoginTest: BaseTestCase { private func saveLogin(givenUrl: String) { navigator.openURL(givenUrl) waitUntilPageLoad() - mozWaitForElementToExist(app.buttons["submit"], timeout: 10) + mozWaitForElementToExist(app.buttons["submit"]) app.buttons["submit"].tap() - mozWaitForElementToExist(app.buttons["SaveLoginPrompt.saveLoginButton"], timeout: 10) + mozWaitForElementToExist(app.buttons["SaveLoginPrompt.saveLoginButton"]) app.buttons["SaveLoginPrompt.saveLoginButton"].tap() } private func openLoginsSettings() { navigator.goto(SettingsScreen) - mozWaitForElementToExist(app.cells["SignInToSync"], timeout: 5) + mozWaitForElementToExist(app.cells["SignInToSync"]) app.cells["SignInToSync"].swipeUp() navigator.goto(LoginsSettings) @@ -45,9 +45,9 @@ class LoginTest: BaseTestCase { } private func openLoginsSettingsFromBrowserTab() { - waitForExistence(app.buttons["TabToolbar.menuButton"], timeout: TIMEOUT) + waitForExistence(app.buttons["TabToolbar.menuButton"]) navigator.goto(BrowserTabMenu) - waitForExistence(app.tables.otherElements[StandardImageIdentifiers.Large.login], timeout: 5) + waitForExistence(app.tables.otherElements[StandardImageIdentifiers.Large.login]) navigator.goto(LoginsSettings) unlockLoginsView() @@ -55,14 +55,14 @@ class LoginTest: BaseTestCase { navigator.nowAt(LoginsSettings) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306961 + // https://mozilla.testrail.io/index.php?/cases/view/2306961 func testLoginsListFromBrowserTabMenu() { closeURLBar() // Make sure you can access empty Login List from Browser Tab Menu navigator.goto(LoginsSettings) unlockLoginsView() mozWaitForElementToExist(app.tables["Login List"]) - XCTAssertTrue(app.searchFields[searchPasswords].exists) + mozWaitForElementToExist(app.searchFields[searchPasswords]) XCTAssertEqual(app.tables["Login List"].cells.count, defaultNumRowsLoginsList) app.buttons["Settings"].tap() navigator.performAction(Action.OpenNewTabFromTabTray) @@ -71,13 +71,13 @@ class LoginTest: BaseTestCase { navigator.goto(LoginsSettings) unlockLoginsView() mozWaitForElementToExist(app.tables["Login List"]) - XCTAssertTrue(app.searchFields[searchPasswords].exists) - XCTAssertTrue(app.staticTexts[domain].exists) - XCTAssertTrue(app.staticTexts[domainLogin].exists) + mozWaitForElementToExist(app.searchFields[searchPasswords]) + mozWaitForElementToExist(app.staticTexts[domain]) + mozWaitForElementToExist(app.staticTexts[domainLogin]) XCTAssertEqual(app.tables["Login List"].cells.count, defaultNumRowsLoginsList + 1) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306951 + // https://mozilla.testrail.io/index.php?/cases/view/2306951 // Smoketest func testSaveLogin() { closeURLBar() @@ -94,7 +94,7 @@ class LoginTest: BaseTestCase { saveLogin(givenUrl: testLoginPage) openLoginsSettings() mozWaitForElementToExist(app.tables["Login List"]) - XCTAssertTrue(app.staticTexts[domain].exists) + mozWaitForElementToExist(app.staticTexts[domain]) // XCTAssertTrue(app.staticTexts[domainLogin].exists) XCTAssertEqual(app.tables["Login List"].cells.count, defaultNumRowsLoginsList + 1) @@ -111,7 +111,7 @@ class LoginTest: BaseTestCase { saveLogin(givenUrl: testSecondLoginPage) openLoginsSettings() mozWaitForElementToExist(app.tables["Login List"]) - XCTAssertTrue(app.staticTexts[domain].exists) + mozWaitForElementToExist(app.staticTexts[domain]) // XCTAssertTrue(app.staticTexts[domainSecondLogin].exists) // Workaround for Bitrise specific issue. "vagrant" user is used in Bitrise. if (ProcessInfo.processInfo.environment["HOME"]!).contains(String("vagrant")) { @@ -122,7 +122,7 @@ class LoginTest: BaseTestCase { } } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306965 + // https://mozilla.testrail.io/index.php?/cases/view/2306965 func testDoNotSaveLogin() { navigator.openURL(testLoginPage) waitUntilPageLoad() @@ -130,33 +130,31 @@ class LoginTest: BaseTestCase { app.buttons["SaveLoginPrompt.dontSaveButton"].tap() // There should not be any login saved openLoginsSettings() - XCTAssertFalse(app.staticTexts[domain].exists) - XCTAssertFalse(app.staticTexts[domainLogin].exists) + mozWaitForElementToNotExist(app.staticTexts[domain]) + mozWaitForElementToNotExist(app.staticTexts[domainLogin]) XCTAssertEqual(app.tables["Login List"].cells.count, defaultNumRowsLoginsList) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306962 + // https://mozilla.testrail.io/index.php?/cases/view/2306962 func testSavedLoginSelectUnselect() { saveLogin(givenUrl: testLoginPage) navigator.goto(SettingsScreen) openLoginsSettings() - XCTAssertTrue(app.staticTexts[domain].exists) - XCTAssertTrue(app.staticTexts[domainLogin].exists) + mozWaitForElementToExist(app.staticTexts[domain]) + mozWaitForElementToExist(app.staticTexts[domainLogin]) XCTAssertTrue(app.buttons["Edit"].isHittable) app.buttons["Edit"].tap() - XCTAssertTrue(app.buttons["Select All"].exists) - XCTAssertTrue(app.staticTexts[domain].exists) - XCTAssertTrue(app.staticTexts[domainLogin].exists) + mozWaitForElementToExist(app.buttons["Select All"]) + mozWaitForElementToExist(app.staticTexts[domain]) + mozWaitForElementToExist(app.staticTexts[domainLogin]) app.staticTexts[domain].tap() mozWaitForElementToExist(app.buttons["Deselect All"]) - - XCTAssertTrue(app.buttons["Deselect All"].exists) - XCTAssertTrue(app.buttons["Delete"].exists) + mozWaitForElementToExist(app.buttons["Delete"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306963 + // https://mozilla.testrail.io/index.php?/cases/view/2306963 func testDeleteLogin() { saveLogin(givenUrl: testLoginPage) openLoginsSettings() @@ -167,12 +165,12 @@ class LoginTest: BaseTestCase { mozWaitForElementToExist(app.alerts["Remove Password?"]) app.alerts.buttons["Remove"].tap() mozWaitForElementToExist(app.tables["Login List"]) - XCTAssertFalse(app.staticTexts[domain].exists) - XCTAssertFalse(app.staticTexts[domainLogin].exists) + mozWaitForElementToNotExist(app.staticTexts[domain]) + mozWaitForElementToNotExist(app.staticTexts[domainLogin]) XCTAssertEqual(app.tables["Login List"].cells.count, defaultNumRowsLoginsList) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306966 + // https://mozilla.testrail.io/index.php?/cases/view/2306966 func testEditOneLoginEntry() { // Go to test login page and save the login: test-password.html saveLogin(givenUrl: testLoginPage) @@ -183,10 +181,10 @@ class LoginTest: BaseTestCase { app.staticTexts[domain].tap() // The login details are available waitForExistence(app.tables["Login Detail List"]) - XCTAssertTrue(app.tables.cells[loginsListURLLabel].exists) - XCTAssertTrue(app.tables.cells[loginsListUsernameLabel].exists) - XCTAssertTrue(app.tables.cells[loginsListPasswordLabel].exists) - XCTAssertTrue(app.tables.cells.staticTexts["Delete"].exists) + mozWaitForElementToExist(app.tables.cells[loginsListURLLabel]) + mozWaitForElementToExist(app.tables.cells[loginsListUsernameLabel]) + mozWaitForElementToExist(app.tables.cells[loginsListPasswordLabel]) + mozWaitForElementToExist(app.tables.cells.staticTexts["Delete"]) // Change the username app.buttons["Edit"].tap() mozWaitForElementToExist(app.tables["Login Detail List"]) @@ -200,13 +198,13 @@ class LoginTest: BaseTestCase { app.buttons["Done"].tap() // The username is correctly changed mozWaitForElementToExist(app.tables["Login Detail List"]) - XCTAssertTrue(app.tables.cells[loginsListURLLabel].exists) - XCTAssertFalse(app.tables.cells[loginsListUsernameLabel].exists) - XCTAssertTrue(app.tables.cells[loginsListUsernameLabelEdited].exists) - XCTAssertTrue(app.tables.cells[loginsListPasswordLabel].exists) + mozWaitForElementToExist(app.tables.cells[loginsListURLLabel]) + mozWaitForElementToNotExist(app.tables.cells[loginsListUsernameLabel]) + mozWaitForElementToExist(app.tables.cells[loginsListUsernameLabelEdited]) + mozWaitForElementToExist(app.tables.cells[loginsListPasswordLabel]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306964 + // https://mozilla.testrail.io/index.php?/cases/view/2306964 func testSearchLogin() { saveLogin(givenUrl: testLoginPage) openLoginsSettings() @@ -228,13 +226,13 @@ class LoginTest: BaseTestCase { XCTAssertEqual(app.tables["Login List"].cells.count, defaultNumRowsLoginsList + 1) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306952 + // https://mozilla.testrail.io/index.php?/cases/view/2306952 // Smoketest func testSavedLoginAutofilled() { navigator.openURL(urlLogin) waitUntilPageLoad() // Provided text fields are completely empty - mozWaitForElementToExist(app.webViews.staticTexts["Username:"], timeout: 15) + mozWaitForElementToExist(app.webViews.staticTexts["Username:"]) // Fill in the username text box app.webViews.textFields.element(boundBy: 0).tap() @@ -245,7 +243,7 @@ class LoginTest: BaseTestCase { // Submit form and choose to save the logins app.buttons["submit"].tap() - mozWaitForElementToExist(app.buttons["SaveLoginPrompt.saveLoginButton"], timeout: 5) + mozWaitForElementToExist(app.buttons["SaveLoginPrompt.saveLoginButton"]) app.buttons["SaveLoginPrompt.saveLoginButton"].tap() // Clear Data and go to test page, fields should be filled in @@ -256,20 +254,20 @@ class LoginTest: BaseTestCase { navigator.performAction(Action.OpenNewTabFromTabTray) navigator.openURL(urlLogin) waitUntilPageLoad() - mozWaitForElementToExist(app.webViews.textFields.element(boundBy: 0), timeout: 3) + mozWaitForElementToExist(app.webViews.textFields.element(boundBy: 0)) // let emailValue = app.webViews.textFields.element(boundBy: 0).value! // XCTAssertEqual(emailValue as! String, mailLogin) // let passwordValue = app.webViews.secureTextFields.element(boundBy: 0).value! // XCTAssertEqual(passwordValue as! String, "••••••••") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306953 + // https://mozilla.testrail.io/index.php?/cases/view/2306953 // Smoketest func testCreateLoginManually() { closeURLBar() navigator.goto(LoginsSettings) unlockLoginsView() - mozWaitForElementToExist(app.tables["Login List"], timeout: 15) + mozWaitForElementToExist(app.tables["Login List"]) mozWaitForElementToExist(app.navigationBars["Passwords"]) mozWaitForElementToExist(app.staticTexts["No passwords found"]) mozWaitForElementToExist(app.buttons["Add"]) @@ -280,14 +278,14 @@ class LoginTest: BaseTestCase { mozWaitForElementToExist(app.tables["Login List"].staticTexts["https://testweb"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306954 + // https://mozilla.testrail.io/index.php?/cases/view/2306954 func testAddDuplicateLogin() { // Add login credential openLoginsSettingsFromBrowserTab() createLoginManually() // The login is correctly created. - XCTAssertTrue(app.tables["Login List"].staticTexts["https://testweb"].exists) - XCTAssertTrue(app.tables["Login List"].staticTexts["foo"].exists) + mozWaitForElementToExist(app.tables["Login List"].staticTexts["https://testweb"]) + mozWaitForElementToExist(app.tables["Login List"].staticTexts["foo"]) // Repeat previous step, adding the same login createLoginManually() // The login cannot be duplicated @@ -297,8 +295,8 @@ class LoginTest: BaseTestCase { private func createLoginManually() { app.buttons["Add"].tap() - mozWaitForElementToExist(app.tables["Add Credential"], timeout: 15) - XCTAssertTrue(app.tables["Add Credential"].cells.staticTexts.containingText("Web").element.exists) + mozWaitForElementToExist(app.tables["Add Credential"]) + mozWaitForElementToExist(app.tables["Add Credential"].cells.staticTexts.containingText("Web").element) mozWaitForElementToExist(app.tables["Add Credential"].cells.staticTexts["Username"]) mozWaitForElementToExist(app.tables["Add Credential"].cells.staticTexts["Password"]) diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/MicrosurveyTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/MicrosurveyTests.swift index b54313b241d8..bcddf3142ffb 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/MicrosurveyTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/MicrosurveyTests.swift @@ -15,9 +15,9 @@ final class MicrosurveyTests: BaseTestCase { func testShowMicrosurveyPromptFromHomepageTrigger() { generateTriggerForMicrosurvey() - XCTAssertTrue(app.buttons[AccessibilityIdentifiers.Microsurvey.Prompt.takeSurveyButton].exists) - XCTAssertTrue(app.images[AccessibilityIdentifiers.Microsurvey.Prompt.firefoxLogo].exists) - XCTAssertTrue(app.buttons[AccessibilityIdentifiers.Microsurvey.Prompt.closeButton].exists) + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Microsurvey.Prompt.takeSurveyButton]) + mozWaitForElementToExist(app.images[AccessibilityIdentifiers.Microsurvey.Prompt.firefoxLogo]) + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Microsurvey.Prompt.closeButton]) } func testURLBorderHiddenWhenMicrosurveyPromptShown() throws { @@ -40,9 +40,9 @@ final class MicrosurveyTests: BaseTestCase { func testCloseButtonDismissesMicrosurveyPrompt() { generateTriggerForMicrosurvey() app.buttons[AccessibilityIdentifiers.Microsurvey.Prompt.closeButton].tap() - XCTAssertFalse(app.buttons[AccessibilityIdentifiers.Microsurvey.Prompt.takeSurveyButton].exists) - XCTAssertFalse(app.images[AccessibilityIdentifiers.Microsurvey.Prompt.firefoxLogo].exists) - XCTAssertFalse(app.images[AccessibilityIdentifiers.Microsurvey.Prompt.closeButton].exists) + mozWaitForElementToNotExist(app.buttons[AccessibilityIdentifiers.Microsurvey.Prompt.takeSurveyButton]) + mozWaitForElementToNotExist(app.images[AccessibilityIdentifiers.Microsurvey.Prompt.firefoxLogo]) + mozWaitForElementToNotExist(app.images[AccessibilityIdentifiers.Microsurvey.Prompt.closeButton]) } func testShowMicrosurvey() { @@ -50,8 +50,8 @@ final class MicrosurveyTests: BaseTestCase { let continueButton = app.buttons[AccessibilityIdentifiers.Microsurvey.Prompt.takeSurveyButton] continueButton.tap() - XCTAssertTrue(app.images[AccessibilityIdentifiers.Microsurvey.Survey.firefoxLogo].exists) - XCTAssertTrue(app.buttons[AccessibilityIdentifiers.Microsurvey.Survey.closeButton].exists) + mozWaitForElementToExist(app.images[AccessibilityIdentifiers.Microsurvey.Survey.firefoxLogo]) + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Microsurvey.Survey.closeButton]) let tablesQuery = app.scrollViews.otherElements.tables let firstOption = tablesQuery.cells.firstMatch firstOption.tap() @@ -72,11 +72,11 @@ final class MicrosurveyTests: BaseTestCase { app.buttons[AccessibilityIdentifiers.Microsurvey.Survey.closeButton].tap() - XCTAssertFalse(app.images[AccessibilityIdentifiers.Microsurvey.Survey.firefoxLogo].exists) - XCTAssertFalse(app.buttons[AccessibilityIdentifiers.Microsurvey.Survey.closeButton].exists) - XCTAssertFalse(app.buttons[AccessibilityIdentifiers.Microsurvey.Prompt.takeSurveyButton].exists) - XCTAssertFalse(app.images[AccessibilityIdentifiers.Microsurvey.Prompt.firefoxLogo].exists) - XCTAssertFalse(app.images[AccessibilityIdentifiers.Microsurvey.Prompt.closeButton].exists) + mozWaitForElementToNotExist(app.images[AccessibilityIdentifiers.Microsurvey.Survey.firefoxLogo]) + mozWaitForElementToNotExist(app.buttons[AccessibilityIdentifiers.Microsurvey.Survey.closeButton]) + mozWaitForElementToNotExist(app.buttons[AccessibilityIdentifiers.Microsurvey.Prompt.takeSurveyButton]) + mozWaitForElementToNotExist(app.images[AccessibilityIdentifiers.Microsurvey.Prompt.firefoxLogo]) + mozWaitForElementToNotExist(app.images[AccessibilityIdentifiers.Microsurvey.Prompt.closeButton]) } private func generateTriggerForMicrosurvey() { diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/MultiWindowTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/MultiWindowTests.swift new file mode 100644 index 000000000000..5666e3844f76 --- /dev/null +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/MultiWindowTests.swift @@ -0,0 +1,82 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Common + +class MultiWindowTests: IpadOnlyTestCase { + let dotMenu = springboard.buttons["top-affordance:org.mozilla.ios.Fennec"] + let splitView = springboard.buttons["top-affordance-split-view-button"] + let dotMenuIdentifier = springboard.buttons.matching(identifier: "top-affordance:org.mozilla.ios.Fennec") + + override func setUp() { + super.setUp() + super.setUpLaunchArguments() + if dotMenuIdentifier.element(boundBy: 1).exists { + closeSplitViewWindow(windowToClose: 1) + } + } + + override func tearDown() { + if dotMenuIdentifier.element(boundBy: 1).exists { + closeSplitViewWindow(windowToClose: 1) + } + super.tearDown() + } + + func testMultiWindowFromHomeScreen() { + if skipPlatform { return } + dismissSurveyPrompt() + splitViewFromHomeScreen() + XCTAssertEqual(dotMenuIdentifier.count, 2, "There are not 2 instances opened") + // Tap menu button on first and second window + let menuButton = AccessibilityIdentifiers.Toolbar.settingsMenuButton + mozWaitForElementToExist(app.buttons.matching(identifier: menuButton).element(boundBy: 0)) + app.buttons.matching(identifier: menuButton).element(boundBy: 0).tap() + mozWaitForElementToExist(app.buttons.matching(identifier: menuButton).element(boundBy: 1)) + app.buttons.matching(identifier: menuButton).element(boundBy: 1).tap() + // Tap on settings on first and second window + let settings = StandardImageIdentifiers.Large.settings + let settingsOption = app.tables.otherElements.matching(identifier: settings).element(boundBy: 0) + settingsOption.tap() + settingsOption.tap() + } + + func testOpenWindowFromTabSwitcher() { + if skipPlatform { return } + openWindowFromTabSwitcher(windowsNumber: 1) + // selectTabFromSwitcher() + } + + private func splitViewFromHomeScreen() { + mozWaitForElementToExist(dotMenu) + dotMenu.tap() + mozWaitForElementToExist(splitView) + splitView.tap() + springboard.icons.elementContainingText("split view with Fennec").tap() + } + + // Param windowsNumber - number of tab windows to open from switcher + private func openWindowFromTabSwitcher(windowsNumber: Int) { + for _ in 1...windowsNumber { + mozWaitForElementToExist(dotMenu) + dotMenu.tap() + let cardOrgMozillaIosFennecButton = springboard.buttons["card:org.mozilla.ios.Fennec:"] + cardOrgMozillaIosFennecButton.tap() + } + } + + // Param windowToClose - 0 for the first window, 1 for the second window + func closeSplitViewWindow(windowToClose: Int) { + mozWaitForElementToExist(dotMenuIdentifier.element(boundBy: windowToClose)) + dotMenuIdentifier.element(boundBy: windowToClose).tap() + mozWaitForElementToExist(springboard.buttons["top-affordance-close-window"]) + springboard.buttons["top-affordance-close-window"].tap() + } + + // Coudn't find a way to select a tab from switcher +// private func selectTabFromSwitcher() { +// let tabIdentifier = "card:org.mozilla.ios.Fennec:sceneID:org.mozilla.ios.Fennec" +// } +} diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/NavigationTest.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/NavigationTest.swift index ddcbb7c8a0d1..8c893bf85f0e 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/NavigationTest.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/NavigationTest.swift @@ -21,10 +21,10 @@ let website_2 = [ let popUpTestUrl = path(forTestPage: "test-popup-blocker.html") class NavigationTest: BaseTestCase { - // https://testrail.stage.mozaws.net/index.php?/cases/view/2441488 + // https://mozilla.testrail.io/index.php?/cases/view/2441488 func testNavigation() { let urlPlaceholder = "Search or enter address" - XCTAssert(app.textFields["url"].exists) + mozWaitForElementToExist(app.textFields["url"]) let defaultValuePlaceholder = app.textFields["url"].placeholderValue! // Check the url placeholder text and that the back and forward buttons are disabled @@ -67,7 +67,7 @@ class NavigationTest: BaseTestCase { mozWaitForValueContains(app.textFields["url"], value: "test-mozilla-org") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2441489 + // https://mozilla.testrail.io/index.php?/cases/view/2441489 func testTapSignInShowsFxAFromTour() { waitForTabsButton() navigator.nowAt(NewTabScreen) @@ -78,7 +78,7 @@ class NavigationTest: BaseTestCase { mozWaitForElementToExist(app.webViews.staticTexts["Continue to your Mozilla account"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2441493 + // https://mozilla.testrail.io/index.php?/cases/view/2441493 func testTapSigninShowsFxAFromSettings() { waitForTabsButton() navigator.nowAt(NewTabScreen) @@ -101,7 +101,7 @@ class NavigationTest: BaseTestCase { closeButtonFxView.tap() } - // Because the Settings menu does not stretch tot the top we need a different function to check + // Because the Settings menu does not stretch tot the top we need a different function to check // if the Firefox Sync screen is shown private func checkFirefoxSyncScreenShownViaSettings() { mozWaitForElementToExist( @@ -118,7 +118,7 @@ class NavigationTest: BaseTestCase { XCTAssertEqual(mailPlaceholder, defaultMailPlaceholder, "The mail placeholder does not show the correct value") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2441494 + // https://mozilla.testrail.io/index.php?/cases/view/2441494 func testTapSignInShowsFxAFromRemoteTabPanel() { waitForTabsButton() navigator.nowAt(NewTabScreen) @@ -131,7 +131,7 @@ class NavigationTest: BaseTestCase { mozWaitForElementToExist(app.buttons["Use Email Instead"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2441495 + // https://mozilla.testrail.io/index.php?/cases/view/2441495 func testScrollsToTopWithMultipleTabs() { navigator.goto(TabTray) navigator.openURL(website_1["url"]!) @@ -148,19 +148,19 @@ class NavigationTest: BaseTestCase { mozWaitForElementToExist(topElement) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306836 + // https://mozilla.testrail.io/index.php?/cases/view/2306836 // Smoketest func testLongPressLinkOptions() { openContextMenuForArticleLink() - XCTAssertTrue(app.buttons["Open in New Tab"].exists, "The option is not shown") - XCTAssertTrue(app.buttons["Open in New Private Tab"].exists, "The option is not shown") - XCTAssertTrue(app.buttons["Copy Link"].exists, "The option is not shown") - XCTAssertTrue(app.buttons["Download Link"].exists, "The option is not shown") - XCTAssertTrue(app.buttons["Share Link"].exists, "The option is not shown") - XCTAssertTrue(app.buttons["Bookmark Link"].exists, "The option is not shown") + mozWaitForElementToExist(app.buttons["Open in New Tab"]) + mozWaitForElementToExist(app.buttons["Open in New Private Tab"]) + mozWaitForElementToExist(app.buttons["Copy Link"]) + mozWaitForElementToExist(app.buttons["Download Link"]) + mozWaitForElementToExist(app.buttons["Share Link"]) + mozWaitForElementToExist(app.buttons["Bookmark Link"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2441496 + // https://mozilla.testrail.io/index.php?/cases/view/2441496 func testCopyLink() { longPressLinkOptions(optionSelected: "Copy Link") navigator.goto(NewTabScreen) @@ -173,7 +173,7 @@ class NavigationTest: BaseTestCase { mozWaitForValueContains(app.textFields["url"], value: website_2["moreLinkLongPressInfo"]!) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2441497 + // https://mozilla.testrail.io/index.php?/cases/view/2441497 func testCopyLinkPrivateMode() { navigator.nowAt(NewTabScreen) navigator.toggleOn(userState.isPrivate, withAction: Action.TogglePrivateMode) @@ -189,7 +189,7 @@ class NavigationTest: BaseTestCase { mozWaitForValueContains(app.textFields["url"], value: website_2["moreLinkLongPressInfo"]!) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2441923 + // https://mozilla.testrail.io/index.php?/cases/view/2441923 func testLongPressOnAddressBar() throws { // Long press on the URL requires copy & paste permission throw XCTSkip("Test needs to be updated") @@ -267,7 +267,7 @@ class NavigationTest: BaseTestCase { app.buttons[optionSelected].tap() } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2441498 + // https://mozilla.testrail.io/index.php?/cases/view/2441498 func testDownloadLink() { longPressLinkOptions(optionSelected: "Download Link") mozWaitForElementToExist(app.tables["Context Menu"]) @@ -279,7 +279,7 @@ class NavigationTest: BaseTestCase { // There should be one item downloaded. It's name and size should be shown let downloadedList = app.tables["DownloadsTable"].cells.count XCTAssertEqual(downloadedList, 1, "The number of items in the downloads table is not correct") - XCTAssertTrue(app.tables.cells.staticTexts["example-domains.html"].exists) + mozWaitForElementToExist(app.tables.cells.staticTexts["example-domains.html"]) // Tap on the just downloaded link to check that the web page is loaded app.tables.cells.staticTexts["example-domains.html"].tap() @@ -287,45 +287,45 @@ class NavigationTest: BaseTestCase { mozWaitForValueContains(app.textFields["url"], value: "example-domains.html") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2441499 + // https://mozilla.testrail.io/index.php?/cases/view/2441499 func testShareLink() { longPressLinkOptions(optionSelected: "Share Link") if #available(iOS 16, *) { - mozWaitForElementToExist(app.cells["Copy"], timeout: TIMEOUT) + mozWaitForElementToExist(app.cells["Copy"]) } else { - mozWaitForElementToExist(app.buttons["Copy"], timeout: TIMEOUT) + mozWaitForElementToExist(app.buttons["Copy"]) } if !iPad() { - mozWaitForElementToExist(app.scrollViews.staticTexts["Messages"], timeout: TIMEOUT) + mozWaitForElementToExist(app.scrollViews.staticTexts["Messages"]) } if #unavailable(iOS 17) { - mozWaitForElementToExist(app.scrollViews.cells["XCElementSnapshotPrivilegedValuePlaceholder"], timeout: TIMEOUT) + mozWaitForElementToExist(app.scrollViews.cells["XCElementSnapshotPrivilegedValuePlaceholder"]) } else { - mozWaitForElementToExist(app.scrollViews.staticTexts["Reminders"], timeout: TIMEOUT) + mozWaitForElementToExist(app.scrollViews.staticTexts["Reminders"]) } } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2441500 + // https://mozilla.testrail.io/index.php?/cases/view/2441500 func testShareLinkPrivateMode() { navigator.nowAt(NewTabScreen) navigator.toggleOn(userState.isPrivate, withAction: Action.TogglePrivateMode) longPressLinkOptions(optionSelected: "Share Link") if #available(iOS 16, *) { - mozWaitForElementToExist(app.cells["Copy"], timeout: TIMEOUT) + mozWaitForElementToExist(app.cells["Copy"]) } else { - mozWaitForElementToExist(app.buttons["Copy"], timeout: TIMEOUT) + mozWaitForElementToExist(app.buttons["Copy"]) } if !iPad() { - mozWaitForElementToExist(app.scrollViews.staticTexts["Messages"], timeout: TIMEOUT) + mozWaitForElementToExist(app.scrollViews.staticTexts["Messages"]) } if #unavailable(iOS 17) { - mozWaitForElementToExist(app.scrollViews.cells["XCElementSnapshotPrivilegedValuePlaceholder"], timeout: TIMEOUT) + mozWaitForElementToExist(app.scrollViews.cells["XCElementSnapshotPrivilegedValuePlaceholder"]) } else { - mozWaitForElementToExist(app.scrollViews.staticTexts["Reminders"], timeout: TIMEOUT) + mozWaitForElementToExist(app.scrollViews.staticTexts["Reminders"]) } } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2441776 + // https://mozilla.testrail.io/index.php?/cases/view/2441776 // Smoketest func testPopUpBlocker() throws { throw XCTSkip("This test is flakey") @@ -362,13 +362,13 @@ class NavigationTest: BaseTestCase { // XCTAssertNotEqual("1", numTabsAfter as? String, "Several tabs are open") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306858 + // https://mozilla.testrail.io/index.php?/cases/view/2306858 // Smoketest func testSSL() { navigator.nowAt(NewTabScreen) navigator.openURL("https://expired.badssl.com/") - mozWaitForElementToExist(app.buttons["Advanced"], timeout: TIMEOUT) + mozWaitForElementToExist(app.buttons["Advanced"], timeout: TIMEOUT_LONG) app.buttons["Advanced"].tap() mozWaitForElementToExist(app.links["Visit site anyway"]) @@ -376,7 +376,7 @@ class NavigationTest: BaseTestCase { mozWaitForElementToExist(app.webViews.otherElements["expired.badssl.com"], timeout: TIMEOUT_LONG) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307022 + // https://mozilla.testrail.io/index.php?/cases/view/2307022 // In this test, the parent window opens a child and in the child it creates a fake link 'link-created-by-parent' func testWriteToChildPopupTab() { waitForTabsButton() @@ -391,35 +391,35 @@ class NavigationTest: BaseTestCase { navigator.openURL(path(forTestPage: "test-mozilla-org.html")) waitUntilPageLoad() navigator.openURL(path(forTestPage: "test-window-opener.html")) - mozWaitForElementToExist(app.links["link-created-by-parent"], timeout: TIMEOUT) + mozWaitForElementToExist(app.links["link-created-by-parent"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307020 + // https://mozilla.testrail.io/index.php?/cases/view/2307020 // Smoketest func testVerifyBrowserTabMenu() { - mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton], timeout: TIMEOUT) + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton]) navigator.nowAt(NewTabScreen) navigator.goto(BrowserTabMenu) mozWaitForElementToExist(app.tables["Context Menu"]) - XCTAssertTrue(app.tables.otherElements[StandardImageIdentifiers.Large.bookmarkTrayFill].exists) - XCTAssertTrue(app.tables.otherElements[StandardImageIdentifiers.Large.history].exists) - XCTAssertTrue(app.tables.otherElements[StandardImageIdentifiers.Large.download].exists) - XCTAssertTrue(app.tables.otherElements[StandardImageIdentifiers.Large.readingList].exists) - XCTAssertTrue(app.tables.otherElements[StandardImageIdentifiers.Large.login].exists) - XCTAssertTrue(app.tables.otherElements[StandardImageIdentifiers.Large.sync].exists) - XCTAssertTrue(app.tables.otherElements[StandardImageIdentifiers.Large.nightMode].exists) - XCTAssertTrue(app.tables.otherElements[StandardImageIdentifiers.Large.whatsNew].exists) - XCTAssertTrue(app.tables.otherElements[StandardImageIdentifiers.Large.helpCircle].exists) - XCTAssertTrue(app.tables.otherElements[StandardImageIdentifiers.Large.edit].exists) - XCTAssertTrue(app.tables.otherElements[StandardImageIdentifiers.Large.settings].exists) + mozWaitForElementToExist(app.tables.otherElements[StandardImageIdentifiers.Large.bookmarkTrayFill]) + mozWaitForElementToExist(app.tables.otherElements[StandardImageIdentifiers.Large.history]) + mozWaitForElementToExist(app.tables.otherElements[StandardImageIdentifiers.Large.download]) + mozWaitForElementToExist(app.tables.otherElements[StandardImageIdentifiers.Large.readingList]) + mozWaitForElementToExist(app.tables.otherElements[StandardImageIdentifiers.Large.login]) + mozWaitForElementToExist(app.tables.otherElements[StandardImageIdentifiers.Large.sync]) + mozWaitForElementToExist(app.tables.otherElements[StandardImageIdentifiers.Large.nightMode]) + mozWaitForElementToExist(app.tables.otherElements[StandardImageIdentifiers.Large.whatsNew]) + mozWaitForElementToExist(app.tables.otherElements[StandardImageIdentifiers.Large.helpCircle]) + mozWaitForElementToExist(app.tables.otherElements[StandardImageIdentifiers.Large.edit]) + mozWaitForElementToExist(app.tables.otherElements[StandardImageIdentifiers.Large.settings]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2441775 + // https://mozilla.testrail.io/index.php?/cases/view/2441775 // Smoketest func testURLBar() { let urlBar = app.textFields["url"] - mozWaitForElementToExist(urlBar, timeout: TIMEOUT) + mozWaitForElementToExist(urlBar) urlBar.tap() let addressBar = app.textFields["address"] @@ -435,7 +435,7 @@ class NavigationTest: BaseTestCase { // swiftlint:enable empty_count } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2441772 + // https://mozilla.testrail.io/index.php?/cases/view/2441772 func testOpenInNewTab() { // Long-tap on an article link. Choose "Open in New Tab". openContextMenuForArticleLink() @@ -446,10 +446,10 @@ class NavigationTest: BaseTestCase { mozWaitForElementToExist(app.otherElements["Tabs Tray"].cells.staticTexts["Example Domain"]) let numTabs = app.otherElements["Tabs Tray"].cells.count XCTAssertEqual(numTabs, 2, "Total number of opened tabs should be 2") - XCTAssertTrue(app.otherElements["Tabs Tray"].cells.elementContainingText("Example Domain.").exists) + mozWaitForElementToExist(app.otherElements["Tabs Tray"].cells.elementContainingText("Example Domain.")) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2441773 + // https://mozilla.testrail.io/index.php?/cases/view/2441773 func testOpenInNewPrivateTab() { // Long-tap on an article link. Choose "Open in New Private Tab". openContextMenuForArticleLink() @@ -459,7 +459,7 @@ class NavigationTest: BaseTestCase { navigator.goto(TabTray) var numTabs = app.otherElements["Tabs Tray"].cells.count XCTAssertEqual(numTabs, 1, "Total number of regulat opened tabs should be 1") - XCTAssertTrue(app.otherElements["Tabs Tray"].cells.elementContainingText("Example Domain.").exists) + mozWaitForElementToExist(app.otherElements["Tabs Tray"].cells.elementContainingText("Example Domain.")) if iPad() { app.buttons["Private"].tap() } else { @@ -467,10 +467,10 @@ class NavigationTest: BaseTestCase { } numTabs = app.otherElements["Tabs Tray"].cells.count XCTAssertEqual(numTabs, 1, "Total number of private opened tabs should be 1") - XCTAssertTrue(app.otherElements["Tabs Tray"].cells.staticTexts["Example Domains"].exists) + mozWaitForElementToExist(app.otherElements["Tabs Tray"].cells.staticTexts["Example Domains"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2441774 + // https://mozilla.testrail.io/index.php?/cases/view/2441774 func testBookmarkLink() { // Long-tap on an article link. Choose "Bookmark Link". openContextMenuForArticleLink() @@ -478,14 +478,37 @@ class NavigationTest: BaseTestCase { app.buttons["Bookmark Link"].tap() // The link has been added to the Bookmarks panel in Library navigator.goto(LibraryPanel_Bookmarks) - mozWaitForElementToExist(app.tables["Bookmarks List"], timeout: 5) - XCTAssertTrue(app.tables["Bookmarks List"].staticTexts[website_2["link"]!].exists) + mozWaitForElementToExist(app.tables["Bookmarks List"]) + mozWaitForElementToExist(app.tables["Bookmarks List"].staticTexts[website_2["link"]!]) + } + + // https://mozilla.testrail.io/index.php?/cases/view/2695828 + func testBackArrowNavigation() { + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton]) + navigator.nowAt(NewTabScreen) + closeFromAppSwitcherAndRelaunch() + navigator.openURL(path(forTestPage: "test-example.html")) + waitUntilPageLoad() + app.links[website_2["link"]!].tap() + waitUntilPageLoad() + let backButton = app.buttons[AccessibilityIdentifiers.Toolbar.backButton] + mozWaitForElementToExist(backButton) + XCTAssertTrue(backButton.isHittable, "Back button is not hittable") + XCTAssertTrue(backButton.isEnabled, "Back button is disabled") + backButton.tap() + waitUntilPageLoad() + mozWaitForValueContains(app.textFields["url"], value: "test-example.html") + XCTAssertTrue(backButton.isHittable, "Back button is not hittable") + XCTAssertTrue(backButton.isEnabled, "Back button is disabled") + backButton.tap() + waitUntilPageLoad() + mozWaitForElementToExist(app.cells[AccessibilityIdentifiers.FirefoxHomepage.TopSites.itemCell]) } private func openContextMenuForArticleLink() { navigator.openURL(path(forTestPage: "test-example.html")) mozWaitForElementToExist(app.webViews.links[website_2["link"]!], timeout: TIMEOUT_LONG) app.webViews.links[website_2["link"]!].press(forDuration: 2) - mozWaitForElementToExist(app.otherElements.collectionViews.element(boundBy: 0), timeout: TIMEOUT) + mozWaitForElementToExist(app.otherElements.collectionViews.element(boundBy: 0)) } } diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/NewTabSettings.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/NewTabSettings.swift index 53fcc89fc81c..1aaf84d40e4c 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/NewTabSettings.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/NewTabSettings.swift @@ -6,22 +6,22 @@ import XCTest let websiteUrl = "www.mozilla.org" class NewTabSettingsTest: BaseTestCase { - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307026 + // https://mozilla.testrail.io/index.php?/cases/view/2307026 // Smoketest func testCheckNewTabSettingsByDefault() { - mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton], timeout: 5) + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton]) navigator.nowAt(NewTabScreen) navigator.goto(NewTabSettings) mozWaitForElementToExist(app.navigationBars["New Tab"]) - XCTAssertTrue(app.tables.cells["Firefox Home"].exists) - XCTAssertTrue(app.tables.cells["Blank Page"].exists) - XCTAssertTrue(app.tables.cells["NewTabAsCustomURL"].exists) + mozWaitForElementToExist(app.tables.cells["Firefox Home"]) + mozWaitForElementToExist(app.tables.cells["Blank Page"]) + mozWaitForElementToExist(app.tables.cells["NewTabAsCustomURL"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307027 + // https://mozilla.testrail.io/index.php?/cases/view/2307027 // Smoketest func testChangeNewTabSettingsShowBlankPage() { - mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton], timeout: 5) + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton]) navigator.nowAt(NewTabScreen) navigator.goto(NewTabSettings) mozWaitForElementToExist(app.navigationBars["New Tab"]) @@ -38,14 +38,14 @@ class NewTabSettingsTest: BaseTestCase { mozWaitForElementToNotExist(app.staticTexts["Highlights"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307028 + // https://mozilla.testrail.io/index.php?/cases/view/2307028 func testChangeNewTabSettingsShowFirefoxHome() { // Set to history page first since FF Home is default waitForTabsButton() navigator.nowAt(NewTabScreen) navigator.performAction(Action.SelectNewTabAsBlankPage) navigator.performAction(Action.OpenNewTabFromTabTray) - mozWaitForElementToExist(app.buttons["urlBar-cancel"], timeout: TIMEOUT) + mozWaitForElementToExist(app.buttons["urlBar-cancel"]) navigator.performAction(Action.CloseURLBarOpen) navigator.nowAt(NewTabScreen) mozWaitForElementToNotExist( @@ -60,15 +60,14 @@ class NewTabSettingsTest: BaseTestCase { mozWaitForElementToExist(app.collectionViews.cells[AccessibilityIdentifiers.FirefoxHomepage.TopSites.itemCell]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307029 + // https://mozilla.testrail.io/index.php?/cases/view/2307029 // Smoketest func testChangeNewTabSettingsShowCustomURL() { navigator.nowAt(NewTabScreen) navigator.goto(NewTabSettings) mozWaitForElementToExist(app.navigationBars["New Tab"]) // Check the placeholder value - let placeholderValue = app.textFields["NewTabAsCustomURLTextField"].value as! String - XCTAssertEqual(placeholderValue, "Custom URL") + mozWaitForValueContains(app.textFields["NewTabAsCustomURLTextField"], value: "Custom URL") navigator.performAction(Action.SelectNewTabAsCustomURL) // Check the value typed app.textFields["NewTabAsCustomURLTextField"].typeText("mozilla.org") @@ -80,11 +79,11 @@ class NewTabSettingsTest: BaseTestCase { navigator.nowAt(NewTabScreen) // Check that website is open - mozWaitForElementToExist(app.webViews.firstMatch, timeout: 20) + mozWaitForElementToExist(app.webViews.firstMatch, timeout: TIMEOUT_LONG) mozWaitForValueContains(app.textFields["url"], value: "mozilla") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307030 + // https://mozilla.testrail.io/index.php?/cases/view/2307030 func testChangeNewTabSettingsLabel() { navigator.nowAt(NewTabScreen) // Go to New Tab settings and select Custom URL option @@ -108,11 +107,11 @@ class NewTabSettingsTest: BaseTestCase { XCTAssertEqual(app.tables.cells["NewTab"].label, "New Tab, Firefox Home") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306877 + // https://mozilla.testrail.io/index.php?/cases/view/2306877 // Smoketest func testKeyboardRaisedWhenTabOpenedFromTabTray() { // Add New tab and set it as Blank - mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton], timeout: 5) + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton]) navigator.nowAt(NewTabScreen) navigator.goto(NewTabSettings) mozWaitForElementToExist(app.navigationBars["New Tab"]) @@ -129,11 +128,11 @@ class NewTabSettingsTest: BaseTestCase { validateKeyboardIsRaisedAndDismissed() } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306875 + // https://mozilla.testrail.io/index.php?/cases/view/2306875 // Smoketest func testNewTabCustomURLKeyboardNotRaised() { // Set a custom URL - mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton], timeout: 5) + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton]) navigator.nowAt(NewTabScreen) navigator.goto(NewTabSettings) navigator.performAction(Action.SelectNewTabAsCustomURL) @@ -168,7 +167,7 @@ class NewTabSettingsTest: BaseTestCase { XCTAssertTrue(addressBar.value(forKey: "hasKeyboardFocus") as? Bool ?? false) XCTAssertTrue(app.keyboards.element.isVisible(), "The keyboard is not shown") // Tap the back button - mozWaitForElementToExist(app.buttons["urlBar-cancel"], timeout: TIMEOUT) + mozWaitForElementToExist(app.buttons["urlBar-cancel"]) navigator.performAction(Action.CloseURLBarOpen) // The keyboard is dismissed and the URL is unfocused mozWaitForElementToExist(app.textFields["url"]) diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/NightModeTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/NightModeTests.swift index d92447db85be..1abae1456093 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/NightModeTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/NightModeTests.swift @@ -14,7 +14,7 @@ class NightModeTests: BaseTestCase { mozWaitForElementToExist(app.tables.otherElements[StandardImageIdentifiers.Large.nightMode]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307056 + // https://mozilla.testrail.io/index.php?/cases/view/2307056 func testNightModeUI() { let url1 = "test-example.html" // Go to a webpage, and select night mode on and off, check it's applied or not diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/OnboardingTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/OnboardingTests.swift index df984afee155..9887dba314d7 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/OnboardingTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/OnboardingTests.swift @@ -19,20 +19,38 @@ class OnboardingTests: BaseTestCase { } // Smoketest - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306814 + // https://mozilla.testrail.io/index.php?/cases/view/2306814 func testFirstRunTour() { // Complete the First run from first screen to the latest one // Check that the first's tour screen is shown as well as all the elements in there - mozWaitForElementToExist(app.images["\(rootA11yId)ImageView"], timeout: 15) + mozWaitForElementToExist(app.images["\(rootA11yId)ImageView"]) + mozWaitForElementToExist(app.staticTexts["\(rootA11yId)TitleLabel"]) + mozWaitForElementToExist(app.staticTexts["\(rootA11yId)DescriptionLabel"]) + mozWaitForElementToExist(app.buttons["\(rootA11yId)PrimaryButton"]) + mozWaitForElementToExist(app.buttons["\(rootA11yId)SecondaryButton"]) + mozWaitForElementToExist(app.buttons["\(AccessibilityIdentifiers.Onboarding.closeButton)"]) + mozWaitForElementToExist(app.pageIndicators["\(AccessibilityIdentifiers.Onboarding.pageControl)"]) + + // Swipe to the second screen + app.buttons["\(rootA11yId)SecondaryButton"].tap() + currentScreen += 1 + mozWaitForElementToExist(app.images["\(rootA11yId)ImageView"]) + mozWaitForElementToExist(app.staticTexts["\(rootA11yId)TitleLabel"]) + mozWaitForElementToExist(app.staticTexts["\(rootA11yId)DescriptionLabel"]) + mozWaitForElementToExist(app.buttons["\(rootA11yId)PrimaryButton"]) + mozWaitForElementToExist(app.buttons["\(rootA11yId)SecondaryButton"]) + + // Swipe to the third screen + app.buttons["\(rootA11yId)SecondaryButton"].tap() + currentScreen += 1 + mozWaitForElementToExist(app.images["\(rootA11yId)ImageView"]) XCTAssertTrue(app.images["\(rootA11yId)ImageView"].exists) XCTAssertTrue(app.staticTexts["\(rootA11yId)TitleLabel"].exists) XCTAssertTrue(app.staticTexts["\(rootA11yId)DescriptionLabel"].exists) XCTAssertTrue(app.buttons["\(rootA11yId)PrimaryButton"].exists) XCTAssertTrue(app.buttons["\(rootA11yId)SecondaryButton"].exists) - XCTAssertTrue(app.buttons["\(AccessibilityIdentifiers.Onboarding.closeButton)"].exists) - XCTAssertTrue(app.pageIndicators["\(AccessibilityIdentifiers.Onboarding.pageControl)"].exists) - // Swipe to the second screen + // Swipe to the fourth screen app.buttons["\(rootA11yId)SecondaryButton"].tap() currentScreen += 1 mozWaitForElementToExist(app.images["\(rootA11yId)ImageView"], timeout: 15) @@ -42,8 +60,8 @@ class OnboardingTests: BaseTestCase { XCTAssertTrue(app.buttons["\(rootA11yId)PrimaryButton"].exists) XCTAssertTrue(app.buttons["\(rootA11yId)SecondaryButton"].exists) - // Swipe to the third screen - app.buttons["\(rootA11yId)SecondaryButton"].tap() + // Swipe to the fifth screen + app.buttons["\(rootA11yId)PrimaryButton"].tap() currentScreen += 1 mozWaitForElementToExist(app.images["\(rootA11yId)ImageView"], timeout: 15) XCTAssertTrue(app.images["\(rootA11yId)ImageView"].exists) @@ -53,20 +71,20 @@ class OnboardingTests: BaseTestCase { XCTAssertTrue(app.buttons["\(rootA11yId)SecondaryButton"].exists) // Finish onboarding - app.buttons["\(rootA11yId)SecondaryButton"].tap() + app.buttons["\(rootA11yId)PrimaryButton"].tap() let topSites = app.collectionViews.cells[AccessibilityIdentifiers.FirefoxHomepage.TopSites.itemCell] mozWaitForElementToExist(topSites) } // Smoketest - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306816 + // https://mozilla.testrail.io/index.php?/cases/view/2306816 func testCloseTour() { app.buttons["\(AccessibilityIdentifiers.Onboarding.closeButton)"].tap() let topSites = app.collectionViews.cells[AccessibilityIdentifiers.FirefoxHomepage.TopSites.itemCell] mozWaitForElementToExist(topSites) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306815 + // https://mozilla.testrail.io/index.php?/cases/view/2306815 func testWhatsNewPage() { app.buttons["\(AccessibilityIdentifiers.Onboarding.closeButton)"].tap() navigator.goto(BrowserTabMenu) @@ -82,18 +100,18 @@ class OnboardingTests: BaseTestCase { let mySubstring = textUrl[range] let releaseVersion = String(mySubstring) - XCTAssertTrue(app.staticTexts[releaseVersion].exists) + mozWaitForElementToExist(app.staticTexts[releaseVersion]) mozWaitForValueContains( app.textFields["url"], value: "www.mozilla.org/en-US/firefox/ios/" + releaseVersion + "/releasenotes/" ) - XCTAssertTrue(app.staticTexts["Release Notes"].exists) + mozWaitForElementToExist(app.staticTexts["Release Notes"]) if iPad() { - XCTAssertTrue( - app.staticTexts["Firefox for iOS \(releaseVersion), See All New Features, Updates and Fixes"].exists + mozWaitForElementToExist( + app.staticTexts["Firefox for iOS \(releaseVersion), See All New Features, Updates and Fixes"] ) } - XCTAssertTrue(app.staticTexts["Firefox for iOS Release"].exists) - XCTAssertTrue(app.staticTexts["Get the most recent version"].exists) + mozWaitForElementToExist(app.staticTexts["Firefox for iOS Release"]) + mozWaitForElementToExist(app.staticTexts["Get the most recent version"]) } } diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/OpeningScreenTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/OpeningScreenTests.swift index e8601be815be..80b03467ec6d 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/OpeningScreenTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/OpeningScreenTests.swift @@ -4,7 +4,7 @@ import Foundation class OpeningScreenTests: BaseTestCase { - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307039 + // https://mozilla.testrail.io/index.php?/cases/view/2307039 func testLastOpenedTab() { // Open a web page navigator.openURL(path(forTestPage: "test-mozilla-org.html"), waitForLoading: true) diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/PerformanceTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/PerformanceTests.swift index 4832cba150d1..805ab28e4b16 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/PerformanceTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/PerformanceTests.swift @@ -87,8 +87,8 @@ class PerformanceTests: BaseTestCase { let tabsButtonNumber = app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton].staticTexts["20"] let doneButton = app.buttons[AccessibilityIdentifiers.TabTray.doneButton] - mozWaitForElementToExist(tabsButton, timeout: TIMEOUT) - mozWaitForElementToExist(tabsButtonNumber, timeout: TIMEOUT) + mozWaitForElementToExist(tabsButton) + mozWaitForElementToExist(tabsButtonNumber) measure(metrics: [ XCTClockMetric(), // to measure timeClock Mon @@ -96,9 +96,9 @@ class PerformanceTests: BaseTestCase { XCTStorageMetric(), // to measure storage consuming XCTMemoryMetric()]) { // go to tab tray - mozWaitForElementToExist(tabsButton, timeout: TIMEOUT) + mozWaitForElementToExist(tabsButton) tabsButton.tap() - mozWaitForElementToExist(doneButton, timeout: TIMEOUT) + mozWaitForElementToExist(doneButton) doneButton.tap() } // Handle termination ourselves as it sometimes hangs when given to xctrunner @@ -111,8 +111,8 @@ class PerformanceTests: BaseTestCase { let tabsButtonNumber = app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton].staticTexts["∞"] let doneButton = app.buttons[AccessibilityIdentifiers.TabTray.doneButton] - mozWaitForElementToExist(tabsButton, timeout: TIMEOUT) - mozWaitForElementToExist(tabsButtonNumber, timeout: TIMEOUT) + mozWaitForElementToExist(tabsButton) + mozWaitForElementToExist(tabsButtonNumber) measure(metrics: [ XCTClockMetric(), // to measure timeClock Mon @@ -120,9 +120,9 @@ class PerformanceTests: BaseTestCase { XCTStorageMetric(), // to measure storage consuming XCTMemoryMetric()]) { // go to tab tray - mozWaitForElementToExist(tabsButton, timeout: TIMEOUT) + mozWaitForElementToExist(tabsButton) tabsButton.tap() - mozWaitForElementToExist(doneButton, timeout: TIMEOUT) + mozWaitForElementToExist(doneButton) doneButton.tap() } // Handle termination ourselves as it sometimes hangs when given to xctrunner @@ -132,7 +132,7 @@ class PerformanceTests: BaseTestCase { func testPerfHistory1startUp() { // Warning: Avoid using mozWaitForElementToExist as it is up to 25x less performant let tabsButton = app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton] - mozWaitForElementToExist(tabsButton, timeout: TIMEOUT) + mozWaitForElementToExist(tabsButton) app.terminate() measure(metrics: [ @@ -143,7 +143,7 @@ class PerformanceTests: BaseTestCase { XCTMemoryMetric()]) { // activity measurement here app.launch() - mozWaitForElementToExist(tabsButton, timeout: TIMEOUT) + mozWaitForElementToExist(tabsButton) app.terminate() } // Handle termination ourselves as it sometimes hangs when given to xctrunner @@ -153,14 +153,14 @@ class PerformanceTests: BaseTestCase { func testPerfHistory1openMenu() { // Warning: Avoid using mozWaitForElementToExist as it is up to 25x less performant let tabsButton = app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton] - mozWaitForElementToExist(tabsButton, timeout: TIMEOUT) + mozWaitForElementToExist(tabsButton) navigator.goto(LibraryPanel_History) // Ensure 'History List' exists before taking a snapshot to avoid expensive retries. // Return firstMatch to avoid traversing the entire { Window, Window } element tree. let historyList = app.tables["History List"].firstMatch - mozWaitForElementToExist(historyList, timeout: TIMEOUT) + mozWaitForElementToExist(historyList) measure(metrics: [ XCTMemoryMetric(), @@ -174,7 +174,7 @@ class PerformanceTests: BaseTestCase { let historyListCells = historyListSnapshot.children.filter { $0.elementType == .cell } let historyItems = historyListCells.dropFirst() - // Warning: If the history database used for this test is updated, so will the date of + // Warning: If the history database used for this test is updated, so will the date of // those history items. This means as those history items age, they will fall into older // buckets, causing new cells to be created representing this new age bucket // (i.e. 'yesterday', 'a week', etc) where the 100 entries will be split across multiple age @@ -193,7 +193,7 @@ class PerformanceTests: BaseTestCase { func testPerfHistory100startUp() { // Warning: Avoid using mozWaitForElementToExist as it is up to 25x less performant let tabsButton = app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton] - mozWaitForElementToExist(tabsButton, timeout: TIMEOUT) + mozWaitForElementToExist(tabsButton) app.terminate() measure(metrics: [ @@ -204,7 +204,7 @@ class PerformanceTests: BaseTestCase { XCTMemoryMetric()]) { // activity measurement here app.launch() - mozWaitForElementToExist(tabsButton, timeout: TIMEOUT) + mozWaitForElementToExist(tabsButton) app.terminate() } // Handle termination ourselves as it sometimes hangs when given to xctrunner @@ -214,14 +214,14 @@ class PerformanceTests: BaseTestCase { func testPerfHistory100openMenu() { // Warning: Avoid using mozWaitForElementToExist as it is up to 25x less performant let tabsButton = app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton] - mozWaitForElementToExist(tabsButton, timeout: TIMEOUT) + mozWaitForElementToExist(tabsButton) navigator.goto(LibraryPanel_History) // Ensure 'History List' exists before taking a snapshot to avoid expensive retries. // Return firstMatch to avoid traversing the entire { Window, Window } element tree. let historyList = app.tables["History List"].firstMatch - mozWaitForElementToExist(historyList, timeout: TIMEOUT) + mozWaitForElementToExist(historyList) measure(metrics: [ XCTMemoryMetric(), @@ -254,7 +254,7 @@ class PerformanceTests: BaseTestCase { func testPerfBookmarks1startUp() { // Warning: Avoid using mozWaitForElementToExist as it is up to 25x less performant let tabsButton = app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton] - mozWaitForElementToExist(tabsButton, timeout: TIMEOUT) + mozWaitForElementToExist(tabsButton) app.terminate() measure(metrics: [ @@ -265,7 +265,7 @@ class PerformanceTests: BaseTestCase { XCTMemoryMetric()]) { // activity measurement here app.launch() - mozWaitForElementToExist(tabsButton, timeout: TIMEOUT) + mozWaitForElementToExist(tabsButton) app.terminate() } // Handle termination ourselves as it sometimes hangs when given to xctrunner @@ -275,14 +275,14 @@ class PerformanceTests: BaseTestCase { func testPerfBookmarks1openMenu() { // Warning: Avoid using mozWaitForElementToExist as it is up to 25x less performant let tabsButton = app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton] - mozWaitForElementToExist(tabsButton, timeout: TIMEOUT) + mozWaitForElementToExist(tabsButton) navigator.goto(LibraryPanel_Bookmarks) // Ensure 'Bookmarks List' exists before taking a snapshot to avoid expensive retries. // Return firstMatch to avoid traversing the entire { Window, Window } element tree. let bookmarksList = app.tables["Bookmarks List"].firstMatch - mozWaitForElementToExist(bookmarksList, timeout: TIMEOUT) + mozWaitForElementToExist(bookmarksList) measure(metrics: [ XCTMemoryMetric(), @@ -311,7 +311,7 @@ class PerformanceTests: BaseTestCase { func testPerfBookmarks100startUp() { // Warning: Avoid using mozWaitForElementToExist as it is up to 25x less performant let tabsButton = app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton] - mozWaitForElementToExist(tabsButton, timeout: TIMEOUT) + mozWaitForElementToExist(tabsButton) app.terminate() measure(metrics: [ @@ -322,7 +322,7 @@ class PerformanceTests: BaseTestCase { XCTMemoryMetric()]) { // Activity measurement here app.launch() - mozWaitForElementToExist(tabsButton, timeout: TIMEOUT) + mozWaitForElementToExist(tabsButton) app.terminate() } // Handle termination ourselves as it sometimes hangs when given to xctrunner @@ -332,14 +332,14 @@ class PerformanceTests: BaseTestCase { func testPerfBookmarks100openMenu() { // Warning: Avoid using mozWaitForElementToExist as it is up to 25x less performant let tabsButton = app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton] - mozWaitForElementToExist(tabsButton, timeout: TIMEOUT) + mozWaitForElementToExist(tabsButton) navigator.goto(LibraryPanel_Bookmarks) // Ensure 'Bookmarks List' exists before taking a snapshot to avoid expensive retries. // Return firstMatch to avoid traversing the entire { Window, Window } element tree. let bookmarksList = app.tables["Bookmarks List"].firstMatch - mozWaitForElementToExist(bookmarksList, timeout: TIMEOUT) + mozWaitForElementToExist(bookmarksList) measure(metrics: [ XCTMemoryMetric(), @@ -368,7 +368,7 @@ class PerformanceTests: BaseTestCase { func testPerfBookmarks1000startUp() { // Warning: Avoid using mozWaitForElementToExist as it is up to 25x less performant let tabsButton = app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton] - mozWaitForElementToExist(tabsButton, timeout: TIMEOUT) + mozWaitForElementToExist(tabsButton) app.terminate() measure(metrics: [ @@ -379,7 +379,7 @@ class PerformanceTests: BaseTestCase { XCTMemoryMetric()]) { // Activity measurement here app.launch() - mozWaitForElementToExist(tabsButton, timeout: TIMEOUT) + mozWaitForElementToExist(tabsButton) app.terminate() } // Handle termination ourselves as it sometimes hangs when given to xctrunner @@ -389,14 +389,14 @@ class PerformanceTests: BaseTestCase { func testPerfBookmarks1000openMenu() { // Warning: Avoid using mozWaitForElementToExist as it is up to 25x less performant let tabsButton = app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton] - mozWaitForElementToExist(tabsButton, timeout: TIMEOUT) + mozWaitForElementToExist(tabsButton) navigator.goto(LibraryPanel_Bookmarks) // Ensure 'Bookmarks List' exists before taking a snapshot to avoid expensive retries. // Return firstMatch to avoid traversing the entire { Window, Window } element tree. let bookmarksList = app.tables["Bookmarks List"].firstMatch - mozWaitForElementToExist(bookmarksList, timeout: TIMEOUT) + mozWaitForElementToExist(bookmarksList) measure(metrics: [ XCTMemoryMetric(), diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/PhotonActionSheetTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/PhotonActionSheetTests.swift index b1c001dc320b..b863227bfafe 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/PhotonActionSheetTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/PhotonActionSheetTests.swift @@ -6,7 +6,7 @@ import XCTest import Common class PhotonActionSheetTests: BaseTestCase { - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306849 + // https://mozilla.testrail.io/index.php?/cases/view/2306849 // Smoketest func testPinToShortcuts() { navigator.openURL("http://example.com") @@ -31,7 +31,7 @@ class PhotonActionSheetTests: BaseTestCase { mozWaitForElementToExist(app.tables.cells.otherElements[StandardImageIdentifiers.Large.pin]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2322067 + // https://mozilla.testrail.io/index.php?/cases/view/2322067 // Smoketest func testShareOptionIsShown() { // Temporarily disabled until url bar redesign work FXIOS-8172 @@ -44,7 +44,7 @@ class PhotonActionSheetTests: BaseTestCase { // mozWaitForElementToExist(app.cells["Copy"], timeout: 15) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2322667 + // https://mozilla.testrail.io/index.php?/cases/view/2322667 func testSendToDeviceFromPageOptionsMenu() { // User not logged in navigator.openURL(path(forTestPage: "test-mozilla-org.html")) @@ -72,15 +72,15 @@ class PhotonActionSheetTests: BaseTestCase { // mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.shareButton], timeout: 10) // app.buttons[AccessibilityIdentifiers.Toolbar.shareButton].tap() navigator.goto(BrowserTabMenu) - app.otherElements[StandardImageIdentifiers.Large.shareApple].tap() + app.otherElements[StandardImageIdentifiers.Large.share].tap() if #unavailable(iOS 16) { mozWaitForElementToExist(app.otherElements["ActivityListView"].navigationBars["UIActivityContentView"]) - mozWaitForElementToExist(app.buttons["Copy"], timeout: TIMEOUT) + mozWaitForElementToExist(app.buttons["Copy"]) } else { mozWaitForElementToExist(app.otherElements["ActivityListView"].otherElements["Example Domain"]) mozWaitForElementToExist(app.otherElements["ActivityListView"].otherElements["example.com"]) - mozWaitForElementToExist(app.collectionViews.cells["Copy"], timeout: TIMEOUT) + mozWaitForElementToExist(app.collectionViews.cells["Copy"]) } var fennecElement = app.collectionViews.scrollViews.cells.elementContainingText("Fennec") // This is not ideal but only way to get the element on iPhone 8 @@ -88,38 +88,36 @@ class PhotonActionSheetTests: BaseTestCase { if #unavailable(iOS 17) { fennecElement = app.collectionViews.scrollViews.cells.element(boundBy: 2) } - mozWaitForElementToExist(fennecElement, timeout: 5) + mozWaitForElementToExist(fennecElement) fennecElement.tap() mozWaitForElementToExist(app.navigationBars["ShareTo.ShareView"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306841 + // https://mozilla.testrail.io/index.php?/cases/view/2306841 // Smoketest func testSharePageWithShareSheetOptions() { openNewShareSheet() - mozWaitForElementToExist(app.staticTexts["Open in Firefox"], timeout: 10) - XCTAssertTrue(app.staticTexts["Open in Firefox"].exists) - XCTAssertTrue(app.staticTexts["Load in Background"].exists) - XCTAssertTrue(app.staticTexts["Bookmark This Page"].exists) - XCTAssertTrue(app.staticTexts["Add to Reading List"].exists) - XCTAssertTrue(app.staticTexts["Send to Device"].exists) + mozWaitForElementToExist(app.staticTexts["Open in Firefox"]) + mozWaitForElementToExist(app.staticTexts["Load in Background"]) + mozWaitForElementToExist(app.staticTexts["Bookmark This Page"]) + mozWaitForElementToExist(app.staticTexts["Add to Reading List"]) + mozWaitForElementToExist(app.staticTexts["Send to Device"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2323203 + // https://mozilla.testrail.io/index.php?/cases/view/2323203 func testShareSheetSendToDevice() { openNewShareSheet() mozWaitForElementToExist(app.staticTexts["Send to Device"]) app.staticTexts["Send to Device"].tap() mozWaitForElementToExist( - app.navigationBars.buttons[AccessibilityIdentifiers.ShareTo.HelpView.doneButton], - timeout: 10 + app.navigationBars.buttons[AccessibilityIdentifiers.ShareTo.HelpView.doneButton] ) - XCTAssertTrue(app.staticTexts["You are not signed in to your account."].exists) + mozWaitForElementToExist(app.staticTexts["You are not signed in to your account."]) app.navigationBars.buttons[AccessibilityIdentifiers.ShareTo.HelpView.doneButton].tap() } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2323204 + // https://mozilla.testrail.io/index.php?/cases/view/2323204 func testShareSheetOpenAndCancel() { openNewShareSheet() app.buttons["Cancel"].tap() diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/PocketTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/PocketTests.swift index 0f2349556eaf..0da6a27c2d59 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/PocketTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/PocketTests.swift @@ -5,7 +5,7 @@ import XCTest class PocketTests: BaseTestCase { - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306924 + // https://mozilla.testrail.io/index.php?/cases/view/2306924 func testPocketEnabledByDefault() { navigator.goto(NewTabScreen) mozWaitForElementToExist(app.staticTexts[AccessibilityIdentifiers.FirefoxHomepage.SectionTitles.pocket]) diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/PrivateBrowsingTest.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/PrivateBrowsingTest.swift index 1cadd2d79369..6a23a26d28a1 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/PrivateBrowsingTest.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/PrivateBrowsingTest.swift @@ -15,7 +15,7 @@ let url2Label = "Internet for people, not profit — Mozilla" class PrivateBrowsingTest: BaseTestCase { typealias HistoryPanelA11y = AccessibilityIdentifiers.LibraryPanels.HistoryPanel - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307004 + // https://mozilla.testrail.io/index.php?/cases/view/2307004 func testPrivateTabDoesNotTrackHistory() { navigator.openURL(url1) waitForTabsButton() @@ -24,7 +24,7 @@ class PrivateBrowsingTest: BaseTestCase { navigator.goto(LibraryPanel_History) mozWaitForElementToExist(app.tables[HistoryPanelA11y.tableView]) - XCTAssertTrue(app.tables[HistoryPanelA11y.tableView].staticTexts[url1And3Label].exists) + mozWaitForElementToExist(app.tables[HistoryPanelA11y.tableView].staticTexts[url1And3Label]) // History without counting Clear Recent History and Recently Closed let history = app.tables[HistoryPanelA11y.tableView].cells.count - 1 @@ -37,15 +37,15 @@ class PrivateBrowsingTest: BaseTestCase { mozWaitForValueContains(app.textFields["url"], value: "mozilla") navigator.goto(LibraryPanel_History) mozWaitForElementToExist(app.tables[HistoryPanelA11y.tableView]) - XCTAssertTrue(app.tables[HistoryPanelA11y.tableView].staticTexts[url1And3Label].exists) - XCTAssertFalse(app.tables[HistoryPanelA11y.tableView].staticTexts[url2Label].exists) + mozWaitForElementToExist(app.tables[HistoryPanelA11y.tableView].staticTexts[url1And3Label]) + mozWaitForElementToNotExist(app.tables[HistoryPanelA11y.tableView].staticTexts[url2Label]) // Open one tab in private browsing and check the total number of tabs let privateHistory = app.tables[HistoryPanelA11y.tableView].cells.count - 1 XCTAssertEqual(privateHistory, 1, "History entries in private browsing do not match") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307005 + // https://mozilla.testrail.io/index.php?/cases/view/2307005 func testTabCountShowsOnlyNormalOrPrivateTabCount() { // Open two tabs in normal browsing and check the number of tabs open navigator.nowAt(NewTabScreen) @@ -89,11 +89,11 @@ class PrivateBrowsingTest: BaseTestCase { XCTAssertEqual(numRegularTabs, 2, "The number of regular tabs is not correct") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307006 + // https://mozilla.testrail.io/index.php?/cases/view/2307006 func testClosePrivateTabsOptionClosesPrivateTabs() { // Check that Close Private Tabs when closing the Private Browsing Button is off by default navigator.nowAt(NewTabScreen) - mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton], timeout: TIMEOUT) + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton]) navigator.goto(SettingsScreen) // FXIOS-8672: "Close Private Tabs" has been removed from the settings. @@ -108,14 +108,10 @@ class PrivateBrowsingTest: BaseTestCase { app.cells.staticTexts["Homepage"].tap() navigator.nowAt(NewTabScreen) - // Go back to private browsing and check that the tab has not been closed + // Go back to private browsing and check that the tab has been closed navigator.toggleOn(userState.isPrivate, withAction: Action.TogglePrivateMode) mozWaitForElementToExist(app.otherElements["Tabs Tray"]) - XCTAssertNotNil( - app.otherElements["Tabs Tray"].collectionViews.cells.staticTexts - .element(boundBy: 0).label - .range(of: url2Label) - ) + mozWaitForElementToExist(app.staticTexts["Private Browsing"]) checkOpenTabsBeforeClosingPrivateMode() } @@ -124,7 +120,7 @@ class PrivateBrowsingTest: BaseTestCase { created (because the db didn't exist in the cache) https://bugzilla.mozilla.org/show_bug.cgi?id=1646756 */ - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307011 + // https://mozilla.testrail.io/index.php?/cases/view/2307011 func testClearIndexedDB() { navigator.nowAt(NewTabScreen) @@ -133,8 +129,8 @@ class PrivateBrowsingTest: BaseTestCase { func checkIndexedDBIsCreated() { navigator.openURL(urlIndexedDB) waitUntilPageLoad() - XCTAssertTrue(app.webViews.staticTexts["DB_CREATED_PAGE"].exists) - XCTAssertTrue(app.webViews.staticTexts["DB_CREATED_WORKER"].exists) + mozWaitForElementToExist(app.webViews.staticTexts["DB_CREATED_PAGE"]) + mozWaitForElementToExist(app.webViews.staticTexts["DB_CREATED_WORKER"]) } navigator.toggleOn(userState.isPrivate, withAction: Action.TogglePrivateMode) @@ -149,7 +145,7 @@ class PrivateBrowsingTest: BaseTestCase { // checkIndexedDBIsCreated() } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307007 + // https://mozilla.testrail.io/index.php?/cases/view/2307007 func testPrivateBrowserPanelView() { navigator.nowAt(NewTabScreen) // If no private tabs are open, there should be a initial screen with label Private Browsing @@ -178,33 +174,32 @@ class PrivateBrowsingTest: BaseTestCase { XCTAssertEqual(numPrivTabsOpen, 1, "The number of private tabs is not correct") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307012 + // https://mozilla.testrail.io/index.php?/cases/view/2307012 func testLongPressLinkOptionsPrivateMode() { navigator.nowAt(NewTabScreen) navigator.toggleOn(userState.isPrivate, withAction: Action.TogglePrivateMode) navigator.openURL(path(forTestPage: "test-example.html")) - mozWaitForElementToExist(app.webViews.links[website_2["link"]!], timeout: TIMEOUT) + mozWaitForElementToExist(app.webViews.links[website_2["link"]!]) app.webViews.links[website_2["link"]!].press(forDuration: 2) mozWaitForElementToExist( - app.collectionViews.staticTexts[website_2["moreLinkLongPressUrl"]!], - timeout: TIMEOUT + app.collectionViews.staticTexts[website_2["moreLinkLongPressUrl"]!] ) - XCTAssertFalse(app.buttons["Open in New Tab"].exists, "The option is not shown") - XCTAssertTrue(app.buttons["Open in New Private Tab"].exists, "The option is not shown") - XCTAssertTrue(app.buttons["Copy Link"].exists, "The option is not shown") - XCTAssertTrue(app.buttons["Download Link"].exists, "The option is not shown") + mozWaitForElementToNotExist(app.buttons["Open in New Tab"]) + mozWaitForElementToExist(app.buttons["Open in New Private Tab"]) + mozWaitForElementToExist(app.buttons["Copy Link"]) + mozWaitForElementToExist(app.buttons["Download Link"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2497357 + // https://mozilla.testrail.io/index.php?/cases/view/2497357 func testAllPrivateTabsRestore() { - // Several tabs opened in private tabs tray. Tap on the trashcan + // Several tabs opened in private tabs tray. Tap on the trashcan navigator.nowAt(NewTabScreen) navigator.toggleOn(userState.isPrivate, withAction: Action.TogglePrivateMode) for _ in 1...4 { navigator.createNewTab() if app.keyboards.element.isVisible() && !iPad() { - mozWaitForElementToExist(app.buttons["urlBar-cancel"], timeout: TIMEOUT) + mozWaitForElementToExist(app.buttons["urlBar-cancel"]) navigator.performAction(Action.CloseURLBarOpen) } } @@ -243,7 +238,7 @@ class PrivateBrowsingTest: BaseTestCase { XCTAssertEqual(4, numTab, "The number of counted tabs is not equal to \(String(describing: numTab))") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307003 + // https://mozilla.testrail.io/index.php?/cases/view/2307003 func testHamburgerMenuNewPrivateTab() { navigator.toggleOn(userState.isPrivate, withAction: Action.TogglePrivateMode) navigator.openURL(urlExample) @@ -269,18 +264,8 @@ fileprivate extension BaseTestCase { let numPrivTabs = app.otherElements["Tabs Tray"].cells.count XCTAssertEqual( numPrivTabs, - 1, - "The number of tabs is not correct, the private tab should not have been closed" - ) - } - - func checkOpenTabsAfterClosingPrivateMode() { - // The private tab is not loger closed after "Close Private Tabs" has been removed - let numPrivTabsAfterClosing = app.otherElements["Tabs Tray"].cells.count - XCTAssertEqual( - numPrivTabsAfterClosing, - 1, - "The number of tabs is not correct" + 0, + "The private tab should have been closed" ) } @@ -298,7 +283,7 @@ fileprivate extension BaseTestCase { class PrivateBrowsingTestIphone: IphoneOnlyTestCase { // This test is disabled for iPad because the toast menu is not shown there - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307013 + // https://mozilla.testrail.io/index.php?/cases/view/2307013 // Smoketest func testSwitchBetweenPrivateTabsToastButton() { if skipPlatform { return } @@ -316,9 +301,9 @@ class PrivateBrowsingTestIphone: IphoneOnlyTestCase { // Check that the tab has changed waitUntilPageLoad() - mozWaitForElementToExist(app.textFields["url"], timeout: 5) + mozWaitForElementToExist(app.textFields["url"]) mozWaitForValueContains(app.textFields["url"], value: "iana") - XCTAssertTrue(app.links["RFC 2606"].exists) + mozWaitForElementToExist(app.links["RFC 2606"]) mozWaitForElementToExist(app.buttons["Show Tabs"]) let numPrivTab = app.buttons["Show Tabs"].value as? String XCTAssertEqual("2", numPrivTab) @@ -329,23 +314,22 @@ class PrivateBrowsingTestIpad: IpadOnlyTestCase { typealias HistoryPanelA11y = AccessibilityIdentifiers.LibraryPanels.HistoryPanel // This test is only enabled for iPad. Shortcut does not exists on iPhone - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307008 + // https://mozilla.testrail.io/index.php?/cases/view/2307008 func testClosePrivateTabsOptionClosesPrivateTabsShortCutiPad() { if skipPlatform { return } waitForTabsButton() navigator.toggleOn(userState.isPrivate, withAction: Action.TogglePrivateMode) navigator.openURL(url2) - mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton], timeout: TIMEOUT) + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton]) - // FXIOS-8672: "Close Private Tabs" has been removed from the settings. // Leave PM by tapping on PM shourt cut navigator.toggleOff(userState.isPrivate, withAction: Action.TogglePrivateModeFromTabBarHomePanel) waitForTabsButton() navigator.toggleOn(userState.isPrivate, withAction: Action.TogglePrivateMode) - checkOpenTabsAfterClosingPrivateMode() + checkOpenTabsBeforeClosingPrivateMode() } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307009 + // https://mozilla.testrail.io/index.php?/cases/view/2307009 func testiPadDirectAccessPrivateMode() { if skipPlatform { return } waitForTabsButton() @@ -370,7 +354,7 @@ class PrivateBrowsingTestIpad: IpadOnlyTestCase { XCTAssertEqual(history, 0, "History list should be empty") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307010 + // https://mozilla.testrail.io/index.php?/cases/view/2307010 func testiPadDirectAccessPrivateModeBrowserTab() { if skipPlatform { return } navigator.openURL("www.mozilla.org") diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ReadingListTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ReadingListTests.swift index 5ecac9fc7b09..b51f748b88e0 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ReadingListTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ReadingListTests.swift @@ -6,17 +6,17 @@ import Common import XCTest class ReadingListTests: BaseTestCase { - // https://testrail.stage.mozaws.net/index.php?/cases/view/2287278f + // https://mozilla.testrail.io/index.php?/cases/view/2287278f // Smoketest func testLoadReaderContent() { navigator.openURL(path(forTestPage: "test-mozilla-book.html")) navigator.nowAt(BrowserTab) mozWaitForElementToNotExist(app.staticTexts["Fennec pasted from XCUITests-Runner"]) - mozWaitForElementToExist(app.buttons["Reader View"], timeout: TIMEOUT) + mozWaitForElementToExist(app.buttons["Reader View"]) app.buttons["Reader View"].tap() // The settings of reader view are shown as well as the content of the web site - mozWaitForElementToExist(app.buttons["Display Settings"], timeout: TIMEOUT) - XCTAssertTrue(app.webViews.staticTexts["The Book of Mozilla"].exists) + mozWaitForElementToExist(app.buttons["Display Settings"]) + mozWaitForElementToExist(app.webViews.staticTexts["The Book of Mozilla"]) } private func checkReadingListNumberOfItems(items: Int) { @@ -25,7 +25,7 @@ class ReadingListTests: BaseTestCase { XCTAssertEqual(list, items, "The number of items in the reading table is not correct") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306991 + // https://mozilla.testrail.io/index.php?/cases/view/2306991 // Smoketest func testAddToReadingList() { navigator.nowAt(NewTabScreen) @@ -44,16 +44,15 @@ class ReadingListTests: BaseTestCase { // Check that there is one item let savedToReadingList = app.tables["ReadingTable"].cells.staticTexts["The Book of Mozilla"] mozWaitForElementToExist(savedToReadingList) - XCTAssertTrue(savedToReadingList.exists) checkReadingListNumberOfItems(items: 1) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306995 + // https://mozilla.testrail.io/index.php?/cases/view/2306995 func testAddToReadingListPrivateMode() { navigator.nowAt(NewTabScreen) navigator.toggleOn(userState.isPrivate, withAction: Action.TogglePrivateMode) navigator.performAction(Action.OpenNewTabFromTabTray) - mozWaitForElementToExist(app.buttons["urlBar-cancel"], timeout: TIMEOUT) + mozWaitForElementToExist(app.buttons["urlBar-cancel"]) navigator.performAction(Action.CloseURLBarOpen) navigator.nowAt(NewTabScreen) waitForTabsButton() @@ -71,14 +70,13 @@ class ReadingListTests: BaseTestCase { // Check that there is one item let savedToReadingList = app.tables["ReadingTable"].cells.staticTexts["The Book of Mozilla"] mozWaitForElementToExist(savedToReadingList) - XCTAssertTrue(savedToReadingList.exists) checkReadingListNumberOfItems(items: 1) app.buttons["Done"].tap() updateScreenGraph() // Check that it appears on regular mode navigator.toggleOff(userState.isPrivate, withAction: Action.ToggleRegularMode) navigator.performAction(Action.OpenNewTabFromTabTray) - mozWaitForElementToExist(app.buttons["urlBar-cancel"], timeout: TIMEOUT) + mozWaitForElementToExist(app.buttons["urlBar-cancel"]) navigator.performAction(Action.CloseURLBarOpen) navigator.nowAt(NewTabScreen) waitForTabsButton() @@ -86,7 +84,7 @@ class ReadingListTests: BaseTestCase { checkReadingListNumberOfItems(items: 1) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306992 + // https://mozilla.testrail.io/index.php?/cases/view/2306992 func testMarkAsReadAndUreadFromReaderView() { addContentToReaderView() @@ -99,7 +97,7 @@ class ReadingListTests: BaseTestCase { mozWaitForElementToExist(app.buttons["Mark as Read"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306996 + // https://mozilla.testrail.io/index.php?/cases/view/2306996 func testRemoveFromReadingView() { addContentToReaderView() // Once the content has been added, remove it @@ -116,7 +114,7 @@ class ReadingListTests: BaseTestCase { checkReadingListNumberOfItems(items: 0) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306997 + // https://mozilla.testrail.io/index.php?/cases/view/2306997 func testMarkAsReadAndUnreadFromReadingList() throws { addContentToReaderView() navigator.goto(BrowserTabMenu) @@ -125,17 +123,17 @@ class ReadingListTests: BaseTestCase { mozWaitForElementToExist(app.tables["ReadingTable"]) // Check that there is one item let savedToReadingList = app.tables["ReadingTable"].cells.staticTexts["The Book of Mozilla"] - XCTAssertTrue(savedToReadingList.exists) + mozWaitForElementToExist(savedToReadingList) // Mark it as read/unread savedToReadingList.swipeLeft() - mozWaitForElementToExist(app.tables.cells.buttons.staticTexts["Mark as Read"], timeout: TIMEOUT) + mozWaitForElementToExist(app.tables.cells.buttons.staticTexts["Mark as Read"]) app.tables["ReadingTable"].cells.buttons.element(boundBy: 1).tap() savedToReadingList.swipeLeft() mozWaitForElementToExist(app.tables.cells.buttons.staticTexts["Mark as Unread"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306998 + // https://mozilla.testrail.io/index.php?/cases/view/2306998 func testRemoveFromReadingList() { addContentToReaderView() navigator.goto(BrowserTabMenu) @@ -148,13 +146,13 @@ class ReadingListTests: BaseTestCase { savedToReadingList.swipeLeft() mozWaitForElementToExist(app.buttons["Remove"]) app.buttons["Remove"].tap() - XCTAssertFalse(savedToReadingList.exists) + mozWaitForElementToNotExist(savedToReadingList) // Reader list view should be empty checkReadingListNumberOfItems(items: 0) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306999 + // https://mozilla.testrail.io/index.php?/cases/view/2306999 func testAddToReadingListFromBrowserTabMenu() { navigator.nowAt(NewTabScreen) // First time Reading list is empty @@ -172,7 +170,7 @@ class ReadingListTests: BaseTestCase { checkReadingListNumberOfItems(items: 1) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307000 + // https://mozilla.testrail.io/index.php?/cases/view/2307000 func testOpenSavedForReadingLongPressInNewTab() { let numTab = app.buttons["Show Tabs"].value as? String XCTAssertEqual(numTab, "1") @@ -197,7 +195,7 @@ class ReadingListTests: BaseTestCase { // XCTAssertEqual(numTabAfter, "2") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307001 + // https://mozilla.testrail.io/index.php?/cases/view/2307001 func testRemoveSavedForReadingLongPress() { // Add item to Reading List addContentToReaderView() @@ -212,10 +210,9 @@ class ReadingListTests: BaseTestCase { // Verify the item has been removed mozWaitForElementToNotExist(app.tables["ReadingTable"].cells.staticTexts["The Book of Mozilla"]) - XCTAssertFalse(app.tables["ReadingTable"].cells.staticTexts["The Book of Mozilla"].exists) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306893 + // https://mozilla.testrail.io/index.php?/cases/view/2306893 // Smoketest func testReadingList() { navigator.nowAt(NewTabScreen) @@ -237,7 +234,7 @@ class ReadingListTests: BaseTestCase { // Tap on an article savedToReadingList.tap() // The article is displayed in Reader View - mozWaitForElementToExist(app.buttons["Reader View"], timeout: TIMEOUT) + mozWaitForElementToExist(app.buttons["Reader View"]) XCTAssertTrue(app.buttons["Reader View"].isSelected) XCTAssertTrue(app.buttons["Reader View"].isEnabled) app.buttons[AccessibilityIdentifiers.Toolbar.homeButton].tap() @@ -248,8 +245,8 @@ class ReadingListTests: BaseTestCase { mozWaitForElementToExist(app.tables["ReadingTable"].cells.elementContainingText("The Book of Mozilla, read")) savedToReadingList.swipeLeft() // Two options are revealed - mozWaitForElementToExist(app.tables.cells.buttons.staticTexts["Mark as Unread"], timeout: TIMEOUT) - mozWaitForElementToExist(app.tables.cells.buttons.staticTexts["Remove"], timeout: TIMEOUT) + mozWaitForElementToExist(app.tables.cells.buttons.staticTexts["Mark as Unread"]) + mozWaitForElementToExist(app.tables.cells.buttons.staticTexts["Remove"]) // Tap 'Mark as Unread' app.tables.cells.buttons.staticTexts["Mark as Unread"].tap(force: true) // The article has been marked as Unread @@ -264,12 +261,12 @@ class ReadingListTests: BaseTestCase { mozWaitForElementToExist(app.staticTexts[emptyReadingList3]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306993 + // https://mozilla.testrail.io/index.php?/cases/view/2306993 // Smoketest func testAddToReaderListOptions() { addContentToReaderView() // Check that Settings layouts options are shown - mozWaitForElementToExist(app.buttons["ReaderModeBarView.settingsButton"], timeout: TIMEOUT) + mozWaitForElementToExist(app.buttons["ReaderModeBarView.settingsButton"]) app.buttons["ReaderModeBarView.settingsButton"].tap() let layoutOptions = ["Light", "Sepia", "Dark", "Decrease text size", "Reset text size", "Increase text size", "Remove from Reading List", "Mark as Read"] diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ReportSiteTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ReportSiteTests.swift index 5fe7f8e71c57..916dd7a20082 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ReportSiteTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ReportSiteTests.swift @@ -13,7 +13,7 @@ class ReportSiteTests: BaseTestCase { func testReportSiteIssueOn() { launchAndGoToMenu() - XCTAssertTrue(app.tables.otherElements[StandardImageIdentifiers.Large.lightbulb].exists) + mozWaitForElementToExist(app.tables.otherElements[StandardImageIdentifiers.Large.lightbulb]) } func testReportSiteIssueOff() { @@ -23,7 +23,7 @@ class ReportSiteTests: BaseTestCase { launchAndGoToMenu() - XCTAssertFalse(app.tables.otherElements[StandardImageIdentifiers.Large.lightbulb].exists) + mozWaitForElementToNotExist(app.tables.otherElements[StandardImageIdentifiers.Large.lightbulb]) } // MARK: Helper diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/SearchSettingsUITest.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/SearchSettingsUITest.swift index a4cb73c1230b..342948392297 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/SearchSettingsUITest.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/SearchSettingsUITest.swift @@ -9,7 +9,7 @@ let defaultSearchEngine2 = "Bing" let customSearchEngine = ["name": "youtube", "url": "https://youtube.com/search?q=%s"] class SearchSettingsUITests: BaseTestCase { - // https://testrail.stage.mozaws.net/index.php?/cases/view/2435664 + // https://mozilla.testrail.io/index.php?/cases/view/2435664 func testDefaultSearchEngine() { navigator.nowAt(NewTabScreen) navigator.goto(SearchSettings) @@ -24,7 +24,7 @@ class SearchSettingsUITests: BaseTestCase { mozWaitForElementToExist(app.tables.cells.staticTexts[defaultSearchEngine2]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2353247 + // https://mozilla.testrail.io/index.php?/cases/view/2353247 func testCustomSearchEngineIsEditable() { navigator.nowAt(NewTabScreen) navigator.goto(SearchSettings) @@ -59,7 +59,7 @@ class SearchSettingsUITests: BaseTestCase { mozWaitForElementToExist(app.tables.cells.staticTexts[customSearchEngine["name"]!]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2353248 + // https://mozilla.testrail.io/index.php?/cases/view/2353248 func testCustomSearchEngineAsDefaultIsNotEditable() { navigator.nowAt(NewTabScreen) navigator.goto(SearchSettings) @@ -79,7 +79,7 @@ class SearchSettingsUITests: BaseTestCase { XCTAssertFalse(app.buttons["Edit"].isEnabled) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2353249 + // https://mozilla.testrail.io/index.php?/cases/view/2353249 func testNavigateToSearchPickerTurnsOffEditing() { navigator.nowAt(NewTabScreen) navigator.goto(SearchSettings) @@ -105,7 +105,7 @@ class SearchSettingsUITests: BaseTestCase { XCTAssertEqual(app.tables.cells.switches.count, app.tables.cells.count - 3) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2353250 + // https://mozilla.testrail.io/index.php?/cases/view/2353250 func testDeletingLastCustomEngineExitsEditing() { navigator.nowAt(NewTabScreen) navigator.goto(SearchSettings) diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/SearchTest.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/SearchTest.swift index 99ba97255007..b0234b47872e 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/SearchTest.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/SearchTest.swift @@ -15,7 +15,7 @@ private let SuggestedSite6: String = "foobar bomb baby" class SearchTests: BaseTestCase { private func typeOnSearchBar(text: String) { - mozWaitForElementToExist(app.textFields.firstMatch, timeout: 10) + mozWaitForElementToExist(app.textFields.firstMatch) app.textFields.firstMatch.tap() app.textFields.firstMatch.tap() app.textFields.firstMatch.typeText(text) @@ -37,10 +37,10 @@ class SearchTests: BaseTestCase { // In the search suggestion, "text" should be displayed let predicate = NSPredicate(format: "label CONTAINS[c] %@", "http://localhost:") let elementQuery = app.staticTexts.containing(predicate) - XCTAssertTrue(elementQuery.element.exists) + mozWaitForElementToExist(elementQuery.element) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2436093 + // https://mozilla.testrail.io/index.php?/cases/view/2436093 func testPromptPresence() { // Suggestion is on by default (starting on Oct 24th 2017), so the prompt should not appear navigator.goto(URLBarOpen) @@ -49,7 +49,6 @@ class SearchTests: BaseTestCase { // Suggestions should be shown mozWaitForElementToExist(app.tables["SiteTable"].cells.firstMatch) - XCTAssertTrue(app.tables["SiteTable"].cells.firstMatch.exists) // Disable Search suggestion app.buttons["urlBar-cancel"].tap() @@ -65,7 +64,6 @@ class SearchTests: BaseTestCase { navigator.goto(URLBarOpen) typeOnSearchBar(text: "foobar") mozWaitForElementToNotExist(app.tables["SiteTable"].cells.firstMatch) - XCTAssertFalse(app.tables["SiteTable"].cells.firstMatch.exists) // Verify that previous choice is remembered app.buttons["urlBar-cancel"].tap() @@ -74,7 +72,6 @@ class SearchTests: BaseTestCase { typeOnSearchBar(text: "foobar") mozWaitForElementToNotExist(app.tables["SiteTable"].cells[SuggestedSite]) - XCTAssertFalse(app.tables["SiteTable"].cells.firstMatch.exists) app.buttons["urlBar-cancel"].tap() waitForTabsButton() @@ -89,10 +86,9 @@ class SearchTests: BaseTestCase { // Suggestions prompt should appear typeOnSearchBar(text: "foobar") mozWaitForElementToExist(app.tables["SiteTable"].cells.firstMatch) - XCTAssertTrue(app.tables["SiteTable"].cells.firstMatch.exists) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2436094 + // https://mozilla.testrail.io/index.php?/cases/view/2436094 func testDoNotShowSuggestionsWhenEnteringURL() { // According to bug 1192155 if a string contains /, do not show suggestions, if there a space an a string, // the suggestions are shown again @@ -105,8 +101,7 @@ class SearchTests: BaseTestCase { if !(app.tables["SiteTable"].cells.staticTexts[SuggestedSite].exists) { if !(app.tables["SiteTable"].cells.staticTexts[SuggestedSite2].exists) { mozWaitForElementToExist( - app.tables["SiteTable"].cells.staticTexts[SuggestedSite3], - timeout: 5 + app.tables["SiteTable"].cells.staticTexts[SuggestedSite3] ) } } @@ -127,26 +122,25 @@ class SearchTests: BaseTestCase { } } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2436095 + // https://mozilla.testrail.io/index.php?/cases/view/2436095 func testCopyPasteComplete() { // Copy, Paste and Go to url navigator.goto(URLBarOpen) typeOnSearchBar(text: "www.mozilla.org") app.textFields["address"].press(forDuration: 5) app.menuItems["Select All"].tap() - mozWaitForElementToExist(app.menuItems["Copy"], timeout: 3) + mozWaitForElementToExist(app.menuItems["Copy"]) app.menuItems["Copy"].tap() mozWaitForElementToExist(app.buttons["urlBar-cancel"]) app.buttons["urlBar-cancel"].tap() navigator.nowAt(HomePanelsScreen) mozWaitForElementToExist( - app.collectionViews.cells[AccessibilityIdentifiers.FirefoxHomepage.TopSites.itemCell], - timeout: 10 + app.collectionViews.cells[AccessibilityIdentifiers.FirefoxHomepage.TopSites.itemCell] ) - mozWaitForElementToExist(app.textFields["url"], timeout: 3) + mozWaitForElementToExist(app.textFields["url"]) app.textFields["url"].tap() - mozWaitForElementToExist(app.textFields["address"], timeout: 3) + mozWaitForElementToExist(app.textFields["address"]) app.textFields["address"].tap() mozWaitForElementToExist(app.menuItems["Paste"]) @@ -181,11 +175,11 @@ class SearchTests: BaseTestCase { tablesQuery2.staticTexts[searchEngine].tap() navigator.openURL("foo bar") - mozWaitForElementToExist(app.webViews.firstMatch, timeout: 3) + mozWaitForElementToExist(app.webViews.firstMatch) mozWaitForValueContains(app.textFields["url"], value: searchEngine.lowercased()) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306940 + // https://mozilla.testrail.io/index.php?/cases/view/2306940 // Smoketest func testSearchEngine() { navigator.nowAt(NewTabScreen) @@ -197,19 +191,19 @@ class SearchTests: BaseTestCase { changeSearchEngine(searchEngine: "Wikipedia") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2353246 + // https://mozilla.testrail.io/index.php?/cases/view/2353246 func testDefaultSearchEngine() { navigator.nowAt(NewTabScreen) navigator.goto(SearchSettings) XCTAssert(app.tables.staticTexts["Google"].exists) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2436091 + // https://mozilla.testrail.io/index.php?/cases/view/2436091 func testSearchWithFirefoxOption() { navigator.nowAt(NewTabScreen) navigator.openURL(path(forTestPage: "test-mozilla-book.html")) waitUntilPageLoad() - mozWaitForElementToExist(app.webViews.staticTexts["cloud"], timeout: 10) + mozWaitForElementToExist(app.webViews.staticTexts["cloud"]) // Select some text and long press to find the option app.webViews.staticTexts["cloud"].press(forDuration: 1) // Click on the > button to get to that option only on iPhone @@ -236,18 +230,18 @@ class SearchTests: BaseTestCase { XCTAssertEqual("2", numTab) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2436092 + // https://mozilla.testrail.io/index.php?/cases/view/2436092 // Smoketest func testSearchStartAfterTypingTwoWords() { navigator.goto(URLBarOpen) - mozWaitForElementToExist(app.textFields["url"], timeout: 10) + mozWaitForElementToExist(app.textFields["url"]) app.typeText("foo bar") app.typeText(XCUIKeyboardKey.return.rawValue) - mozWaitForElementToExist(app.textFields["url"], timeout: 20) + mozWaitForElementToExist(app.textFields["url"]) mozWaitForValueContains(app.textFields["url"], value: "google") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306943 + // https://mozilla.testrail.io/index.php?/cases/view/2306943 func testSearchIconOnAboutHome() throws { if iPad() { throw XCTSkip("iPad does not have search icon") @@ -257,7 +251,7 @@ class SearchTests: BaseTestCase { // Search icon is displayed. mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.searchButton]) XCTAssertEqual(app.buttons[AccessibilityIdentifiers.Toolbar.searchButton].label, "Search") - XCTAssertTrue(app.buttons[AccessibilityIdentifiers.Toolbar.searchButton].exists) + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.searchButton]) app.buttons[AccessibilityIdentifiers.Toolbar.searchButton].tap() let addressBar = app.textFields["address"] @@ -273,7 +267,7 @@ class SearchTests: BaseTestCase { mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.homeButton]) XCTAssertEqual(app.buttons[AccessibilityIdentifiers.Toolbar.homeButton].label, "Home") app.buttons[AccessibilityIdentifiers.Toolbar.homeButton].tap() - waitForExistence(app.buttons[AccessibilityIdentifiers.Toolbar.backButton]) + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.backButton]) app.buttons[AccessibilityIdentifiers.Toolbar.backButton].tap() mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.homeButton]) @@ -289,7 +283,7 @@ class SearchTests: BaseTestCase { } } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306989 + // https://mozilla.testrail.io/index.php?/cases/view/2306989 // Smoketest func testOpenTabsInSearchSuggestions() throws { if #unavailable(iOS 16) { @@ -303,15 +297,14 @@ class SearchTests: BaseTestCase { restartInBackground() // Open new tab mozWaitForElementToExist( - app.buttons[AccessibilityIdentifiers.Browser.UrlBar.cancelButton], - timeout: TIMEOUT + app.buttons[AccessibilityIdentifiers.Browser.UrlBar.cancelButton] ) navigator.performAction(Action.CloseURLBarOpen) waitForTabsButton() validateSearchSuggestionText(typeText: "localhost") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306886 + // https://mozilla.testrail.io/index.php?/cases/view/2306886 // SmokeTest func testBottomVIewURLBar() throws { if iPad() { @@ -364,7 +357,7 @@ class SearchTests: BaseTestCase { } } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306942 + // https://mozilla.testrail.io/index.php?/cases/view/2306942 func testSearchSuggestions() { // Tap on URL Bar and type "g" navigator.nowAt(NewTabScreen) @@ -455,7 +448,7 @@ class SearchTests: BaseTestCase { // navigator.nowAt(NewTabScreen) // navigator.goto(SearchSettings) // navigator.nowAt(SearchSettings) -// +// // // By default, disable search suggest in private mode // let privateModeSearchSuggestSwitch = app.otherElements.tables.cells[ // AccessibilityIdentifiers.Settings.Search.disableSearchSuggestsInPrivateMode diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/SettingsTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/SettingsTests.swift index c04d0ad7c462..831d4709496b 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/SettingsTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/SettingsTests.swift @@ -15,7 +15,7 @@ class SettingsTests: BaseTestCase { } } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2334757 + // https://mozilla.testrail.io/index.php?/cases/view/2334757 func testHelpOpensSUMOInTab() { navigator.nowAt(NewTabScreen) navigator.goto(SettingsScreen) @@ -36,15 +36,15 @@ class SettingsTests: BaseTestCase { XCTAssertEqual("2", numTabs as? String, "Sume should be open in a different tab") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2334760 + // https://mozilla.testrail.io/index.php?/cases/view/2334760 func testOpenSiriOption() { waitForTabsButton() navigator.nowAt(NewTabScreen) navigator.performAction(Action.OpenSiriFromSettings) - mozWaitForElementToExist(app.cells["SiriSettings"], timeout: 5) + mozWaitForElementToExist(app.cells["SiriSettings"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2334756 + // https://mozilla.testrail.io/index.php?/cases/view/2334756 func testCopiedLinks() { navigator.nowAt(NewTabScreen) navigator.goto(SettingsScreen) @@ -76,7 +76,7 @@ class SettingsTests: BaseTestCase { XCTAssertEqual(value3 as? String, "1") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307041 + // https://mozilla.testrail.io/index.php?/cases/view/2307041 func testOpenMailAppSettings() { waitForTabsButton() navigator.nowAt(NewTabScreen) @@ -84,10 +84,9 @@ class SettingsTests: BaseTestCase { // Check that the list is shown mozWaitForElementToExist(app.tables["OpenWithPage.Setting.Options"]) - XCTAssertTrue(app.tables["OpenWithPage.Setting.Options"].exists) // Check that the list is shown with all elements disabled - XCTAssertTrue(app.tables.staticTexts["OPEN MAIL LINKS WITH"].exists) + mozWaitForElementToExist(app.tables.staticTexts["OPEN MAIL LINKS WITH"]) XCTAssertFalse(app.tables.cells.staticTexts["Mail"].isSelected) XCTAssertFalse(app.tables.cells.staticTexts["Outlook"].isSelected) XCTAssertFalse(app.tables.cells.staticTexts["ProtonMail"].isSelected) @@ -108,7 +107,7 @@ class SettingsTests: BaseTestCase { navigator.goto(SettingsScreen) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307058 + // https://mozilla.testrail.io/index.php?/cases/view/2307058 // Functionality is tested by UITests/NoImageModeTests, here only the UI is updated properly // SmokeTest func testImageOnOff() { @@ -134,10 +133,10 @@ class SettingsTests: BaseTestCase { checkShowImages(showImages: true) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306808 + // https://mozilla.testrail.io/index.php?/cases/view/2306808 // Smoketest func testSettingsOptionSubtitles() { - mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton], timeout: TIMEOUT) + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton]) navigator.nowAt(NewTabScreen) navigator.goto(SettingsScreen) let table = app.tables.element(boundBy: 0) diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/SyncFAUITests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/SyncFAUITests.swift index e846d81efa6f..aa9aab121245 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/SyncFAUITests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/SyncFAUITests.swift @@ -16,7 +16,7 @@ var uid: String! var code: String! class SyncUITests: BaseTestCase { - // https://testrail.stage.mozaws.net/index.php?/cases/view/2448597 + // https://mozilla.testrail.io/index.php?/cases/view/2448597 func testSyncUIFromBrowserTabMenu() { // Check menu available from HomeScreenPanel waitForTabsButton() @@ -30,15 +30,10 @@ class SyncUITests: BaseTestCase { private func verifyFxASigninScreen() { mozWaitForElementToExist( - app.navigationBars[AccessibilityIdentifiers.Settings.FirefoxAccount.fxaNavigationBar], - timeout: 30 + app.navigationBars[AccessibilityIdentifiers.Settings.FirefoxAccount.fxaNavigationBar] ) mozWaitForElementToExist( - app.webViews.textFields[AccessibilityIdentifiers.Settings.FirefoxAccount.emailTextField], - timeout: 10 - ) - XCTAssertTrue( - app.webViews.textFields[AccessibilityIdentifiers.Settings.FirefoxAccount.emailTextField].exists + app.webViews.textFields[AccessibilityIdentifiers.Settings.FirefoxAccount.emailTextField] ) // Verify the placeholdervalues here for the textFields @@ -49,23 +44,22 @@ class SyncUITests: BaseTestCase { defaultMailPlaceholder, "The mail placeholder does not show the correct value" ) - XCTAssertTrue(app.webViews.buttons[AccessibilityIdentifiers.Settings.FirefoxAccount.continueButton].exists) + mozWaitForElementToExist(app.webViews.buttons[AccessibilityIdentifiers.Settings.FirefoxAccount.continueButton]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2448874 + // https://mozilla.testrail.io/index.php?/cases/view/2448874 func testTypeOnGivenFields() { waitForTabsButton() navigator.nowAt(NewTabScreen) navigator.goto(FxASigninScreen) mozWaitForElementToExist( app.navigationBars[AccessibilityIdentifiers.Settings.FirefoxAccount.fxaNavigationBar], - timeout: 60 + timeout: TIMEOUT_LONG ) // Tap Sign in without any value in email Password focus on Email mozWaitForElementToExist( - app.webViews.buttons[AccessibilityIdentifiers.Settings.FirefoxAccount.continueButton], - timeout: 20 + app.webViews.buttons[AccessibilityIdentifiers.Settings.FirefoxAccount.continueButton] ) navigator.performAction(Action.FxATapOnContinueButton) mozWaitForElementToExist(app.webViews.staticTexts["Valid email required"]) @@ -73,7 +67,7 @@ class SyncUITests: BaseTestCase { // Enter only email, wrong and correct and tap sign in userState.fxaUsername = "foo1bar2baz3@gmail.com" navigator.performAction(Action.FxATypeEmail) - navigator.performAction(Action.FxATapOnSignInButton) + navigator.performAction(Action.FxATapOnContinueButton) // Enter invalid (too short, it should be at least 8 chars) and incorrect password userState.fxaPassword = "foo" @@ -89,14 +83,14 @@ class SyncUITests: BaseTestCase { mozWaitForElementToNotExist(app.webViews.staticTexts["At least 8 characters"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2449603 + // https://mozilla.testrail.io/index.php?/cases/view/2449603 func testCreateAnAccountLink() { navigator.nowAt(NewTabScreen) navigator.goto(FxASigninScreen) - mozWaitForElementToExist(app.webViews.firstMatch, timeout: 20) + mozWaitForElementToExist(app.webViews.firstMatch, timeout: TIMEOUT_LONG) mozWaitForElementToExist( app.webViews.textFields[AccessibilityIdentifiers.Settings.FirefoxAccount.emailTextField], - timeout: 40 + timeout: TIMEOUT_LONG ) userState.fxaUsername = "foo1bar2@gmail.com" navigator.performAction(Action.FxATypeEmail) @@ -104,7 +98,7 @@ class SyncUITests: BaseTestCase { mozWaitForElementToExist(app.webViews.buttons["Create account"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2449604 + // https://mozilla.testrail.io/index.php?/cases/view/2449604 func testShowPassword() { // The aim of this test is to check if the option to show password is shown when user starts typing // and disappears when no password is typed @@ -113,7 +107,7 @@ class SyncUITests: BaseTestCase { navigator.goto(FxASigninScreen) mozWaitForElementToExist( app.webViews.textFields[AccessibilityIdentifiers.Settings.FirefoxAccount.emailTextField], - timeout: 20 + timeout: TIMEOUT_LONG ) // Typing on Email should not show Show (password) option userState.fxaUsername = "iosmztest@gmail.com" @@ -124,13 +118,13 @@ class SyncUITests: BaseTestCase { mozWaitForElementToExist(app.secureTextFields.element(boundBy: 1)) navigator.performAction(Action.FxATypePasswordNewAccount) let passMessage = "Your password is currently hidden." - mozWaitForElementToExist(app.webViews.buttons[passMessage], timeout: 3) + mozWaitForElementToExist(app.webViews.buttons[passMessage]) // Remove the password typed, Show (password) option should not be shown app.keyboards.keys["delete"].tap() mozWaitForElementToNotExist(app.webViews.staticTexts[passMessage]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2449605 + // https://mozilla.testrail.io/index.php?/cases/view/2449605 func testQRPairing() { waitForTabsButton() navigator.nowAt(NewTabScreen) @@ -138,8 +132,7 @@ class SyncUITests: BaseTestCase { // QR does not work on sim but checking that the button works, no crash navigator.performAction(Action.OpenEmailToQR) mozWaitForElementToExist( - app.navigationBars[AccessibilityIdentifiers.Settings.FirefoxAccount.fxaNavigationBar], - timeout: 5 + app.navigationBars[AccessibilityIdentifiers.Settings.FirefoxAccount.fxaNavigationBar] ) mozWaitForElementToExist(app.buttons["Ready to Scan"]) mozWaitForElementToExist(app.buttons["Use Email Instead"]) diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/TabCounterTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/TabCounterTests.swift index a0a9e63f67a7..5655667d89cf 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/TabCounterTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/TabCounterTests.swift @@ -6,7 +6,7 @@ import Common import XCTest class TabCounterTests: BaseTestCase { - // https://testrail.stage.mozaws.net/index.php?/cases/view/2359077 + // https://mozilla.testrail.io/index.php?/cases/view/2359077 func testTabIncrement() throws { navigator.nowAt(NewTabScreen) waitForTabsButton() @@ -35,7 +35,7 @@ class TabCounterTests: BaseTestCase { } } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2359078 + // https://mozilla.testrail.io/index.php?/cases/view/2359078 func testTabDecrement() throws { navigator.nowAt(NewTabScreen) waitForTabsButton() @@ -83,7 +83,7 @@ class TabCounterTests: BaseTestCase { tabsOpen = app.segmentedControls.buttons.element(boundBy: 0).label XCTAssertTrue(app.segmentedControls.buttons.element(boundBy: 0).isSelected) if !isTablet { - mozWaitForElementToExist(app.segmentedControls.firstMatch, timeout: 5) + mozWaitForElementToExist(app.segmentedControls.firstMatch) let tabsOpenTabTray: String = app.segmentedControls.buttons.firstMatch.label XCTAssertTrue(tabsOpenTabTray.hasSuffix("1")) } diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ThirdPartySearchTest.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ThirdPartySearchTest.swift index ab3e22c5fa63..872b8b3a6743 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ThirdPartySearchTest.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ThirdPartySearchTest.swift @@ -11,11 +11,11 @@ class ThirdPartySearchTest: BaseTestCase { app.buttons["Done"].tap() } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2443998 + // https://mozilla.testrail.io/index.php?/cases/view/2443998 func testCustomSearchEngines() { addCustomSearchEngine() - mozWaitForElementToExist(app.navigationBars["Search"].buttons["Settings"], timeout: 3) + mozWaitForElementToExist(app.navigationBars["Search"].buttons["Settings"]) app.navigationBars["Search"].buttons["Settings"].tap() mozWaitForElementToExist(app.navigationBars["Settings"].buttons[AccessibilityIdentifiers.Settings.navigationBarItem]) app.navigationBars["Settings"].buttons[AccessibilityIdentifiers.Settings.navigationBarItem].tap() @@ -36,7 +36,7 @@ class ThirdPartySearchTest: BaseTestCase { XCTAssert(url.hasPrefix("https://developer.mozilla.org/en-US"), "The URL should indicate that the search was performed on MDN and not the default") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2444328 + // https://mozilla.testrail.io/index.php?/cases/view/2444328 func testCustomSearchEngineAsDefault() { addCustomSearchEngine() @@ -61,10 +61,10 @@ class ThirdPartySearchTest: BaseTestCase { XCTAssert(url.hasPrefix("https://developer.mozilla.org/en-US/search"), "The URL should indicate that the search was performed on MDN and not the default") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306941 + // https://mozilla.testrail.io/index.php?/cases/view/2306941 func testCustomSearchEngineDeletion() { addCustomSearchEngine() - mozWaitForElementToExist(app.navigationBars["Search"].buttons["Settings"], timeout: 3) + mozWaitForElementToExist(app.navigationBars["Search"].buttons["Settings"]) app.navigationBars["Search"].buttons["Settings"].tap() mozWaitForElementToExist(app.navigationBars["Settings"].buttons[AccessibilityIdentifiers.Settings.navigationBarItem]) @@ -75,10 +75,9 @@ class ThirdPartySearchTest: BaseTestCase { app.textFields.firstMatch.press(forDuration: 1) app.staticTexts["Paste"].tap() mozWaitForElementToExist(app.scrollViews.otherElements.buttons["Mozilla Engine search"]) - XCTAssertTrue(app.scrollViews.otherElements.buttons["Mozilla Engine search"].exists) // Need to go step by step to Search Settings. The ScreenGraph will fail to go to the Search Settings Screen - mozWaitForElementToExist(app.buttons["urlBar-cancel"], timeout: 3) + mozWaitForElementToExist(app.buttons["urlBar-cancel"]) app.buttons["urlBar-cancel"].tap() app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton].tap() app.tables["Context Menu"].otherElements["Settings"].tap() @@ -91,7 +90,7 @@ class ThirdPartySearchTest: BaseTestCase { dismissSearchScreen() // Perform a search to check - mozWaitForElementToExist(app.textFields["url"], timeout: 3) + mozWaitForElementToExist(app.textFields["url"]) app.textFields["url"].tap() mozWaitForElementToExist(app.buttons["urlBar-cancel"]) UIPasteboard.general.string = "window" @@ -99,7 +98,6 @@ class ThirdPartySearchTest: BaseTestCase { app.staticTexts["Paste"].tap() mozWaitForElementToNotExist(app.scrollViews.otherElements.buttons["Mozilla Engine search"]) - XCTAssertFalse(app.scrollViews.otherElements.buttons["Mozilla Engine search"].exists) } } @@ -107,7 +105,7 @@ class ThirdPartySearchTest: BaseTestCase { waitForTabsButton() navigator.nowAt(NewTabScreen) navigator.performAction(Action.AddCustomSearchEngine) - mozWaitForElementToExist(app.buttons["customEngineSaveButton"], timeout: 3) + mozWaitForElementToExist(app.buttons["customEngineSaveButton"]) app.buttons["customEngineSaveButton"].tap() if #unavailable(iOS 16) { // Wait for "Fennec pasted from XCUITests-Runner" banner to disappear @@ -122,7 +120,7 @@ class ThirdPartySearchTest: BaseTestCase { app.navigationBars["Settings"].buttons[AccessibilityIdentifiers.Settings.navigationBarItem].tap() } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2444333 + // https://mozilla.testrail.io/index.php?/cases/view/2444333 func testCustomEngineFromIncorrectTemplate() { waitForTabsButton() navigator.nowAt(NewTabScreen) @@ -136,7 +134,7 @@ class ThirdPartySearchTest: BaseTestCase { .textViews["customEngineUrl"] .staticTexts["URL (Replace Query with %s)"] - XCTAssertTrue(customengineurlTextView.exists) + mozWaitForElementToExist(customengineurlTextView) UIPasteboard.general.string = searchUrl customengineurlTextView.tap() @@ -153,9 +151,9 @@ class ThirdPartySearchTest: BaseTestCase { pasteOption.tap() } - mozWaitForElementToExist(app.buttons["customEngineSaveButton"], timeout: 3) + mozWaitForElementToExist(app.buttons["customEngineSaveButton"]) app.buttons["customEngineSaveButton"].tap() - mozWaitForElementToExist(app.navigationBars["Add Search Engine"], timeout: 3) + mozWaitForElementToExist(app.navigationBars["Add Search Engine"]) app.navigationBars["Add Search Engine"].buttons["Save"].tap() // The alert appears on iOS 15 but it disappears by itself immediately. diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ToolbarMenuTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ToolbarMenuTests.swift index a45933ad10f7..2a83df7e52ec 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ToolbarMenuTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ToolbarMenuTests.swift @@ -11,7 +11,7 @@ class ToolbarMenuTests: BaseTestCase { super.tearDown() } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306840 + // https://mozilla.testrail.io/index.php?/cases/view/2306840 func testToolbarMenu() { navigator.nowAt(NewTabScreen) let hamburgerMenu = app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton] @@ -45,7 +45,7 @@ class ToolbarMenuTests: BaseTestCase { mozWaitForElementToExist(app.tables["Context Menu"]) validateMenuOptions() XCUIDevice.shared.orientation = .landscapeLeft - mozWaitForElementToExist(hamburgerMenu, timeout: 15) + mozWaitForElementToExist(hamburgerMenu) mozWaitForElementToNotExist(app.tables["Context Menu"]) mozWaitForElementToExist(app.textFields["url"]) mozWaitForElementToExist(app.webViews["contentView"]) @@ -75,16 +75,15 @@ class ToolbarMenuTests: BaseTestCase { } private func validateMenuOptions() { - XCTAssertTrue(app.tables.otherElements[StandardImageIdentifiers.Large.bookmarkTrayFill].exists) - XCTAssertTrue(app.tables.otherElements[StandardImageIdentifiers.Large.history].exists) - XCTAssertTrue(app.tables.otherElements[StandardImageIdentifiers.Large.download].exists) - XCTAssertTrue(app.tables.otherElements[StandardImageIdentifiers.Large.readingList].exists) - XCTAssertTrue(app.tables.otherElements[StandardImageIdentifiers.Large.login].exists) - XCTAssertTrue(app.tables.otherElements[StandardImageIdentifiers.Large.sync].exists) - XCTAssertTrue(app.tables.otherElements[StandardImageIdentifiers.Large.nightMode].exists) - XCTAssertTrue(app.tables.otherElements[StandardImageIdentifiers.Large.whatsNew].exists) - XCTAssertTrue(app.tables.otherElements[StandardImageIdentifiers.Large.helpCircle].exists) - XCTAssertTrue(app.tables.otherElements[StandardImageIdentifiers.Large.edit].exists) - XCTAssertTrue(app.tables.otherElements[StandardImageIdentifiers.Large.settings].exists) + mozWaitForElementToExist(app.tables.otherElements[StandardImageIdentifiers.Large.bookmarkTrayFill]) + mozWaitForElementToExist(app.tables.otherElements[StandardImageIdentifiers.Large.download]) + mozWaitForElementToExist(app.tables.otherElements[StandardImageIdentifiers.Large.readingList]) + mozWaitForElementToExist(app.tables.otherElements[StandardImageIdentifiers.Large.login]) + mozWaitForElementToExist(app.tables.otherElements[StandardImageIdentifiers.Large.sync]) + mozWaitForElementToExist(app.tables.otherElements[StandardImageIdentifiers.Large.nightMode]) + mozWaitForElementToExist(app.tables.otherElements[StandardImageIdentifiers.Large.whatsNew]) + mozWaitForElementToExist(app.tables.otherElements[StandardImageIdentifiers.Large.helpCircle]) + mozWaitForElementToExist(app.tables.otherElements[StandardImageIdentifiers.Large.edit]) + mozWaitForElementToExist(app.tables.otherElements[StandardImageIdentifiers.Large.settings]) } } diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ToolbarTest.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ToolbarTest.swift index c127c4fadaec..e9cd45d8f2bd 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ToolbarTest.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ToolbarTest.swift @@ -23,7 +23,7 @@ class ToolbarTests: BaseTestCase { super.tearDown() } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2344428 + // https://mozilla.testrail.io/index.php?/cases/view/2344428 /** * Tests landscape page navigation enablement with the URL bar with tab switching. */ @@ -74,7 +74,7 @@ class ToolbarTests: BaseTestCase { XCTAssertTrue(app.buttons[AccessibilityIdentifiers.Toolbar.forwardButton].isEnabled) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2344430 + // https://mozilla.testrail.io/index.php?/cases/view/2344430 func testClearURLTextUsingBackspace() { navigator.openURL(website1["url"]!) waitUntilPageLoad() @@ -94,7 +94,7 @@ class ToolbarTests: BaseTestCase { // Check that after scrolling on a page, the URL bar is hidden. Tapping one on the status bar will reveal // the URL bar, tapping again on the status will scroll to the top // Skipping for iPad for now, not sure how to implement it there - // https://testrail.stage.mozaws.net/index.php?/cases/view/2344431 + // https://mozilla.testrail.io/index.php?/cases/view/2344431 func testRevealToolbarWhenTappingOnStatusbar() { if !iPad() { // Workaround when testing on iPhone. If the orientation is in landscape on iPhone the tests will fail. @@ -126,7 +126,7 @@ class ToolbarTests: BaseTestCase { } } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306870 + // https://mozilla.testrail.io/index.php?/cases/view/2306870 func testOpenNewTabButtonOnToolbar() throws { if iPad() { throw XCTSkip("iPhone only test") diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/TopTabsTest.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/TopTabsTest.swift index 8059ec4c6593..8c2d607ed126 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/TopTabsTest.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/TopTabsTest.swift @@ -18,11 +18,11 @@ let urlValueLongExample = "localhost:\(serverPort)/test-fixture/test-example.htm let toastUrl = ["url": "twitter.com", "link": "About", "urlLabel": "about"] class TopTabsTest: BaseTestCase { - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307042 + // https://mozilla.testrail.io/index.php?/cases/view/2307042 // Smoketest func testAddTabFromTabTray() throws { XCTExpectFailure("The app was not launched", strict: false) { - mozWaitForElementToExist(app.collectionViews["FxCollectionView"], timeout: TIMEOUT) + mozWaitForElementToExist(app.collectionViews["FxCollectionView"]) } navigator.nowAt(NewTabScreen) waitForTabsButton() @@ -36,15 +36,15 @@ class TopTabsTest: BaseTestCase { // The tab tray shows the correct tabs if iPad() { - mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton], timeout: 15) + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton]) app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton].tap() } else { navigator.goto(TabTray) } - mozWaitForElementToExist(app.cells.staticTexts[urlLabel], timeout: TIMEOUT) + mozWaitForElementToExist(app.cells.staticTexts[urlLabel]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2354300 + // https://mozilla.testrail.io/index.php?/cases/view/2354300 func testAddTabFromContext() { navigator.nowAt(NewTabScreen) navigator.openURL(urlExample) @@ -53,7 +53,7 @@ class TopTabsTest: BaseTestCase { XCTAssertEqual("1", tabsOpenInitially as? String) // Open link in a different tab and switch to it - mozWaitForElementToExist(app.webViews.links.staticTexts["More information..."], timeout: 5) + mozWaitForElementToExist(app.webViews.links.staticTexts["More information..."]) app.webViews.links.staticTexts["More information..."].press(forDuration: 5) app.buttons["Open in New Tab"].tap() waitUntilPageLoad() @@ -63,7 +63,7 @@ class TopTabsTest: BaseTestCase { mozWaitForElementToExist(app.cells.staticTexts["Example Domain"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2354447 + // https://mozilla.testrail.io/index.php?/cases/view/2354447 func testSwitchBetweenTabs() { // Open two urls from tab tray and switch between them navigator.openURL(path(forTestPage: "test-mozilla-org.html")) @@ -88,7 +88,7 @@ class TopTabsTest: BaseTestCase { XCTAssertEqual(value, urlValueLongExample) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2354449 + // https://mozilla.testrail.io/index.php?/cases/view/2354449 func testCloseOneTab() { navigator.openURL(path(forTestPage: "test-mozilla-org.html")) waitUntilPageLoad() @@ -116,7 +116,7 @@ class TopTabsTest: BaseTestCase { } } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306865 + // https://mozilla.testrail.io/index.php?/cases/view/2306865 // Smoketest func testCloseAllTabsUndo() { navigator.nowAt(NewTabScreen) @@ -126,13 +126,13 @@ class TopTabsTest: BaseTestCase { waitForTabsButton() navigator.nowAt(BrowserTab) if iPad() { - mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton], timeout: 10) + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton]) app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton].tap() - mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.TabTray.newTabButton], timeout: 10) + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.TabTray.newTabButton]) app.buttons[AccessibilityIdentifiers.TabTray.newTabButton].tap() } else { navigator.performAction(Action.OpenNewTabFromTabTray) - mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton], timeout: 5) + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton]) } if iPad() { @@ -149,12 +149,11 @@ class TopTabsTest: BaseTestCase { app.otherElements.buttons.staticTexts["Undo"].tap() mozWaitForElementToExist( - app.collectionViews.cells[AccessibilityIdentifiers.FirefoxHomepage.TopSites.itemCell], - timeout: 5 + app.collectionViews.cells[AccessibilityIdentifiers.FirefoxHomepage.TopSites.itemCell] ) navigator.nowAt(BrowserTab) if !iPad() { - mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton], timeout: 5) + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton]) } if iPad() { @@ -167,7 +166,7 @@ class TopTabsTest: BaseTestCase { mozWaitForElementToExist(app.cells.staticTexts[urlLabel]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2354473 + // https://mozilla.testrail.io/index.php?/cases/view/2354473 // Smoketest func testCloseAllTabsPrivateModeUndo() { navigator.goto(URLBarOpen) @@ -180,13 +179,13 @@ class TopTabsTest: BaseTestCase { waitForTabsButton() if iPad() { - mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton], timeout: 10) + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton]) app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton].tap() - mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.TabTray.newTabButton], timeout: 10) + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.TabTray.newTabButton]) app.buttons[AccessibilityIdentifiers.TabTray.newTabButton].tap() } else { navigator.performAction(Action.OpenNewTabFromTabTray) - mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton], timeout: 5) + mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.tabsButton]) } navigator.goto(URLBarOpen) @@ -198,10 +197,10 @@ class TopTabsTest: BaseTestCase { } // Close all tabs, undo it and check that the number of tabs is correct navigator.performAction(Action.AcceptRemovingAllTabs) - mozWaitForElementToExist(app.staticTexts["Private Browsing"], timeout: 10) + mozWaitForElementToExist(app.staticTexts["Private Browsing"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2354579 + // https://mozilla.testrail.io/index.php?/cases/view/2354579 func testCloseAllTabs() { // A different tab than home is open to do the proper checks navigator.openURL(path(forTestPage: "test-mozilla-org.html")) @@ -226,7 +225,7 @@ class TopTabsTest: BaseTestCase { mozWaitForElementToExist(app.cells.staticTexts["Homepage"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2354580 + // https://mozilla.testrail.io/index.php?/cases/view/2354580 func testCloseAllTabsPrivateMode() { // A different tab than home is open to do the proper checks navigator.toggleOn(userState.isPrivate, withAction: Action.TogglePrivateMode) @@ -243,10 +242,10 @@ class TopTabsTest: BaseTestCase { checkNumberOfTabsExpectedToBeOpen(expectedNumberOfTabsOpen: 2) // Close all tabs and check that the number of tabs is correct navigator.performAction(Action.AcceptRemovingAllTabs) - mozWaitForElementToExist(app.staticTexts["Private Browsing"], timeout: TIMEOUT) + mozWaitForElementToExist(app.staticTexts["Private Browsing"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306884 + // https://mozilla.testrail.io/index.php?/cases/view/2306884 // Smoketest func testOpenNewTabLandscape() { XCUIDevice.shared.orientation = .landscapeLeft @@ -256,8 +255,7 @@ class TopTabsTest: BaseTestCase { app.buttons[AccessibilityIdentifiers.Toolbar.addNewTabButton].tap() } else { mozWaitForElementToExist( - app.buttons[AccessibilityIdentifiers.Toolbar.addNewTabButton], - timeout: 15 + app.buttons[AccessibilityIdentifiers.Toolbar.addNewTabButton] ) app.buttons[AccessibilityIdentifiers.Toolbar.addNewTabButton].tap() } @@ -272,17 +270,16 @@ class TopTabsTest: BaseTestCase { } } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306838 + // https://mozilla.testrail.io/index.php?/cases/view/2306838 // Smoketest func testLongTapTabCounter() { if !iPad() { // Long tap on Tab Counter should show the correct options navigator.nowAt(NewTabScreen) - mozWaitForElementToExist(app.buttons["Show Tabs"], timeout: 10) + mozWaitForElementToExist(app.buttons["Show Tabs"]) app.buttons["Show Tabs"].press(forDuration: 1) mozWaitForElementToExist(app.cells.otherElements[StandardImageIdentifiers.Large.plus]) - XCTAssertTrue(app.cells.otherElements[StandardImageIdentifiers.Large.plus].exists) - XCTAssertTrue(app.cells.otherElements[StandardImageIdentifiers.Large.cross].exists) + mozWaitForElementToExist(app.cells.otherElements[StandardImageIdentifiers.Large.cross]) // Open New Tab app.cells.otherElements[StandardImageIdentifiers.Large.plus].tap() @@ -324,7 +321,7 @@ class TopTabsTest: BaseTestCase { } } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307047 + // https://mozilla.testrail.io/index.php?/cases/view/2307047 func testOpenTabsViewCurrentTabThumbnail() { // Open ten or more tabs navigator.nowAt(NewTabScreen) @@ -332,7 +329,7 @@ class TopTabsTest: BaseTestCase { for _ in 1...10 { navigator.createNewTab() if app.keyboards.element.isVisible() && !iPad() { - mozWaitForElementToExist(app.buttons["urlBar-cancel"], timeout: TIMEOUT) + mozWaitForElementToExist(app.buttons["urlBar-cancel"]) navigator.performAction(Action.CloseURLBarOpen) } } @@ -377,7 +374,7 @@ class TopTabsTest: BaseTestCase { } } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306869 + // https://mozilla.testrail.io/index.php?/cases/view/2306869 func testTabTrayContextMenuCloseTab() { // Have multiple tabs opened in the tab tray navigator.nowAt(NewTabScreen) @@ -388,7 +385,7 @@ class TopTabsTest: BaseTestCase { addTabsAndUndoCloseTabAction(nrOfTabs: 4) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306868 + // https://mozilla.testrail.io/index.php?/cases/view/2306868 func testTabTrayCloseMultipleTabs() { navigator.nowAt(NewTabScreen) validateToastWhenClosingMultipleTabs() @@ -429,7 +426,7 @@ class TopTabsTest: BaseTestCase { for _ in 1...4 { navigator.createNewTab() if app.keyboards.element.isVisible() && !iPad() { - mozWaitForElementToExist(app.buttons["urlBar-cancel"], timeout: TIMEOUT) + mozWaitForElementToExist(app.buttons["urlBar-cancel"]) navigator.performAction(Action.CloseURLBarOpen) } } @@ -451,7 +448,7 @@ class TopTabsTest: BaseTestCase { for _ in 1...nrOfTabs { navigator.createNewTab() if app.keyboards.element.isVisible() && !iPad() { - mozWaitForElementToExist(app.buttons["urlBar-cancel"], timeout: TIMEOUT) + mozWaitForElementToExist(app.buttons["urlBar-cancel"]) navigator.performAction(Action.CloseURLBarOpen) } } @@ -496,7 +493,7 @@ fileprivate extension BaseTestCase { } class TopTabsTestIphone: IphoneOnlyTestCase { - // https://testrail.stage.mozaws.net/index.php?/cases/view/2355535 + // https://mozilla.testrail.io/index.php?/cases/view/2355535 // Smoketest func testCloseTabFromLongPressTabsButton() { if skipPlatform { return } @@ -527,7 +524,7 @@ class TopTabsTestIphone: IphoneOnlyTestCase { } // This test only runs for iPhone see bug 1409750 - // https://testrail.stage.mozaws.net/index.php?/cases/view/2355536 + // https://mozilla.testrail.io/index.php?/cases/view/2355536 // Smoketest func testAddTabByLongPressTabsButton() { if skipPlatform { return } @@ -540,7 +537,7 @@ class TopTabsTestIphone: IphoneOnlyTestCase { } // This test only runs for iPhone see bug 1409750 - // https://testrail.stage.mozaws.net/index.php?/cases/view/2355537 + // https://mozilla.testrail.io/index.php?/cases/view/2355537 // Smoketest func testAddPrivateTabByLongPressTabsButton() { if skipPlatform { return } @@ -556,7 +553,7 @@ class TopTabsTestIphone: IphoneOnlyTestCase { } // This test is disabled for iPad because the toast menu is not shown there - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306861 + // https://mozilla.testrail.io/index.php?/cases/view/2306861 // Smoketest func testSwitchBetweenTabsToastButton() { if skipPlatform { return } @@ -580,7 +577,7 @@ class TopTabsTestIphone: IphoneOnlyTestCase { } // This test is disabled for iPad because the toast menu is not shown there - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306860 + // https://mozilla.testrail.io/index.php?/cases/view/2306860 // Smoketest func testSwitchBetweenTabsNoPrivatePrivateToastButton() { if skipPlatform { return } @@ -589,14 +586,14 @@ class TopTabsTestIphone: IphoneOnlyTestCase { waitUntilPageLoad() app.webViews.links.firstMatch.press(forDuration: 1) - mozWaitForElementToExist(app.buttons["Open in New Tab"], timeout: 3) + mozWaitForElementToExist(app.buttons["Open in New Tab"]) app.buttons["Open in New Private Tab"].press(forDuration: 1) - mozWaitForElementToExist(app.buttons["Switch"], timeout: 5) + mozWaitForElementToExist(app.buttons["Switch"]) app.buttons["Switch"].tap() // Check that the tab has changed to the new open one and that the user is in private mode waitUntilPageLoad() - mozWaitForElementToExist(app.textFields["url"], timeout: 5) + mozWaitForElementToExist(app.textFields["url"]) mozWaitForValueContains(app.textFields["url"], value: "iana") navigator.goto(TabTray) XCTAssertTrue(app.buttons["privateModeLarge"].isEnabled) @@ -606,7 +603,7 @@ class TopTabsTestIphone: IphoneOnlyTestCase { // Tests to check if Tab Counter is updating correctly after opening three tabs by tapping on '+' button // and closing the tabs by tapping 'x' button class TopTabsTestIpad: IpadOnlyTestCase { - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307023 + // https://mozilla.testrail.io/index.php?/cases/view/2307023 func testUpdateTabCounter() { if skipPlatform { return } // Open three tabs by tapping on '+' button diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/TrackingProtectionTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/TrackingProtectionTests.swift index 80559d261842..bdfc1cdff251 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/TrackingProtectionTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/TrackingProtectionTests.swift @@ -14,7 +14,7 @@ let websiteWithBlockedElements = "twitter.com" let differentWebsite = path(forTestPage: "test-example.html") class TrackingProtectionTests: BaseTestCase { - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307059 + // https://mozilla.testrail.io/index.php?/cases/view/2307059 // Smoketest func testStandardProtectionLevel() { navigator.goto(URLBarOpen) @@ -35,8 +35,7 @@ class TrackingProtectionTests: BaseTestCase { // The lock icon should still be there mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.trackingProtection]) mozWaitForElementToExist( - app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton], - timeout: 5 + app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton] ) // Switch to Private Browsing @@ -47,14 +46,13 @@ class TrackingProtectionTests: BaseTestCase { // Make sure TP is also there in PBM mozWaitForElementToExist(app.buttons[AccessibilityIdentifiers.Toolbar.trackingProtection]) mozWaitForElementToExist( - app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton], - timeout: TIMEOUT + app.buttons[AccessibilityIdentifiers.Toolbar.settingsMenuButton] ) navigator.goto(BrowserTabMenu) - mozWaitForElementToExist(app.tables.otherElements[StandardImageIdentifiers.Large.settings], timeout: 5) + mozWaitForElementToExist(app.tables.otherElements[StandardImageIdentifiers.Large.settings]) app.tables.otherElements[StandardImageIdentifiers.Large.settings].tap() navigator.nowAt(SettingsScreen) - mozWaitForElementToExist(app.tables.cells["NewTab"], timeout: 5) + mozWaitForElementToExist(app.tables.cells["NewTab"]) app.tables.cells["NewTab"].swipeUp() // Enable TP again navigator.goto(TrackingProtectionSettings) @@ -83,7 +81,7 @@ class TrackingProtectionTests: BaseTestCase { app.buttons["Done"].tap() } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2319381 + // https://mozilla.testrail.io/index.php?/cases/view/2319381 func testLockIconMenu() { navigator.openURL(differentWebsite) waitUntilPageLoad() @@ -94,7 +92,7 @@ class TrackingProtectionTests: BaseTestCase { } navigator.nowAt(BrowserTab) navigator.goto(TrackingProtectionContextMenuDetails) - mozWaitForElementToExist(app.staticTexts["Connection is not secure"], timeout: 5) + mozWaitForElementToExist(app.staticTexts["Connection not secure"], timeout: 5) var switchValue = app.switches.firstMatch.value! // Need to make sure first the setting was not turned off previously if switchValue as! String == "0" { @@ -108,7 +106,7 @@ class TrackingProtectionTests: BaseTestCase { XCTAssertEqual(switchValueOFF as! String, "0") // Open TP Settings menu - app.buttons["Protection Settings"].tap() + app.buttons["Privacy settings"].tap() mozWaitForElementToExist(app.navigationBars["Tracking Protection"], timeout: 5) let switchSettingsValue = app.switches["prefkey.trackingprotection.normalbrowsing"].value! XCTAssertEqual(switchSettingsValue as! String, "1") @@ -118,22 +116,24 @@ class TrackingProtectionTests: BaseTestCase { app.buttons["Done"].tap() navigator.nowAt(BrowserTab) navigator.goto(TrackingProtectionContextMenuDetails) - mozWaitForElementToExist(app.staticTexts["Connection is not secure"], timeout: 5) - XCTAssertFalse(app.switches.element.exists) + mozWaitForElementToExist(app.staticTexts["Connection not secure"], timeout: 5) + // This is a workaround in order to pass the tests for the newest Tracking Protection UI + // it may be changed back to "false", after the Tracking Protection UI implementation is done + XCTAssertTrue(app.switches.element.exists) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2318742 + // https://mozilla.testrail.io/index.php?/cases/view/2318742 func testProtectionLevelMoreInfoMenu() { navigator.nowAt(NewTabScreen) navigator.goto(TrackingProtectionSettings) // See Basic mode info app.cells["Settings.TrackingProtectionOption.BlockListBasic"].buttons["More Info"].tap() - XCTAssertTrue(app.navigationBars["Client.TPAccessoryInfo"].exists) - XCTAssertTrue(app.cells.staticTexts["Social Trackers"].exists) - XCTAssertTrue(app.cells.staticTexts["Cross-Site Trackers"].exists) - XCTAssertTrue(app.cells.staticTexts["Fingerprinters"].exists) - XCTAssertTrue(app.cells.staticTexts["Cryptominers"].exists) - XCTAssertFalse(app.cells.staticTexts["Tracking content"].exists) + mozWaitForElementToExist(app.navigationBars["Client.TPAccessoryInfo"]) + mozWaitForElementToExist(app.cells.staticTexts["Social Trackers"]) + mozWaitForElementToExist(app.cells.staticTexts["Cross-Site Trackers"]) + mozWaitForElementToExist(app.cells.staticTexts["Fingerprinters"]) + mozWaitForElementToExist(app.cells.staticTexts["Cryptominers"]) + mozWaitForElementToNotExist(app.cells.staticTexts["Tracking content"]) // Go back to TP settings app.buttons["Tracking Protection"].tap() @@ -146,11 +146,11 @@ class TrackingProtectionTests: BaseTestCase { app.buttons["Tracking Protection"].tap() } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2307061 + // https://mozilla.testrail.io/index.php?/cases/view/2307061 func testLockIconSecureConnection() { - navigator.openURL("https://www.Mozilla.org") + navigator.openURL("https://www.mozilla.org") waitUntilPageLoad() - // iOS 15 displays a toast for the paste. The toast may cover areas to be + // iOS 15 displays a toast for the paste. The toast may cover areas to be // tapped in the next step. if #unavailable(iOS 16) { sleep(2) @@ -159,11 +159,8 @@ class TrackingProtectionTests: BaseTestCase { navigator.nowAt(BrowserTab) navigator.goto(TrackingProtectionContextMenuDetails) // A page displaying the connection is secure - XCTAssertTrue(app.staticTexts["mozilla.org"].exists) - XCTAssertTrue( - app.staticTexts["Connection is secure"].exists, - "Missing Connection is secure info" - ) + mozWaitForElementToExist(app.staticTexts["mozilla.org"]) + mozWaitForElementToExist(app.staticTexts["Secure connection"]) XCTAssertEqual( app.buttons[AccessibilityIdentifiers.Toolbar.trackingProtection].label, "Secure connection" @@ -178,7 +175,24 @@ class TrackingProtectionTests: BaseTestCase { waitUntilPageLoad() // The page is correctly displayed with the lock icon disabled mozWaitForElementToExist(app.staticTexts["This Connection is Untrusted"], timeout: TIMEOUT_LONG) - XCTAssertTrue(app.staticTexts.elementContainingText("Firefox has not connected to this website.").exists) + mozWaitForElementToExist(app.staticTexts.elementContainingText("Firefox has not connected to this website.")) XCTAssertEqual(app.buttons[AccessibilityIdentifiers.Toolbar.trackingProtection].label, "Connection not secure") } + + // https://mozilla.testrail.io/index.php?/cases/view/2693741 + func testLockIconCloseMenu() { + navigator.openURL("https://www.mozilla.org") + waitUntilPageLoad() + // iOS 15 displays a toast for the paste. The toast may cover areas to be + // tapped in the next step. + if #unavailable(iOS 16) { + sleep(2) + } + // Tap "Secure connection" + navigator.nowAt(BrowserTab) + navigator.goto(TrackingProtectionContextMenuDetails) + mozWaitForElementToExist(app.staticTexts["Secure connection"]) + navigator.performAction(Action.CloseTPContextMenu) + mozWaitForElementToNotExist(app.staticTexts["Secure connection"]) + } } diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/URLValidationTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/URLValidationTests.swift index c044a9041ed0..a4638c62153b 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/URLValidationTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/URLValidationTests.swift @@ -19,7 +19,7 @@ class URLValidationTests: BaseTestCase { navigator.goto(NewTabScreen) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2460854 + // https://mozilla.testrail.io/index.php?/cases/view/2460854 // Smoketest func testDifferentURLTypes() { for i in urlTypes { diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/UrlBarTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/UrlBarTests.swift index dc85eb89e9eb..3898634a4c65 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/UrlBarTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/UrlBarTests.swift @@ -28,7 +28,7 @@ let iPhoneSearchIcon = XCUIApplication() .children(matching: .image).element class UrlBarTests: BaseTestCase { - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306888 + // https://mozilla.testrail.io/index.php?/cases/view/2306888 func testNewTabUrlBar() { // Visit any website and select the URL bar navigator.openURL("http://localhost:\(serverPort)/test-fixture/find-in-page-test.html") @@ -48,7 +48,7 @@ class UrlBarTests: BaseTestCase { XCTAssertEqual(app.textFields["url"].value as! String, "Search or enter address") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306887 + // https://mozilla.testrail.io/index.php?/cases/view/2306887 func testSearchEngineLogo() { tapUrlBarValidateKeyboardAndIcon() // Type a search term and hit "go" diff --git a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ZoomingTests.swift b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ZoomingTests.swift index c01b6b337826..40de3083ab14 100644 --- a/firefox-ios/firefox-ios-tests/Tests/XCUITests/ZoomingTests.swift +++ b/firefox-ios/firefox-ios-tests/Tests/XCUITests/ZoomingTests.swift @@ -25,7 +25,7 @@ class ZoomingTests: BaseTestCase { super.tearDown() } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306947 + // https://mozilla.testrail.io/index.php?/cases/view/2306947 // Smoketest func testZoomingActions() { // Regular browsing @@ -42,7 +42,7 @@ class ZoomingTests: BaseTestCase { validateZoomActions() } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306949 + // https://mozilla.testrail.io/index.php?/cases/view/2306949 func testZoomForceCloseFirefox() { openWebsiteAndReachZoomSetting(website: 0) zoomLevel = app.staticTexts[AccessibilityIdentifiers.ZoomPageBar.zoomPageZoomLevelLabel] @@ -59,7 +59,7 @@ class ZoomingTests: BaseTestCase { XCTAssertEqual(zoomLevel.label, "Current Zoom Level: 100%") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2306948 + // https://mozilla.testrail.io/index.php?/cases/view/2306948 func testSwitchingZoomedTabs() { validateZoomLevelOnSwitchingTabs() // Repeat all steps in private browsing @@ -72,7 +72,7 @@ class ZoomingTests: BaseTestCase { validateZoomLevelOnSwitchingTabs() } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2609150 + // https://mozilla.testrail.io/index.php?/cases/view/2609150 func testSwitchingZoomedTabsLandscape() { XCUIDevice.shared.orientation = UIDeviceOrientation.landscapeLeft validateZoomLevelOnSwitchingTabs() diff --git a/firefox-ios/nimbus-features/addressAutofillFeature.yaml b/firefox-ios/nimbus-features/addressAutofillFeature.yaml index f56692ffd8fa..301032c3ebbc 100644 --- a/firefox-ios/nimbus-features/addressAutofillFeature.yaml +++ b/firefox-ios/nimbus-features/addressAutofillFeature.yaml @@ -1,13 +1,9 @@ # The configuration for the addressAutofillFeature feature features: - address-autofill-feature: - description: This property defines the address card autofill feature + address-autofill-edit: + description: This property defines if the address editing is enabled in Settings variables: status: - description: If true, we will allow user to use the address autofill feature - type: Boolean - default: false - address-autofill-edit: description: If true, we will allow user to edit the address type: Boolean default: false @@ -15,8 +11,6 @@ features: - channel: beta value: status: false - address-autofill-edit: false - channel: developer value: status: true - address-autofill-edit: true diff --git a/firefox-ios/nimbus-features/microsurveyFeature.yaml b/firefox-ios/nimbus-features/microsurveyFeature.yaml index 6f3741c587ec..42470c8873d5 100644 --- a/firefox-ios/nimbus-features/microsurveyFeature.yaml +++ b/firefox-ios/nimbus-features/microsurveyFeature.yaml @@ -13,7 +13,7 @@ features: defaults: - channel: beta value: - enabled: true + enabled: false - channel: developer value: enabled: true diff --git a/firefox-ios/nimbus-features/nativeErrorPageFeature.yaml b/firefox-ios/nimbus-features/nativeErrorPageFeature.yaml new file mode 100644 index 000000000000..c83ff6ce52e1 --- /dev/null +++ b/firefox-ios/nimbus-features/nativeErrorPageFeature.yaml @@ -0,0 +1,19 @@ +# The configuration for the nativeErrorPageFeature feature +features: + native-error-page-feature: + description: > + This feature is for managing the roll out of the native error page feature + variables: + enabled: + description: > + If true, the feature is active. + type: Boolean + default: false + + defaults: + - channel: beta + value: + enabled: false + - channel: developer + value: + enabled: false diff --git a/firefox-ios/nimbus-features/onboardingFrameworkFeature.yaml b/firefox-ios/nimbus-features/onboardingFrameworkFeature.yaml index 699564ae56f5..18db3588c405 100644 --- a/firefox-ios/nimbus-features/onboardingFrameworkFeature.yaml +++ b/firefox-ios/nimbus-features/onboardingFrameworkFeature.yaml @@ -146,6 +146,49 @@ features: onboarding-type: fresh-install prerequisites: - ALWAYS + customization-theme: + card-type: multiple-choice + order: 40 + title: Onboarding/Onboarding.Customization.Theme.Title.v123 + body: Onboarding/Onboarding.Customization.Theme.Description.v123 + image: themeing + buttons: + primary: + title: Onboarding/Onboarding.Customization.Theme.Continue.Action.v123 + action: forward-one-card + multiple-choice-buttons: + - title: Onboarding/Onboarding.Customization.Theme.System.Action.v123 + image: theme-system + action: theme-system-default + - title: Onboarding/Onboarding.Customization.Theme.Light.Action.v123 + image: theme-light + action: theme-light + - title: Onboarding/Onboarding.Customization.Theme.Dark.Action.v123 + image: theme-dark + action: theme-dark + onboarding-type: fresh-install + prerequisites: + - ALWAYS + customization-toolbar: + card-type: multiple-choice + order: 41 + title: Onboarding/Onboarding.Customization.Toolbar.Title.v123 + body: Onboarding/Onboarding.Customization.Toolbar.Description.v123 + image: toolbar + buttons: + primary: + title: Onboarding/Onboarding.Customization.Toolbar.Continue.Action.v123 + action: forward-one-card + multiple-choice-buttons: + - title: Onboarding/Onboarding.Customization.Toolbar.Top.Action.v123 + image: toolbar-top + action: toolbar-top + - title: Onboarding/Onboarding.Customization.Toolbar.Bottom.Action.v123 + image: toolbar-bottom + action: toolbar-bottom + onboarding-type: fresh-install + prerequisites: + - ALWAYS update-welcome: card-type: basic order: 10 diff --git a/firefox-ios/nimbus-features/passwordGeneratorFeature.yaml b/firefox-ios/nimbus-features/passwordGeneratorFeature.yaml new file mode 100644 index 000000000000..6efae6f53385 --- /dev/null +++ b/firefox-ios/nimbus-features/passwordGeneratorFeature.yaml @@ -0,0 +1,17 @@ +# The configuration for the passwordGeneratorFeature feature +features: + password-generator-feature: + description: Password Generator Feature + variables: + enabled: + description: If true, the password generator feature is enabled + type: Boolean + default: false + defaults: + - channel: beta + value: + enabled: false + - channel: developer + value: + enabled: false + diff --git a/firefox-ios/nimbus-features/remoteTabManagement.yaml b/firefox-ios/nimbus-features/remoteTabManagement.yaml new file mode 100644 index 000000000000..b4017b30302d --- /dev/null +++ b/firefox-ios/nimbus-features/remoteTabManagement.yaml @@ -0,0 +1,22 @@ +# The configuration for the remoteTabManagement feature +features: + remote-tab-management: + description: > + Features that let users manage tabs on other devices that are + connected to the same Mozilla account. + variables: + close-tabs-enabled: + description: > + Whether the feature to close synced tabs is enabled. When enabled, + this device will allow other devices to close tabs that are open on this device, and + show a "close" button for tabs that are currently open on other supported devices + in the synced tabs tray. + type: Boolean + default: false + defaults: + - channel: beta + value: + close-tabs-enabled: false + - channel: developer + value: + close-tabs-enabled: true diff --git a/firefox-ios/nimbus.fml.yaml b/firefox-ios/nimbus.fml.yaml index f6eaee2a676a..8e0fca0f9507 100644 --- a/firefox-ios/nimbus.fml.yaml +++ b/firefox-ios/nimbus.fml.yaml @@ -26,9 +26,12 @@ include: - nimbus-features/menuRefactorFeature.yaml - nimbus-features/messagingFeature.yaml - nimbus-features/microsurveyFeature.yaml + - nimbus-features/nativeErrorPageFeature.yaml - nimbus-features/nightModeFeature.yaml - nimbus-features/onboardingFrameworkFeature.yaml + - nimbus-features/passwordGeneratorFeature.yaml - nimbus-features/reduxSearchSettingsFeature.yaml + - nimbus-features/remoteTabManagement.yaml - nimbus-features/searchFeature.yaml - nimbus-features/splashScreenFeature.yaml - nimbus-features/spotlightSearchFeature.yaml diff --git a/focus-ios/Blockzilla.xcodeproj/project.pbxproj b/focus-ios/Blockzilla.xcodeproj/project.pbxproj index d0c18e55c776..658339ac4e06 100644 --- a/focus-ios/Blockzilla.xcodeproj/project.pbxproj +++ b/focus-ios/Blockzilla.xcodeproj/project.pbxproj @@ -2461,7 +2461,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if [ $ACTION != \"indexbuild\" ]; then\n bash $SOURCE_ROOT/bin/nimbus-fml.sh --verbose\nfi\n"; + shellScript = "if [ $ACTION != \"indexbuild\" ]; then\n /usr/bin/env -i HOME=$HOME PROJECT=$PROJECT CONFIGURATION=$CONFIGURATION SOURCE_ROOT=$SOURCE_ROOT bash $SOURCE_ROOT/bin/nimbus-fml.sh --verbose\nfi\n"; }; D48C953D27FF411800FF8AEF /* Run Swiftlint */ = { isa = PBXShellScriptBuildPhase; @@ -7159,7 +7159,7 @@ repositoryURL = "https://github.com/mozilla/rust-components-swift"; requirement = { kind = exactVersion; - version = 130.0.20240724050232; + version = 131.0.20240821050323; }; }; 8A0E7F2C2BA0F0E0006BC6B6 /* XCRemoteSwiftPackageReference "Fuzi" */ = { @@ -7175,7 +7175,7 @@ repositoryURL = "https://github.com/mozilla/glean-swift"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 60.3.0; + minimumVersion = 60.5.0; }; }; F8324C2D264C807C007E4BFA /* XCRemoteSwiftPackageReference "SnapKit" */ = { @@ -7191,7 +7191,7 @@ repositoryURL = "https://github.com/getsentry/sentry-cocoa"; requirement = { kind = exactVersion; - version = 8.21.0; + version = 8.34.0; }; }; /* End XCRemoteSwiftPackageReference section */ diff --git a/focus-ios/Blockzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/focus-ios/Blockzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 6b20fdcbcf53..7881ffafaea2 100644 --- a/focus-ios/Blockzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/focus-ios/Blockzilla.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -33,8 +33,8 @@ "repositoryURL": "https://github.com/mozilla/glean-swift", "state": { "branch": null, - "revision": "38c00984dcbc7aff85352b917a40f95395044eab", - "version": "60.3.0" + "revision": "2185e2eea2d5ce272ff5c0e851eed42d52104ce4", + "version": "60.5.0" } }, { @@ -51,8 +51,8 @@ "repositoryURL": "https://github.com/mozilla/rust-components-swift", "state": { "branch": null, - "revision": "21102e5081ce1b7446b110f474d2d2a7a2029527", - "version": "130.0.20240724050232" + "revision": "368989055f07f4e2c135ebed7f4d649b22c1dae2", + "version": "131.0.20240821050323" } }, { @@ -60,8 +60,8 @@ "repositoryURL": "https://github.com/getsentry/sentry-cocoa", "state": { "branch": null, - "revision": "38f4f70d07117b9f958a76b1bff278c2f29ffe0e", - "version": "8.21.0" + "revision": "d2ced2d961b34573ebd2ea0567a2f1408e90f0ae", + "version": "8.34.0" } }, { diff --git a/focus-ios/Blockzilla/BrowserViewController.swift b/focus-ios/Blockzilla/BrowserViewController.swift index a0a1a43a02d7..f40a0bd9947f 100644 --- a/focus-ios/Blockzilla/BrowserViewController.swift +++ b/focus-ios/Blockzilla/BrowserViewController.swift @@ -23,7 +23,9 @@ class BrowserViewController: UIViewController { private lazy var webViewController: LegacyWebViewController = { var menuAction = WebMenuAction.live menuAction.openLink = { url in - self.submit(url: url, source: .action) + if let url = URIFixup.getURL(entry: url.absoluteString) { + self.submit(url: url, source: .action) + } } return LegacyWebViewController(trackingProtectionManager: trackingProtectionManager, webMenuAction: menuAction) }() diff --git a/focus-ios/Blockzilla/Info.plist b/focus-ios/Blockzilla/Info.plist index be107be44694..4a596cfe3ad5 100644 --- a/focus-ios/Blockzilla/Info.plist +++ b/focus-ios/Blockzilla/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 130.0 + 131.0 CFBundleSignature ???? CFBundleURLTypes diff --git a/focus-ios/Blockzilla/co.lproj/Localizable.strings b/focus-ios/Blockzilla/co.lproj/Localizable.strings index 37552cb6f23d..2b0ba454524e 100644 --- a/focus-ios/Blockzilla/co.lproj/Localizable.strings +++ b/focus-ios/Blockzilla/co.lproj/Localizable.strings @@ -8,7 +8,7 @@ "About.privateBullet1" = "Fate ricerche è navigate in l’appiecazione"; /* Label on About screen */ -"About.privateBullet2" = "Bluccate i perseguitatori (o mudificate e preferenze per auturizalli)"; +"About.privateBullet2" = "Bluccate i perseguitatori (o mudificate i parametri per auturizalli)"; /* Label on About screen */ "About.privateBullet3" = "Squassate i canistrelli è ancu e cronolugie di navigazione è di ricerca"; @@ -74,7 +74,7 @@ "Autocomplete.addCustomUrlWithPlus" = "+ Aghjunghje un indirizzu persunalizatu"; /* Label for toast alerting a custom URL has been added */ -"Autocomplete.customUrlAdded" = "Novu indirizzu persunalizatu aghjuntu."; +"Autocomplete.customUrlAdded" = "L’indirizzu web persunalizatu novu hè aghjuntu."; /* Description for enabling or disabling the default list. The placeholder is replaced with the application name, which can be either Firefox Focus or Firefox Klar. */ "Autocomplete.defaultDescriptoin" = "Attivà per chì %@ compii autumaticamente più di 450 indirizzi populari in a barra d’indirizzu."; @@ -107,7 +107,7 @@ "BiometricAuthentication.UnlockButton.Title" = "Sbluccà"; /* Create a new session after failing a biometric check */ -"BiometricPrompt.newSession" = "Nova sessione"; +"BiometricPrompt.newSession" = "Sessione nova"; /* Accessibility label for the back button */ "Browser.backLabel" = "Ritornu"; @@ -125,7 +125,7 @@ "Browser.reloadLabel" = "Ricaricà"; /* Accessibility label for the settings button */ -"Browser.settingsLabel" = "Preferenze"; +"Browser.settingsLabel" = "Parametri"; /* Accessibility label for the stop button */ "Browser.stopLabel" = "Piantà"; @@ -215,7 +215,7 @@ "Onboarding.Incognito.Title" = "Più chè solu incognitu"; /* Text for a label that indicates the description of protection section from onboarding screen. */ -"Onboarding.Protection.Description" = "Cunfigureghja e preferenze per sceglie s’è vò scumpartite assai o pocu."; +"Onboarding.Protection.Description" = "Cunfigureghja i parametri per sceglie s’è vò scumpartite assai o pocu."; /* Text for a label that indicates the title of protection section from onboarding screen. */ "Onboarding.Protection.Title" = "Prutezzione discreziunale"; @@ -239,10 +239,10 @@ "Safari.instructionsNotEnabled" = "%@ ùn hè micca attivatu."; /* Label for instructions to enable Safari, shown when enabling Safari Integration in Settings */ -"Safari.instructionsOpen" = "Aprite e preferenze"; +"Safari.instructionsOpen" = "Aprite i parametri"; /* Label for instructions to enable extensions in Safari, shown when enabling Safari Integration in Settings */ -"Safari.openInstruction" = "Aprite e preferenze di l’apparechju"; +"Safari.openInstruction" = "Aprite i parametri di l’apparechju"; /* Save button label */ "Save" = "Arregistrà"; @@ -305,7 +305,7 @@ "Settings.safariTitle" = "INTEGRAZIONE SAFARI"; /* Title for settings screen */ -"Settings.screenTitle" = "Preferenze"; +"Settings.screenTitle" = "Parametri"; /* Text for button to add another search engine in settings */ "Settings.Search.AddSearchEngineButton" = "Aghjunghje un altru mutore di ricerca"; @@ -320,7 +320,7 @@ "Settings.Search.NameToDisplay" = "Nome à affissà"; /* Toast displayed after adding a search engine */ -"Settings.Search.NewSearchEngineAdded" = "Novu mutore di ricerca aghjuntu."; +"Settings.Search.NewSearchEngineAdded" = "U mutore di ricerca novu hè aghjuntu."; /* Label for button to bring deleted default engines back */ "Settings.Search.RestoreEngine" = "Risturà i mutori di ricerca predefiniti"; @@ -563,10 +563,10 @@ "TodayWidget.SearchInApp.Instruction" = "Circà in %@"; /* This is the body text that is displayed for the Context Menu icon tooltip */ -"TooltipBodyText.ContextMenu" = "Andà à e preferenze per urganizà l’ozzioni specifiche di cunfidenzialità è di sicurità."; +"TooltipBodyText.ContextMenu" = "Andà à i parametri per urganizà l’ozzioni specifiche di cunfidenzialità è di sicurità."; /* This is the body text that is displayed for the Privacy tooltip */ -"TooltipBodyText.Privacy" = "Ste preferenze predefinite furniscenu una prutezzione forte. Ma hè faciule di mudificalle per suddisfà i vostri bisogni specifichi."; +"TooltipBodyText.Privacy" = "Sti parametri predefiniti furniscenu una prutezzione forte. Ma hè faciule di mudificalli per suddisfà i vostri bisogni specifichi."; /* This is the body text that is displayed for the Search Bar tooltip */ "TooltipBodyText.SearchBar" = "Principiate a vostra sessione di navigazione privata è noi bluccheremu l’elementi di spiunagiu è l’altre minacce durante a navigazione."; @@ -581,7 +581,7 @@ "TooltipBodyText.TrashIcon" = "Picchichjate a curbella à ogni mumenti per caccià tutta traccia di a vostra sessione currente."; /* This is the body text that is displayed for the Trash icon tooltip */ -"TooltipBodyText.TrashIcon.V2" = "Picchicciate quì per squassà tuttu - cronolugia, canistrelli, ecc. - è principiate à zeru in un’unghjetta nova."; +"TooltipBodyText.TrashIcon.V2" = "Picchichjate quì per squassà tuttu - cronolugia, canistrelli, ecc. - è principiate à zeru in un’unghjetta nova."; /* This is the title text that is displayed for the Privacy tooltip */ "TooltipTitleText.Privacy" = "Site prutetti !"; diff --git a/focus-ios/ContentBlocker/Info.plist b/focus-ios/ContentBlocker/Info.plist index dfdb68990e29..7a6944ee4f28 100644 --- a/focus-ios/ContentBlocker/Info.plist +++ b/focus-ios/ContentBlocker/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 130.0 + 131.0 CFBundleSignature ???? CFBundleVersion diff --git a/focus-ios/FocusIntentExtension/Info.plist b/focus-ios/FocusIntentExtension/Info.plist index dd39bc992c1d..6e6c33f687ef 100644 --- a/focus-ios/FocusIntentExtension/Info.plist +++ b/focus-ios/FocusIntentExtension/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 130.0 + 131.0 CFBundleVersion 1 NSExtension diff --git a/focus-ios/OpenInFocus/Info.plist b/focus-ios/OpenInFocus/Info.plist index 474b988e52cb..f77d58a5b501 100644 --- a/focus-ios/OpenInFocus/Info.plist +++ b/focus-ios/OpenInFocus/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 130.0 + 131.0 CFBundleVersion 1 NSExtension diff --git a/focus-ios/Shared/AppConfig.swift b/focus-ios/Shared/AppConfig.swift index 8f21fa94dbb0..de2dc61fc92a 100644 --- a/focus-ios/Shared/AppConfig.swift +++ b/focus-ios/Shared/AppConfig.swift @@ -6,8 +6,8 @@ import Foundation import UIKit protocol AppConfig { - var adjustFile: String { get } - var firefoxAppStoreURL: URL { get } +// var adjustFile: String { get } +// var firefoxAppStoreURL: URL { get } var productName: String { get } var supportPath: String { get } var appId: String { get } @@ -15,8 +15,8 @@ protocol AppConfig { } struct FocusAppConfig: AppConfig { - let adjustFile = "Adjust-Focus" - let firefoxAppStoreURL = URL(string: "https://app.adjust.com/gs1ao4")! +// let adjustFile = "Adjust-Focus" +// let firefoxAppStoreURL = URL(string: "https://app.adjust.com/gs1ao4")! let productName = "Focus" let supportPath = "kb/focus" let appId = "1055677337" @@ -24,8 +24,8 @@ struct FocusAppConfig: AppConfig { } struct KlarAppConfig: AppConfig { - let adjustFile = "Adjust-Klar" - let firefoxAppStoreURL = URL(string: "https://app.adjust.com/c04cts")! +// let adjustFile = "Adjust-Klar" +// let firefoxAppStoreURL = URL(string: "https://app.adjust.com/c04cts")! let productName = "Klar" let supportPath = "products/klar" let appId = "1073435754" diff --git a/focus-ios/Widgets/Info.plist b/focus-ios/Widgets/Info.plist index 1c3413fbd257..bd444ff41590 100644 --- a/focus-ios/Widgets/Info.plist +++ b/focus-ios/Widgets/Info.plist @@ -3,7 +3,7 @@ CFBundleShortVersionString - 130.0 + 131.0 NSExtension NSExtensionPointIdentifier diff --git a/focus-ios/focus-ios-tests/XCUITest/AsianLocaleTest.swift b/focus-ios/focus-ios-tests/XCUITest/AsianLocaleTest.swift index fdce73b0ef69..296a819f1fc2 100644 --- a/focus-ios/focus-ios-tests/XCUITest/AsianLocaleTest.swift +++ b/focus-ios/focus-ios-tests/XCUITest/AsianLocaleTest.swift @@ -20,7 +20,7 @@ class AsianLocaleTest: BaseTestCase { checkForHomeScreen() } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2599440 + // https://mozilla.testrail.io/index.php?/cases/view/2599440 func testSearchInLocale() { // Test Setup dismissURLBarFocused() diff --git a/focus-ios/focus-ios-tests/XCUITest/BrowsingTest.swift b/focus-ios/focus-ios-tests/XCUITest/BrowsingTest.swift index 4bf5e6171ea4..6b54862c7cd7 100644 --- a/focus-ios/focus-ios-tests/XCUITest/BrowsingTest.swift +++ b/focus-ios/focus-ios-tests/XCUITest/BrowsingTest.swift @@ -7,7 +7,7 @@ import XCTest class BrowsingTest: BaseTestCase { // Smoke test - // https://testrail.stage.mozaws.net/index.php?/cases/view/1569888 + // https://mozilla.testrail.io/index.php?/cases/view/1569888 func testLaunchExternalApp() { // Load URL loadWebPage("https://www.example.com") @@ -18,7 +18,7 @@ class BrowsingTest: BaseTestCase { app.buttons["HomeView.settingsButton"].tap() // Tap Share button - // https://testrail.stage.mozaws.net/index.php?/cases/view/1569888 + // https://mozilla.testrail.io/index.php?/cases/view/1569888 let shareButton: XCUIElement if #available(iOS 14, *) { shareButton = app.cells.buttons["Share Page With…"] @@ -31,10 +31,14 @@ class BrowsingTest: BaseTestCase { // Launch external app let RemindersApp: XCUIElement - if iPad() { - RemindersApp = app.collectionViews.scrollViews.cells.element(boundBy: 0) + if #available(iOS 17, *) { + RemindersApp = app.collectionViews.scrollViews.cells["Reminders"] } else { - RemindersApp = app.collectionViews.scrollViews.cells.element(boundBy: 1) + if iPad() { + RemindersApp = app.collectionViews.scrollViews.cells.element(boundBy: 0) + } else { + RemindersApp = app.collectionViews.scrollViews.cells.element(boundBy: 1) + } } waitForExistence(RemindersApp) waitForHittable(RemindersApp) @@ -43,7 +47,7 @@ class BrowsingTest: BaseTestCase { } // Smoketest - // https://testrail.stage.mozaws.net/index.php?/cases/view/1569889 + // https://mozilla.testrail.io/index.php?/cases/view/1569889 func testNavigationToolbar() { loadWebPage("example.com") waitForWebPageLoad() @@ -78,7 +82,7 @@ class BrowsingTest: BaseTestCase { } // Smoketest - // https://testrail.stage.mozaws.net/index.php?/cases/view/2587661 + // https://mozilla.testrail.io/index.php?/cases/view/2587661 func testActivityMenuRequestDesktopItem() { // Wait for existence rather than hittable because the textfield is technically disabled loadWebPage("facebook.com") @@ -113,7 +117,7 @@ class BrowsingTest: BaseTestCase { } // Smoketest - // https://testrail.stage.mozaws.net/index.php?/cases/view/2587662 + // https://mozilla.testrail.io/index.php?/cases/view/2587662 func testCheckCollapsedURL() { // Test do not apply to iPad if !iPad() { diff --git a/focus-ios/focus-ios-tests/XCUITest/CopyPasteTest.swift b/focus-ios/focus-ios-tests/XCUITest/CopyPasteTest.swift index 774f72821c86..c81a5379eca5 100644 --- a/focus-ios/focus-ios-tests/XCUITest/CopyPasteTest.swift +++ b/focus-ios/focus-ios-tests/XCUITest/CopyPasteTest.swift @@ -5,7 +5,7 @@ import XCTest class CopyPasteTest: BaseTestCase { - // https://testrail.stage.mozaws.net/index.php?/cases/view/395745 + // https://mozilla.testrail.io/index.php?/cases/view/395745 func testCopyMenuItem() { let urlBarTextField = app.textFields["URLBar.urlText"] loadWebPage("https://www.example.com") @@ -34,7 +34,7 @@ class CopyPasteTest: BaseTestCase { } // Smoketest - // https://testrail.stage.mozaws.net/index.php?/cases/view/2609148 + // https://mozilla.testrail.io/index.php?/cases/view/2609148 // Test the clipboard contents are displayed/updated properly func testClipboard() throws { let app = XCUIApplication() @@ -85,7 +85,7 @@ class CopyPasteTest: BaseTestCase { } // Smoketest - // https://testrail.stage.mozaws.net/index.php?/cases/view/2609149 + // https://mozilla.testrail.io/index.php?/cases/view/2609149 // Test Paste & Go feature func testPastenGo() { // Inject a string into clipboard diff --git a/focus-ios/focus-ios-tests/XCUITest/DragAndDropTest.swift b/focus-ios/focus-ios-tests/XCUITest/DragAndDropTest.swift index 7d84b81cc9e0..515380e254cb 100644 --- a/focus-ios/focus-ios-tests/XCUITest/DragAndDropTest.swift +++ b/focus-ios/focus-ios-tests/XCUITest/DragAndDropTest.swift @@ -7,7 +7,7 @@ import XCTest class DragAndDropTest: BaseTestCase { let websiteWithSearchField = ["url": "https://developer.mozilla.org/en-US/", "urlSearchField": "Search MDN"] - // https://testrail.stage.mozaws.net/index.php?/cases/view/2609718 + // https://mozilla.testrail.io/index.php?/cases/view/2609718 func testDragElement() { let urlBarTextField = app.textFields["URLBar.urlText"] loadWebPage(websiteWithSearchField["url"]!) diff --git a/focus-ios/focus-ios-tests/XCUITest/OnboardingTest.swift b/focus-ios/focus-ios-tests/XCUITest/OnboardingTest.swift index ef1079a65099..c8d50e8e2c17 100644 --- a/focus-ios/focus-ios-tests/XCUITest/OnboardingTest.swift +++ b/focus-ios/focus-ios-tests/XCUITest/OnboardingTest.swift @@ -18,7 +18,7 @@ class OnboardingTest: BaseTestCase { } // Smoketest - // https://testrail.stage.mozaws.net/index.php?/cases/view/394959 + // https://mozilla.testrail.io/index.php?/cases/view/394959 func testPressingDots() throws { let pageIndicatorButton = app.pageIndicators.firstMatch XCTAssertEqual(pageIndicatorButton.value as? String, "page 1 of 2") diff --git a/focus-ios/focus-ios-tests/XCUITest/PageActionMenuTest.swift b/focus-ios/focus-ios-tests/XCUITest/PageActionMenuTest.swift index 58871ddb3c1d..cd613b601872 100644 --- a/focus-ios/focus-ios-tests/XCUITest/PageActionMenuTest.swift +++ b/focus-ios/focus-ios-tests/XCUITest/PageActionMenuTest.swift @@ -5,7 +5,7 @@ import XCTest class PageActionMenuTest: BaseTestCase { - // https://testrail.stage.mozaws.net/index.php?/cases/view/2587660 + // https://mozilla.testrail.io/index.php?/cases/view/2587660 func testFindInPageURLBarElement() { // Navigate to website loadWebPage("https://www.example.com") @@ -33,7 +33,7 @@ class PageActionMenuTest: BaseTestCase { } // Smoketest - // https://testrail.stage.mozaws.net/index.php?/cases/view/395022 + // https://mozilla.testrail.io/index.php?/cases/view/395022 func testActivityMenuFindInPageAction() { // Navigate to website loadWebPage("https://www.example.com") diff --git a/focus-ios/focus-ios-tests/XCUITest/PageShortcutsTest.swift b/focus-ios/focus-ios-tests/XCUITest/PageShortcutsTest.swift index 7652ee97cd4e..dadeebf8fc27 100644 --- a/focus-ios/focus-ios-tests/XCUITest/PageShortcutsTest.swift +++ b/focus-ios/focus-ios-tests/XCUITest/PageShortcutsTest.swift @@ -11,7 +11,7 @@ class PageShortcutsTest: BaseTestCase { super.tearDown() } - // https://testrail.stage.mozaws.net/index.php?/cases/view/1857439 + // https://mozilla.testrail.io/index.php?/cases/view/1857439 func testAddRemoveShortcut() { addShortcut(website: "mozilla.org") @@ -29,7 +29,7 @@ class PageShortcutsTest: BaseTestCase { } // Smoketest - // https://testrail.stage.mozaws.net/index.php?/cases/view/1857438 + // https://mozilla.testrail.io/index.php?/cases/view/1857438 func testAddRenameShortcut() { addShortcut(website: "mozilla.org") @@ -51,7 +51,7 @@ class PageShortcutsTest: BaseTestCase { } // Smoketest - // https://testrail.stage.mozaws.net/index.php?/cases/view/1857440 + // https://mozilla.testrail.io/index.php?/cases/view/1857440 func testShortcutShownWhileTypingURLBar() { addShortcut(website: "example.com") app.urlTextField.tap() diff --git a/focus-ios/focus-ios-tests/XCUITest/SearchProviderTest.swift b/focus-ios/focus-ios-tests/XCUITest/SearchProviderTest.swift index 19cc423f6c2a..20be7b5b40d5 100644 --- a/focus-ios/focus-ios-tests/XCUITest/SearchProviderTest.swift +++ b/focus-ios/focus-ios-tests/XCUITest/SearchProviderTest.swift @@ -5,22 +5,22 @@ import XCTest class SearchProviderTest: BaseTestCase { - // https://testrail.stage.mozaws.net/index.php?/cases/view/1707743 + // https://mozilla.testrail.io/index.php?/cases/view/1707743 func testGoogleSearchProvider() { searchProviderTestHelper(provider: "Google") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2512720 + // https://mozilla.testrail.io/index.php?/cases/view/2512720 func testDuckDuckGoSearchProvider() { searchProviderTestHelper(provider: "DuckDuckGo") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2512721 + // https://mozilla.testrail.io/index.php?/cases/view/2512721 func testWikipediaSearchProvider() { searchProviderTestHelper(provider: "Wikipedia") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2524588 + // https://mozilla.testrail.io/index.php?/cases/view/2524588 func testSearchQuery() { searchQuery("test", provider: "Google") dismissKeyboardFocusMenuSettings() @@ -65,7 +65,7 @@ class SearchProviderTest: BaseTestCase { } } - // https://testrail.stage.mozaws.net/index.php?/cases/view/1707744 + // https://mozilla.testrail.io/index.php?/cases/view/1707744 func testAddRemoveCustomSearchProvider() { dismissURLBarFocused() waitForExistence(app.buttons["HomeView.settingsButton"]) @@ -110,7 +110,7 @@ class SearchProviderTest: BaseTestCase { app.navigationBars.buttons["edit"].tap() } - // https://testrail.stage.mozaws.net/index.php?/cases/view/1707745 + // https://mozilla.testrail.io/index.php?/cases/view/1707745 func testPreventionOfRemovingDefaultSearchProvider() { dismissURLBarFocused() waitForExistence(app.buttons["HomeView.settingsButton"]) diff --git a/focus-ios/focus-ios-tests/XCUITest/SearchSuggestionsTest.swift b/focus-ios/focus-ios-tests/XCUITest/SearchSuggestionsTest.swift index e1ea08034b64..e870ef9f78a7 100644 --- a/focus-ios/focus-ios-tests/XCUITest/SearchSuggestionsTest.swift +++ b/focus-ios/focus-ios-tests/XCUITest/SearchSuggestionsTest.swift @@ -46,7 +46,7 @@ class SearchSuggestionsPromptTest: BaseTestCase { checkToggle(isOn: false) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/1707746 + // https://mozilla.testrail.io/index.php?/cases/view/1707746 func testEnableThroughPrompt() { // Check search suggestions toggle is initially OFF checkToggleStartsOff() @@ -80,7 +80,7 @@ class SearchSuggestionsPromptTest: BaseTestCase { checkToggle(isOn: true) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2524590 + // https://mozilla.testrail.io/index.php?/cases/view/2524590 func testDisableThroughPrompt() { // Check search suggestions toggle is initially OFF checkToggleStartsOff() @@ -117,7 +117,7 @@ class SearchSuggestionsPromptTest: BaseTestCase { checkToggle(isOn: false) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2524591 + // https://mozilla.testrail.io/index.php?/cases/view/2524591 func testEnableThroughToggle() { // Check search suggestions toggle is initially OFF checkToggleStartsOff() @@ -140,7 +140,7 @@ class SearchSuggestionsPromptTest: BaseTestCase { checkSuggestions() } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2524592 + // https://mozilla.testrail.io/index.php?/cases/view/2524592 func testEnableThenDisable() { // Check search suggestions toggle is initially OFF checkToggleStartsOff() diff --git a/focus-ios/focus-ios-tests/XCUITest/SettingTest.swift b/focus-ios/focus-ios-tests/XCUITest/SettingTest.swift index c17fba2743d4..50da58958fb1 100644 --- a/focus-ios/focus-ios-tests/XCUITest/SettingTest.swift +++ b/focus-ios/focus-ios-tests/XCUITest/SettingTest.swift @@ -9,7 +9,7 @@ class SettingTest: BaseTestCase { // Smoketest // Check for the basic appearance of the Settings Menu - // https://testrail.stage.mozaws.net/index.php?/cases/view/394976 + // https://mozilla.testrail.io/index.php?/cases/view/394976 func testCheckSetting() { dismissURLBarFocused() @@ -120,7 +120,7 @@ class SettingTest: BaseTestCase { waitForExistence(app.navigationBars["Settings"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2574974 + // https://mozilla.testrail.io/index.php?/cases/view/2574974 func testOpenInSafari() { let safariapp = XCUIApplication(privateWithPath: nil, bundleID: "com.apple.mobilesafari")! loadWebPage("https://www.google.com", waitForLoadToFinish: true) @@ -150,7 +150,7 @@ class SettingTest: BaseTestCase { waitForExistence(app.staticTexts["Browsing history cleared"]) } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2574975 + // https://mozilla.testrail.io/index.php?/cases/view/2574975 func testEnableDisableAutocomplete() { dismissURLBarFocused() @@ -179,7 +179,7 @@ class SettingTest: BaseTestCase { toggle.tap() } - // https://testrail.stage.mozaws.net/index.php?/cases/view/2574976 + // https://mozilla.testrail.io/index.php?/cases/view/2574976 func testAddRemoveCustomDomain() { dismissURLBarFocused() // Navigate to Settings @@ -234,7 +234,7 @@ class SettingTest: BaseTestCase { } // Smoketest - // https://testrail.stage.mozaws.net/index.php?/cases/view/1569297 + // https://mozilla.testrail.io/index.php?/cases/view/1569297 func testSafariIntegration() { dismissURLBarFocused() @@ -254,28 +254,30 @@ class SettingTest: BaseTestCase { XCTAssertEqual(app.switches["BlockerToggle.Safari"].value! as! String, "0") iOS_Settings.activate() - waitForExistence(iOS_Settings.cells["Safari"]) - iOS_Settings.cells["Safari"].tap() - iOS_Settings.cells["AutoFill"].swipeUp() - if #available(iOS 15.0, *) { - iOS_Settings.cells.staticTexts["Extensions"].tap() - } else { - iOS_Settings.cells.staticTexts["CONTENT_BLOCKERS"].tap() + if #unavailable(iOS 18) { + waitForExistence(iOS_Settings.cells["Safari"]) + iOS_Settings.cells["Safari"].tap() + iOS_Settings.cells["AutoFill"].swipeUp() + if #available(iOS 15.0, *) { + iOS_Settings.cells.staticTexts["Extensions"].tap() + } else { + iOS_Settings.cells.staticTexts["CONTENT_BLOCKERS"].tap() + } + iOS_Settings.tables.cells.staticTexts["Firefox Focus"].tap() + iOS_Settings.tables.cells.switches.element(boundBy: 0).tap() + iOS_Settings.terminate() + + XCUIDevice.shared.press(.home) + // Let's be sure the app is backgrounded + _ = app.wait(for: XCUIApplication.State.runningBackground, timeout: 45) + let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard") + waitForExistence(springboard.icons["XCUITest-Runner"], timeout: 15) + + // Go back to the app to verify that the toggle has changed its value + app.activate() + waitForExistence(app.navigationBars["Settings"], timeout: 15) + XCTAssertEqual(app.switches["BlockerToggle.Safari"].value! as! String, "1") } - iOS_Settings.tables.cells.staticTexts["Firefox Focus"].tap() - iOS_Settings.tables.cells.switches.element(boundBy: 0).tap() - iOS_Settings.terminate() - - XCUIDevice.shared.press(.home) - // Let's be sure the app is backgrounded - _ = app.wait(for: XCUIApplication.State.runningBackground, timeout: 45) - let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard") - waitForExistence(springboard.icons["XCUITest-Runner"], timeout: 15) - - // Go back to the app to verify that the toggle has changed its value - app.activate() - waitForExistence(app.navigationBars["Settings"], timeout: 15) - XCTAssertEqual(app.switches["BlockerToggle.Safari"].value! as! String, "1") } func setUrlAutoCompleteTo(desiredAutoCompleteState: String) { @@ -303,7 +305,7 @@ class SettingTest: BaseTestCase { } // Smoketest - // https://testrail.stage.mozaws.net/index.php?/cases/view/2584834 + // https://mozilla.testrail.io/index.php?/cases/view/2584834 func testVisitWebsite() { dismissURLBarFocused() @@ -334,7 +336,7 @@ class SettingTest: BaseTestCase { checkForHomeScreen() } - // https://testrail.stage.mozaws.net/index.php?/cases/view/394967 + // https://mozilla.testrail.io/index.php?/cases/view/394967 func testDisableAutocomplete() { let urlTextField = app.urlTextField let searchSuggestionsOverlay = app.searchSuggestionsOverlay @@ -351,7 +353,7 @@ class SettingTest: BaseTestCase { XCTAssertEqual(urlTextField.value as? String, "mozilla") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/394967 + // https://mozilla.testrail.io/index.php?/cases/view/394967 func testReEnableAutoComplete() { let urlTextField = app.urlTextField let searchSuggestionsOverlay = app.searchSuggestionsOverlay @@ -369,7 +371,7 @@ class SettingTest: BaseTestCase { XCTAssertEqual(urlTextField.value as? String, "mozilla.org/") } - // https://testrail.stage.mozaws.net/index.php?/cases/view/394967 + // https://mozilla.testrail.io/index.php?/cases/view/394967 func testAutocompleteCustomDomain() { dismissURLBarFocused() app.buttons["HomeView.settingsButton"].tap() diff --git a/focus-ios/focus-ios-tests/XCUITest/TrackingProtectionTest.swift b/focus-ios/focus-ios-tests/XCUITest/TrackingProtectionTest.swift index 81ed8aa9dab0..74017b1df662 100644 --- a/focus-ios/focus-ios-tests/XCUITest/TrackingProtectionTest.swift +++ b/focus-ios/focus-ios-tests/XCUITest/TrackingProtectionTest.swift @@ -5,7 +5,7 @@ import XCTest class TrackingProtectionTest: BaseTestCase { - // https://testrail.stage.mozaws.net/index.php?/cases/view/2544056 + // https://mozilla.testrail.io/index.php?/cases/view/2544056 func testInactiveSettings() { // Go to in-app settings // Check the new options in TP Settings menu @@ -34,7 +34,7 @@ class TrackingProtectionTest: BaseTestCase { } // Smoketest - // https://testrail.stage.mozaws.net/index.php?/cases/view/394999 + // https://mozilla.testrail.io/index.php?/cases/view/394999 func testProtectionSidebar() { // Visit https://www.mozilla.org loadWebPage("mozilla.org") @@ -61,7 +61,7 @@ class TrackingProtectionTest: BaseTestCase { } // Smoke test - // https://testrail.stage.mozaws.net/index.php?/cases/view/1569890 + // https://mozilla.testrail.io/index.php?/cases/view/1569890 func testAdBlocking() { // Load URL loadWebPage("https://blockads.fivefilters.org/") @@ -73,7 +73,7 @@ class TrackingProtectionTest: BaseTestCase { } // Smoke test - // https://testrail.stage.mozaws.net/index.php?/cases/view/1569869 + // https://mozilla.testrail.io/index.php?/cases/view/1569869 func testShieldMenuSetting() { // Load URL loadWebPage("https://blockads.fivefilters.org/") diff --git a/focus-ios/focus-ios-tests/XCUITest/URLValidationTest.swift b/focus-ios/focus-ios-tests/XCUITest/URLValidationTest.swift index 909dc19c740b..8bd92dee7bcc 100644 --- a/focus-ios/focus-ios-tests/XCUITest/URLValidationTest.swift +++ b/focus-ios/focus-ios-tests/XCUITest/URLValidationTest.swift @@ -15,7 +15,7 @@ class URLValidationTest: BaseTestCase { "mozilla.org/en-US", "https://mozilla.org/", "https://mozilla.org/en", "https://mozilla.org/en-US"] let urlHttpTypes=["http://example.com", "http://example.com/"] - // https://testrail.stage.mozaws.net/index.php?/cases/view/2460275 + // https://mozilla.testrail.io/index.php?/cases/view/2460275 func testDifferentURLTypes() { for i in urlTypes { loadAndValidateURL(URL: i)