diff --git a/.github/workflows/alpha.yml b/.github/workflows/alpha.yml index 3d3ba3c8af..01ec4a7540 100644 --- a/.github/workflows/alpha.yml +++ b/.github/workflows/alpha.yml @@ -74,8 +74,9 @@ jobs: restore-keys: | ${{ runner.os }}-spm- + # Using Xcode 15 as the alpha build uses iOS 17 APIs - name: Select Xcode - run: sudo xcode-select -s /Applications/Xcode_$(<.xcode-version).app/Contents/Developer + run: sudo xcode-select -s /Applications/Xcode_15.0.1.app/Contents/Developer - name: Prepare fastlane run: bundle install diff --git a/.github/workflows/end-to-end.yml b/.github/workflows/end-to-end.yml index ebf4aaad18..de34f5fd98 100644 --- a/.github/workflows/end-to-end.yml +++ b/.github/workflows/end-to-end.yml @@ -44,46 +44,38 @@ jobs: -derivedDataPath "DerivedData" \ | tee xcodebuild.log - - name: Setup tests - run: bundle install && bundle exec fastlane setup_e2e_tests - - name: Release tests - uses: mobile-dev-inc/action-maestro-cloud@v1.2.3 + uses: mobile-dev-inc/action-maestro-cloud@v1.4.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: DerivedData/Build/Products/Debug-iphonesimulator/DuckDuckGo.app - workspace: .maestro/release_tests + workspace: .maestro + include-tags: release - name: Privacy tests - uses: mobile-dev-inc/action-maestro-cloud@v1.2.3 + uses: mobile-dev-inc/action-maestro-cloud@v1.4.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: DerivedData/Build/Products/Debug-iphonesimulator/DuckDuckGo.app - workspace: .maestro/privacy_tests + workspace: .maestro + include-tags: privacy - name: Ad Click Detection Flow tests - uses: mobile-dev-inc/action-maestro-cloud@v1.2.3 + uses: mobile-dev-inc/action-maestro-cloud@v1.4.1 with: api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} app-file: DerivedData/Build/Products/Debug-iphonesimulator/DuckDuckGo.app - workspace: .maestro/privacy_tests + workspace: .maestro + include-tags: adClick - name: Create Asana task when workflow failed if: ${{ failure() }} run: | curl -s "https://app.asana.com/api/1.0/tasks" \ - -H "Authorization: Bearer ${{ secrets.ASANA_ACCESS_TOKEN }}" \ - --data ' \ - { \ - data: " \ - "name": "GH Workflow Failure - End to end tests", \ - "workspace": ${{ vars.GH_ASANA_WORKSPACE_ID }}, \ - "projects": [ \ - ${{ vars.GH_ASANA_IOS_APP_PROJECT_ID }} \ - ], \ - "notes" : "The end to end workflow has failed. See https://github.com/duckduckgo/iOS/actions/runs/${{ github.run_id }}", \ - " \ - }' + --header "Accept: application/json" \ + --header "Authorization: Bearer ${{ secrets.ASANA_ACCESS_TOKEN }}" \ + --header "Content-Type: application/json" \ + --data ' { "data": { "name": "GH Workflow Failure - End to end tests", "workspace": "${{ vars.GH_ASANA_WORKSPACE_ID }}", "projects": [ "${{ vars.GH_ASANA_IOS_APP_PROJECT_ID }}" ], "notes" : "The end to end workflow has failed. See https://github.com/duckduckgo/iOS/actions/runs/${{ github.run_id }}" } }' - name: Upload logs when workflow failed uses: actions/upload-artifact@v3 diff --git a/.github/workflows/sync-end-to-end.yml b/.github/workflows/sync-end-to-end.yml new file mode 100644 index 0000000000..3dc49732fa --- /dev/null +++ b/.github/workflows/sync-end-to-end.yml @@ -0,0 +1,82 @@ +name: Sync-End-to-End tests + +on: + schedule: + - cron: '0 5 * * *' # run at 5 AM UTC + +jobs: + sync-end-to-end-tests: + name: Sync End to end Tests + runs-on: macos-13 + + steps: + - name: Check out the code + uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Set cache key hash + run: | + has_only_tags=$(jq '[ .object.pins[].state | has("version") ] | all' DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved) + if [[ "$has_only_tags" == "true" ]]; then + echo "cache_key_hash=${{ hashFiles('DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved') }}" >> $GITHUB_ENV + else + echo "Package.resolved contains dependencies specified by branch or commit, skipping cache." + fi + + - name: Cache SPM + if: env.cache_key_hash + uses: actions/cache@v3 + with: + path: DerivedData/SourcePackages + key: ${{ runner.os }}-spm-${{ env.cache_key_hash }} + restore-keys: | + ${{ runner.os }}-spm- + + - name: Select Xcode + run: sudo xcode-select -s /Applications/Xcode_$(<.xcode-version).app/Contents/Developer + + - name: Build for tests + run: | + set -o pipefail && xcodebuild \ + -scheme "DuckDuckGo" \ + -destination "platform=iOS Simulator,name=iPhone 14,OS=16.4" \ + -derivedDataPath "DerivedData" \ + | tee xcodebuild.log + + - name: Create test account for Sync and return the recovery code + uses: duckduckgo/sync_crypto/action@main + id: sync-recovery-code + with: + debug: true + + - name: Sync e2e tests + uses: mobile-dev-inc/action-maestro-cloud@v1.6.0 + with: + api-key: ${{ secrets.MAESTRO_CLOUD_API_KEY }} + app-file: DerivedData/Build/Products/Debug-iphonesimulator/DuckDuckGo.app + workspace: .maestro + include-tags: sync + env: | + CODE=${{ steps.sync-recovery-code.outputs.recovery-code }} + + - name: Create Asana task when workflow failed + if: ${{ failure() }} + run: | + curl -s "https://app.asana.com/api/1.0/tasks" \ + --header "Accept: application/json" \ + --header "Authorization: Bearer ${{ secrets.ASANA_ACCESS_TOKEN }}" \ + --header "Content-Type: application/json" \ + --data ' { "data": { "name": "GH Workflow Failure - Sync End to end tests", "workspace": "${{ vars.GH_ASANA_WORKSPACE_ID }}", "projects": [ "${{ vars.GH_ASANA_IOS_APP_PROJECT_ID }}" ], "notes" : "The end to end workflow has failed. See https://github.com/duckduckgo/iOS/actions/runs/${{ github.run_id }}" } }' + + - name: Upload logs when workflow failed + uses: actions/upload-artifact@v3 + if: failure() + with: + name: BuildLogs + path: | + xcodebuild.log + DerivedData/Logs/Test/*.xcresult + retention-days: 7 + + diff --git a/.gitignore b/.gitignore index d6fc1c4859..f723942232 100644 --- a/.gitignore +++ b/.gitignore @@ -70,9 +70,6 @@ fastlane/report.xml fastlane/Preview.html fastlane/test_output -# Mestro -.maestro/**/shared - # DuckDuckGo Configuration/ExternalDeveloper.xcconfig diff --git a/.maestro/ad_click_detection_flow_tests/01_yjs_heuristic_no_ad_domain_param_u3_param_included.yaml b/.maestro/ad_click_detection_flow_tests/01_yjs_heuristic_no_ad_domain_param_u3_param_included.yaml index 70c1ded221..9765f58cab 100644 --- a/.maestro/ad_click_detection_flow_tests/01_yjs_heuristic_no_ad_domain_param_u3_param_included.yaml +++ b/.maestro/ad_click_detection_flow_tests/01_yjs_heuristic_no_ad_domain_param_u3_param_included.yaml @@ -1,6 +1,7 @@ -# yjs_heuristic_no_ad_domain_param_u3_param_included.yaml - appId: com.duckduckgo.mobile.ios +tags: + - adClick + --- # Set up @@ -11,14 +12,14 @@ appId: com.duckduckgo.mobile.ios visible: text: "Let’s Do It!" index: 0 - file: ./shared/onboarding.yaml + file: ../shared/onboarding.yaml # Load Site - assertVisible: id: "searchEntry" - tapOn: id: "searchEntry" -- inputText: "https://www.search-company.site/" +- inputText: "https://www.search-company.site/#ad-id-1" - pressKey: Enter # Manage onboarding @@ -27,9 +28,7 @@ appId: com.duckduckgo.mobile.ios visible: text: "Got It" index: 0 - file: ./shared/onboarding_browsing.yaml - -- assertVisible: "Search engine" + file: ../shared/onboarding_browsing.yaml # Test Ad1 - assertVisible: diff --git a/.maestro/ad_click_detection_flow_tests/02_mjs_heuristic_no_ad_domain_param_dsl_param_included.yaml b/.maestro/ad_click_detection_flow_tests/02_mjs_heuristic_no_ad_domain_param_dsl_param_included.yaml index 0d701735f2..25b2f615f9 100644 --- a/.maestro/ad_click_detection_flow_tests/02_mjs_heuristic_no_ad_domain_param_dsl_param_included.yaml +++ b/.maestro/ad_click_detection_flow_tests/02_mjs_heuristic_no_ad_domain_param_dsl_param_included.yaml @@ -1,6 +1,7 @@ -# mjs_heuristic_no_ad_domain_param_dsl_param_included.yaml - appId: com.duckduckgo.mobile.ios +tags: + - adClick + --- # Set up @@ -11,14 +12,14 @@ appId: com.duckduckgo.mobile.ios visible: text: "Let’s Do It!" index: 0 - file: ./shared/onboarding.yaml + file: ../shared/onboarding.yaml # Load Site - assertVisible: id: "searchEntry" - tapOn: id: "searchEntry" -- inputText: "https://www.search-company.site/" +- inputText: "https://www.search-company.site/#ad-id-2" - pressKey: Enter # Manage onboarding @@ -27,9 +28,7 @@ appId: com.duckduckgo.mobile.ios visible: text: "Got It" index: 0 - file: ./shared/onboarding_browsing.yaml - -- assertVisible: "Search engine" + file: ../shared/onboarding_browsing.yaml # Test Ad2 - assertVisible: diff --git a/.maestro/ad_click_detection_flow_tests/03_yjs_heuristic_no_ad_domain_param_but_missing_u3_param.yaml b/.maestro/ad_click_detection_flow_tests/03_yjs_heuristic_no_ad_domain_param_but_missing_u3_param.yaml index 5b329e95c2..7f624b5bba 100644 --- a/.maestro/ad_click_detection_flow_tests/03_yjs_heuristic_no_ad_domain_param_but_missing_u3_param.yaml +++ b/.maestro/ad_click_detection_flow_tests/03_yjs_heuristic_no_ad_domain_param_but_missing_u3_param.yaml @@ -1,6 +1,7 @@ -# yjs_heuristic_no_ad_domain_param_but_missing_u3_param.yaml - appId: com.duckduckgo.mobile.ios +tags: + - adClick + --- # Set up @@ -11,14 +12,14 @@ appId: com.duckduckgo.mobile.ios visible: text: "Let’s Do It!" index: 0 - file: ./shared/onboarding.yaml + file: ../shared/onboarding.yaml # Load Site - assertVisible: id: "searchEntry" - tapOn: id: "searchEntry" -- inputText: "https://www.search-company.site/" +- inputText: "https://www.search-company.site/#ad-id-3" - pressKey: Enter # Manage onboarding @@ -27,9 +28,7 @@ appId: com.duckduckgo.mobile.ios visible: text: "Got It" index: 0 - file: ./shared/onboarding_browsing.yaml - -- assertVisible: "Search engine" + file: ../shared/onboarding_browsing.yaml # Test Ad3 - assertVisible: diff --git a/.maestro/ad_click_detection_flow_tests/04_mjs_heuristic_no_ad_domain_param_but_missing_dsl_param.yaml b/.maestro/ad_click_detection_flow_tests/04_mjs_heuristic_no_ad_domain_param_but_missing_dsl_param.yaml index 1ef6042990..0c708a1a9c 100644 --- a/.maestro/ad_click_detection_flow_tests/04_mjs_heuristic_no_ad_domain_param_but_missing_dsl_param.yaml +++ b/.maestro/ad_click_detection_flow_tests/04_mjs_heuristic_no_ad_domain_param_but_missing_dsl_param.yaml @@ -1,6 +1,7 @@ -# _mjs_heuristic_no_ad_domain_param_but_missing_dsl_param.yaml - appId: com.duckduckgo.mobile.ios +tags: + - adClick + --- # Set up @@ -11,14 +12,14 @@ appId: com.duckduckgo.mobile.ios visible: text: "Let’s Do It!" index: 0 - file: ./shared/onboarding.yaml + file: ../shared/onboarding.yaml # Load Site - assertVisible: id: "searchEntry" - tapOn: id: "searchEntry" -- inputText: "https://www.search-company.site/" +- inputText: "https://www.search-company.site/#ad-id-4" - pressKey: Enter # Manage onboarding @@ -27,15 +28,8 @@ appId: com.duckduckgo.mobile.ios visible: text: "Got It" index: 0 - file: ./shared/onboarding_browsing.yaml - -- assertVisible: "Search engine" + file: ../shared/onboarding_browsing.yaml -# Test Ad4 -- swipe: - start: 400, 200 - end: 400, 0 - duration: 200 - assertVisible: text: "[Ad 4] Shopping Tab Ad (heuristic)" - tapOn: diff --git a/.maestro/ad_click_detection_flow_tests/05_yjs_heuristic_ad_domain_provided_but_empty_u3_not_needed.yaml b/.maestro/ad_click_detection_flow_tests/05_yjs_heuristic_ad_domain_provided_but_empty_u3_not_needed.yaml index 6ca3db68f6..a40e33dae0 100644 --- a/.maestro/ad_click_detection_flow_tests/05_yjs_heuristic_ad_domain_provided_but_empty_u3_not_needed.yaml +++ b/.maestro/ad_click_detection_flow_tests/05_yjs_heuristic_ad_domain_provided_but_empty_u3_not_needed.yaml @@ -1,6 +1,7 @@ -# yjs_heuristic_ad_domain_provided_but_empty_u3_not_needed.yaml - appId: com.duckduckgo.mobile.ios +tags: + - adClick + --- # Set up @@ -11,14 +12,14 @@ appId: com.duckduckgo.mobile.ios visible: text: "Let’s Do It!" index: 0 - file: ./shared/onboarding.yaml + file: ../shared/onboarding.yaml # Load Site - assertVisible: id: "searchEntry" - tapOn: id: "searchEntry" -- inputText: "https://www.search-company.site/" +- inputText: "https://www.search-company.site/#ad-id-5" - pressKey: Enter # Manage onboarding @@ -27,15 +28,8 @@ appId: com.duckduckgo.mobile.ios visible: text: "Got It" index: 0 - file: ./shared/onboarding_browsing.yaml - -- assertVisible: "Search engine" + file: ../shared/onboarding_browsing.yaml -# Test Ad5 -- swipe: - start: 400, 200 - end: 400, 0 - duration: 200 - assertVisible: text: "[Ad 5] SERP Ad (heuristic)" - tapOn: diff --git a/.maestro/ad_click_detection_flow_tests/06_mjs_heuristic_ad_domain_provided_but_empty_dsl_not_needed.yaml b/.maestro/ad_click_detection_flow_tests/06_mjs_heuristic_ad_domain_provided_but_empty_dsl_not_needed.yaml index 364124bb17..f55bdff711 100644 --- a/.maestro/ad_click_detection_flow_tests/06_mjs_heuristic_ad_domain_provided_but_empty_dsl_not_needed.yaml +++ b/.maestro/ad_click_detection_flow_tests/06_mjs_heuristic_ad_domain_provided_but_empty_dsl_not_needed.yaml @@ -1,4 +1,7 @@ appId: com.duckduckgo.mobile.ios +tags: + - adClick + --- - clearState - launchApp @@ -7,14 +10,14 @@ appId: com.duckduckgo.mobile.ios visible: text: "Let’s Do It!" index: 0 - file: ./shared/onboarding.yaml + file: ../shared/onboarding.yaml # Load Site - assertVisible: id: "searchEntry" - tapOn: id: "searchEntry" -- inputText: "https://www.search-company.site/" +- inputText: "https://www.search-company.site/#ad-id-6" - pressKey: Enter # Manage onboarding @@ -23,15 +26,7 @@ appId: com.duckduckgo.mobile.ios visible: text: "Got It" index: 0 - file: ./shared/onboarding_browsing.yaml - -- assertVisible: "Search engine" - -# Test Ad6 -- swipe: - start: 200, 200 - end: 200, 0 - duration: 200 + file: ../shared/onboarding_browsing.yaml - assertVisible: text: "[Ad 6] Shopping Tab Ad (heuristic)" diff --git a/.maestro/ad_click_detection_flow_tests/07_yjs_bing-provided_ad_domain_provided_u3_not_needed.yaml b/.maestro/ad_click_detection_flow_tests/07_yjs_bing-provided_ad_domain_provided_u3_not_needed.yaml index 33e5d389d6..4d37ae8f4b 100644 --- a/.maestro/ad_click_detection_flow_tests/07_yjs_bing-provided_ad_domain_provided_u3_not_needed.yaml +++ b/.maestro/ad_click_detection_flow_tests/07_yjs_bing-provided_ad_domain_provided_u3_not_needed.yaml @@ -1,4 +1,7 @@ appId: com.duckduckgo.mobile.ios +tags: + - adClick + --- - clearState - launchApp @@ -7,14 +10,14 @@ appId: com.duckduckgo.mobile.ios visible: text: "Let’s Do It!" index: 0 - file: ./shared/onboarding.yaml + file: ../shared/onboarding.yaml # Load Site - assertVisible: id: "searchEntry" - tapOn: id: "searchEntry" -- inputText: "https://www.search-company.site/" +- inputText: "https://www.search-company.site/#ad-id-7" - pressKey: Enter # Manage onboarding @@ -23,15 +26,7 @@ appId: com.duckduckgo.mobile.ios visible: text: "Got It" index: 0 - file: ./shared/onboarding_browsing.yaml - -- assertVisible: "Search engine" - -# Test Ad7 -- swipe: - start: 200, 250 - end: 200, 0 - duration: 200 + file: ../shared/onboarding_browsing.yaml - assertVisible: text: "[Ad 7] SERP Ad (SERP-provided)" diff --git a/.maestro/ad_click_detection_flow_tests/08_mjs_bing-provided_ad_domain_provided_dsl_not_needed.yaml b/.maestro/ad_click_detection_flow_tests/08_mjs_bing-provided_ad_domain_provided_dsl_not_needed.yaml index c379a4d28b..1443b604a9 100644 --- a/.maestro/ad_click_detection_flow_tests/08_mjs_bing-provided_ad_domain_provided_dsl_not_needed.yaml +++ b/.maestro/ad_click_detection_flow_tests/08_mjs_bing-provided_ad_domain_provided_dsl_not_needed.yaml @@ -1,4 +1,7 @@ appId: com.duckduckgo.mobile.ios +tags: + - adClick + --- - clearState - launchApp @@ -7,14 +10,14 @@ appId: com.duckduckgo.mobile.ios visible: text: "Let’s Do It!" index: 0 - file: ./shared/onboarding.yaml + file: ../shared/onboarding.yaml # Load Site - assertVisible: id: "searchEntry" - tapOn: id: "searchEntry" -- inputText: "https://www.search-company.site/" +- inputText: "https://www.search-company.site/#ad-id-8" - pressKey: Enter # Manage onboarding @@ -23,19 +26,7 @@ appId: com.duckduckgo.mobile.ios visible: text: "Got It" index: 0 - file: ./shared/onboarding_browsing.yaml - -- assertVisible: "Search engine" - -# Test Ad8 -- swipe: - start: 200, 200 - end: 200, 0 - duration: 200 -- swipe: - start: 200, 200 - end: 200, 0 - duration: 200 + file: ../shared/onboarding_browsing.yaml - assertVisible: text: "[Ad 8] Shopping Tab Ad (SERP-provided)" diff --git a/.maestro/ad_click_detection_flow_tests/09_yjs_bing-provided_ad_domain_provided_but_incorrect_u3_not_needed.yaml b/.maestro/ad_click_detection_flow_tests/09_yjs_bing-provided_ad_domain_provided_but_incorrect_u3_not_needed.yaml index 0e415654fa..672eb8e2b8 100644 --- a/.maestro/ad_click_detection_flow_tests/09_yjs_bing-provided_ad_domain_provided_but_incorrect_u3_not_needed.yaml +++ b/.maestro/ad_click_detection_flow_tests/09_yjs_bing-provided_ad_domain_provided_but_incorrect_u3_not_needed.yaml @@ -1,5 +1,9 @@ appId: com.duckduckgo.mobile.ios +tags: + - adClick + --- + - clearState - launchApp - runFlow: @@ -7,14 +11,14 @@ appId: com.duckduckgo.mobile.ios visible: text: "Let’s Do It!" index: 0 - file: ./shared/onboarding.yaml + file: ../shared/onboarding.yaml # Load Site - assertVisible: id: "searchEntry" - tapOn: id: "searchEntry" -- inputText: "https://www.search-company.site/" +- inputText: "https://www.search-company.site/#ad-id-9" - pressKey: Enter # Manage onboarding @@ -23,20 +27,9 @@ appId: com.duckduckgo.mobile.ios visible: text: "Got It" index: 0 - file: ./shared/onboarding_browsing.yaml - -- assertVisible: "Search engine" + file: ../shared/onboarding_browsing.yaml # Test Ad9 -- swipe: - start: 200, 200 - end: 200, 0 - duration: 200 -- swipe: - start: 200, 200 - end: 200, 0 - duration: 200 - - assertVisible: text: "[Ad 9] SERP Ad (SERP-provided)" - tapOn: diff --git a/.maestro/ad_click_detection_flow_tests/10_mjs_bing-provided_ad_domain_provided_but_incorrect_dsl_not_needed.yaml b/.maestro/ad_click_detection_flow_tests/10_mjs_bing-provided_ad_domain_provided_but_incorrect_dsl_not_needed.yaml index 41d31a2bed..479b962c3d 100644 --- a/.maestro/ad_click_detection_flow_tests/10_mjs_bing-provided_ad_domain_provided_but_incorrect_dsl_not_needed.yaml +++ b/.maestro/ad_click_detection_flow_tests/10_mjs_bing-provided_ad_domain_provided_but_incorrect_dsl_not_needed.yaml @@ -1,5 +1,9 @@ appId: com.duckduckgo.mobile.ios +tags: + - adClick + --- + - clearState - launchApp - runFlow: @@ -7,14 +11,14 @@ appId: com.duckduckgo.mobile.ios visible: text: "Let’s Do It!" index: 0 - file: ./shared/onboarding.yaml + file: ../shared/onboarding.yaml # Load Site - assertVisible: id: "searchEntry" - tapOn: id: "searchEntry" -- inputText: "https://www.search-company.site/" +- inputText: "https://www.search-company.site/#ad-id-10" - pressKey: Enter # Manage onboarding @@ -23,23 +27,7 @@ appId: com.duckduckgo.mobile.ios visible: text: "Got It" index: 0 - file: ./shared/onboarding_browsing.yaml - -- assertVisible: "Search engine" - -# Test Ad10 -- swipe: - start: 200, 200 - end: 200, 0 - duration: 200 -- swipe: - start: 200, 200 - end: 200, 0 - duration: 200 -- swipe: - start: 200, 200 - end: 200, 0 - duration: 200 + file: ../shared/onboarding_browsing.yaml - assertVisible: text: "[Ad 10] Shopping Tab Ad (SERP-provided)" diff --git a/.maestro/ad_click_detection_flow_tests/11_yjs_bing-provided_ad_domain_provided_but_its_not_a_domain_u3_not_needed.yaml b/.maestro/ad_click_detection_flow_tests/11_yjs_bing-provided_ad_domain_provided_but_its_not_a_domain_u3_not_needed.yaml index 694b0477a0..0fbc199b64 100644 --- a/.maestro/ad_click_detection_flow_tests/11_yjs_bing-provided_ad_domain_provided_but_its_not_a_domain_u3_not_needed.yaml +++ b/.maestro/ad_click_detection_flow_tests/11_yjs_bing-provided_ad_domain_provided_but_its_not_a_domain_u3_not_needed.yaml @@ -1,5 +1,9 @@ appId: com.duckduckgo.mobile.ios +tags: + - adClick + --- + - clearState - launchApp - runFlow: @@ -7,14 +11,14 @@ appId: com.duckduckgo.mobile.ios visible: text: "Let’s Do It!" index: 0 - file: ./shared/onboarding.yaml + file: ../shared/onboarding.yaml # Load Site - assertVisible: id: "searchEntry" - tapOn: id: "searchEntry" -- inputText: "https://www.search-company.site/" +- inputText: "https://www.search-company.site/#ad-id-11" - pressKey: Enter # Manage onboarding @@ -23,23 +27,7 @@ appId: com.duckduckgo.mobile.ios visible: text: "Got It" index: 0 - file: ./shared/onboarding_browsing.yaml - -- assertVisible: "Search engine" - -# Test Ad11 -- swipe: - start: 200, 200 - end: 200, 0 - duration: 200 -- swipe: - start: 200, 200 - end: 200, 0 - duration: 200 -- swipe: - start: 200, 200 - end: 200, 0 - duration: 200 + file: ../shared/onboarding_browsing.yaml - assertVisible: text: "[Ad 11] SERP Ad (heuristic)" diff --git a/.maestro/ad_click_detection_flow_tests/12_mjs_bing-provided_ad_domain_provided_but_its_not_a_domain_dsl_not_needed.yaml b/.maestro/ad_click_detection_flow_tests/12_mjs_bing-provided_ad_domain_provided_but_its_not_a_domain_dsl_not_needed.yaml index 0fa42f8694..f6a7a4816b 100644 --- a/.maestro/ad_click_detection_flow_tests/12_mjs_bing-provided_ad_domain_provided_but_its_not_a_domain_dsl_not_needed.yaml +++ b/.maestro/ad_click_detection_flow_tests/12_mjs_bing-provided_ad_domain_provided_but_its_not_a_domain_dsl_not_needed.yaml @@ -1,5 +1,9 @@ appId: com.duckduckgo.mobile.ios +tags: + - adClick + --- + - clearState - launchApp - runFlow: @@ -7,14 +11,14 @@ appId: com.duckduckgo.mobile.ios visible: text: "Let’s Do It!" index: 0 - file: ./shared/onboarding.yaml + file: ../shared/onboarding.yaml # Load Site - assertVisible: id: "searchEntry" - tapOn: id: "searchEntry" -- inputText: "https://www.search-company.site/" +- inputText: "https://www.search-company.site/#ad-id-12" - pressKey: Enter # Manage onboarding @@ -23,23 +27,7 @@ appId: com.duckduckgo.mobile.ios visible: text: "Got It" index: 0 - file: ./shared/onboarding_browsing.yaml - -- assertVisible: "Search engine" - -# Test Ad12 -- swipe: - start: 200, 200 - end: 200, 0 - duration: 200 -- swipe: - start: 200, 200 - end: 200, 0 - duration: 200 -- swipe: - start: 200, 200 - end: 200, 0 - duration: 200 + file: ../shared/onboarding_browsing.yaml - assertVisible: text: "[Ad 12] Shopping Tab Ad (heuristic)" diff --git a/.maestro/ad_click_detection_flow_tests/13_yjs_bing-provided_ad_domain_provided_but_its_a_subdomain_of_advertiser_u3_not_needed.yaml b/.maestro/ad_click_detection_flow_tests/13_yjs_bing-provided_ad_domain_provided_but_its_a_subdomain_of_advertiser_u3_not_needed.yaml index 1c46a73f43..ca0834351f 100644 --- a/.maestro/ad_click_detection_flow_tests/13_yjs_bing-provided_ad_domain_provided_but_its_a_subdomain_of_advertiser_u3_not_needed.yaml +++ b/.maestro/ad_click_detection_flow_tests/13_yjs_bing-provided_ad_domain_provided_but_its_a_subdomain_of_advertiser_u3_not_needed.yaml @@ -1,5 +1,9 @@ appId: com.duckduckgo.mobile.ios +tags: + - adClick + --- + - clearState - launchApp - runFlow: @@ -7,14 +11,14 @@ appId: com.duckduckgo.mobile.ios visible: text: "Let’s Do It!" index: 0 - file: ./shared/onboarding.yaml + file: ../shared/onboarding.yaml # Load Site - assertVisible: id: "searchEntry" - tapOn: id: "searchEntry" -- inputText: "https://www.search-company.site/" +- inputText: "https://www.search-company.site/#ad-id-13" - pressKey: Enter # Manage onboarding @@ -23,27 +27,7 @@ appId: com.duckduckgo.mobile.ios visible: text: "Got It" index: 0 - file: ./shared/onboarding_browsing.yaml - -- assertVisible: "Search engine" - -# Test Ad13 -- swipe: - start: 200, 200 - end: 200, 0 - duration: 200 -- swipe: - start: 200, 200 - end: 200, 0 - duration: 200 -- swipe: - start: 200, 200 - end: 200, 0 - duration: 200 -- swipe: - start: 200, 200 - end: 200, 0 - duration: 200 + file: ../shared/onboarding_browsing.yaml - assertVisible: text: "[Ad 13] SERP Ad (SERP-provided)" diff --git a/.maestro/ad_click_detection_flow_tests/14_mjs_bing-provided_ad_domain_provided_but_its_a_subdomain_of_advertiser_dsl_not_needed.yaml b/.maestro/ad_click_detection_flow_tests/14_mjs_bing-provided_ad_domain_provided_but_its_a_subdomain_of_advertiser_dsl_not_needed.yaml index 805327bcb1..e6bf86893c 100644 --- a/.maestro/ad_click_detection_flow_tests/14_mjs_bing-provided_ad_domain_provided_but_its_a_subdomain_of_advertiser_dsl_not_needed.yaml +++ b/.maestro/ad_click_detection_flow_tests/14_mjs_bing-provided_ad_domain_provided_but_its_a_subdomain_of_advertiser_dsl_not_needed.yaml @@ -1,5 +1,9 @@ appId: com.duckduckgo.mobile.ios +tags: + - adClick + --- + - clearState - launchApp - runFlow: @@ -7,14 +11,14 @@ appId: com.duckduckgo.mobile.ios visible: text: "Let’s Do It!" index: 0 - file: ./shared/onboarding.yaml + file: ../shared/onboarding.yaml # Load Site - assertVisible: id: "searchEntry" - tapOn: id: "searchEntry" -- inputText: "https://www.search-company.site/" +- inputText: "https://www.search-company.site/#ad-id-14" - pressKey: Enter # Manage onboarding @@ -23,27 +27,7 @@ appId: com.duckduckgo.mobile.ios visible: text: "Got It" index: 0 - file: ./shared/onboarding_browsing.yaml - -- assertVisible: "Search engine" - -# Test Ad13 -- swipe: - start: 200, 200 - end: 200, 0 - duration: 200 -- swipe: - start: 200, 200 - end: 200, 0 - duration: 200 -- swipe: - start: 200, 200 - end: 200, 0 - duration: 200 -- swipe: - start: 200, 200 - end: 200, 0 - duration: 200 + file: ../shared/onboarding_browsing.yaml - assertVisible: text: "[Ad 14] Shopping Tab Ad (SERP-provided)" diff --git a/.maestro/config.yaml b/.maestro/config.yaml index eae90d6ee7..b74550e22c 100644 --- a/.maestro/config.yaml +++ b/.maestro/config.yaml @@ -2,3 +2,5 @@ appStartup: enabled: false appSize: enabled: false +flows: + - "**" \ No newline at end of file diff --git a/.maestro/privacy_tests/01_single-site_single-tab_session.yaml b/.maestro/privacy_tests/01_single-site_single-tab_session.yaml index 2a70691bbf..bfb22d5973 100644 --- a/.maestro/privacy_tests/01_single-site_single-tab_session.yaml +++ b/.maestro/privacy_tests/01_single-site_single-tab_session.yaml @@ -1,6 +1,7 @@ -# single_site-single_tab.yaml - appId: com.duckduckgo.mobile.ios +tags: + - privacy + --- # Set up @@ -11,14 +12,14 @@ appId: com.duckduckgo.mobile.ios visible: text: "Let’s Do It!" index: 0 - file: ./shared/onboarding.yaml + file: ../shared/onboarding.yaml # Load Site - assertVisible: id: "searchEntry" - tapOn: id: "searchEntry" -- inputText: "https://www.search-company.site/" +- inputText: "https://www.search-company.site/#ad-id-5" - pressKey: Enter # Manage onboarding @@ -27,12 +28,9 @@ appId: com.duckduckgo.mobile.ios visible: text: "Got It" index: 0 - file: ./shared/onboarding_browsing.yaml - -- assertVisible: "Search engine" + file: ../shared/onboarding_browsing.yaml # Test -- scroll - assertVisible: "[Ad 5] SERP Ad (heuristic)" - tapOn: "[Ad 5] SERP Ad (heuristic)" - assertVisible: "Publisher site" diff --git a/.maestro/privacy_tests/02_single-site_new_tab_session.yaml b/.maestro/privacy_tests/02_single-site_new_tab_session.yaml index 26d162f525..c6b72bf028 100644 --- a/.maestro/privacy_tests/02_single-site_new_tab_session.yaml +++ b/.maestro/privacy_tests/02_single-site_new_tab_session.yaml @@ -1,4 +1,7 @@ appId: com.duckduckgo.mobile.ios +tags: + - privacy + --- # Set up @@ -9,14 +12,14 @@ appId: com.duckduckgo.mobile.ios visible: text: "Let’s Do It!" index: 0 - file: ./shared/onboarding.yaml + file: ../shared/onboarding.yaml # Load Site - assertVisible: id: "searchEntry" - tapOn: id: "searchEntry" -- inputText: "https://www.search-company.site/" +- inputText: "https://www.search-company.site/#ad-id-5" - pressKey: Enter # Manage onboarding @@ -25,12 +28,9 @@ appId: com.duckduckgo.mobile.ios visible: text: "Got It" index: 0 - file: ./shared/onboarding_browsing.yaml - -- assertVisible: "Search engine" + file: ../shared/onboarding_browsing.yaml # Test -- scroll - assertVisible: "[Ad 5] SERP Ad (heuristic)" - longPressOn: "[Ad 5] SERP Ad (heuristic)" - assertVisible: diff --git a/.maestro/privacy_tests/03_single-site_new-tab_session_variant_two.yaml b/.maestro/privacy_tests/03_single-site_new-tab_session_variant_two.yaml index c51af8ae80..3d56674a16 100644 --- a/.maestro/privacy_tests/03_single-site_new-tab_session_variant_two.yaml +++ b/.maestro/privacy_tests/03_single-site_new-tab_session_variant_two.yaml @@ -1,4 +1,7 @@ appId: com.duckduckgo.mobile.ios +tags: + - privacy + --- # Set up @@ -9,14 +12,14 @@ appId: com.duckduckgo.mobile.ios visible: text: "Let’s Do It!" index: 0 - file: ./shared/onboarding.yaml + file: ../shared/onboarding.yaml # Load Site - assertVisible: id: "searchEntry" - tapOn: id: "searchEntry" -- inputText: "https://www.search-company.site/" +- inputText: "https://www.search-company.site/#ad-id-5" - pressKey: Enter # Manage onboarding @@ -25,12 +28,9 @@ appId: com.duckduckgo.mobile.ios visible: text: "Got It" index: 0 - file: ./shared/onboarding_browsing.yaml - -- assertVisible: "Search engine" + file: ../shared/onboarding_browsing.yaml # Test -- scroll - assertVisible: "[Ad 5] SERP Ad (heuristic)" - tapOn: "[Ad 5] SERP Ad (heuristic)" - assertVisible: diff --git a/.maestro/privacy_tests/04_single-site_multi-tab_session.yaml b/.maestro/privacy_tests/04_single-site_multi-tab_session.yaml index 14d8aee917..071716d320 100644 --- a/.maestro/privacy_tests/04_single-site_multi-tab_session.yaml +++ b/.maestro/privacy_tests/04_single-site_multi-tab_session.yaml @@ -1,4 +1,7 @@ appId: com.duckduckgo.mobile.ios +tags: + - privacy + --- # Set up @@ -9,14 +12,14 @@ appId: com.duckduckgo.mobile.ios visible: text: "Let’s Do It!" index: 0 - file: ./shared/onboarding.yaml + file: ../shared/onboarding.yaml # Load Site - assertVisible: id: "searchEntry" - tapOn: id: "searchEntry" -- inputText: "https://www.search-company.site/" +- inputText: "https://www.search-company.site/#ad-id-5" - pressKey: Enter # Manage onboarding @@ -25,12 +28,9 @@ appId: com.duckduckgo.mobile.ios visible: text: "Got It" index: 0 - file: ./shared/onboarding_browsing.yaml - -- assertVisible: "Search engine" + file: ../shared/onboarding_browsing.yaml # Test -- scroll - assertVisible: "[Ad 5] SERP Ad (heuristic)" - tapOn: "[Ad 5] SERP Ad (heuristic)" - assertVisible: diff --git a/.maestro/privacy_tests/05_multi-site_single-tab_session.yaml b/.maestro/privacy_tests/05_multi-site_single-tab_session.yaml index 487b232ebc..34afabb5cc 100644 --- a/.maestro/privacy_tests/05_multi-site_single-tab_session.yaml +++ b/.maestro/privacy_tests/05_multi-site_single-tab_session.yaml @@ -1,4 +1,7 @@ appId: com.duckduckgo.mobile.ios +tags: + - privacy + --- # Set up @@ -9,14 +12,14 @@ appId: com.duckduckgo.mobile.ios visible: text: "Let’s Do It!" index: 0 - file: ./shared/onboarding.yaml + file: ../shared/onboarding.yaml # Load Site - assertVisible: id: "searchEntry" - tapOn: id: "searchEntry" -- inputText: "https://www.search-company.site/" +- inputText: "https://www.search-company.site/#ad-id-5" - pressKey: Enter # Manage onboarding @@ -25,12 +28,9 @@ appId: com.duckduckgo.mobile.ios visible: text: "Got It" index: 0 - file: ./shared/onboarding_browsing.yaml - -- assertVisible: "Search engine" + file: ../shared/onboarding_browsing.yaml # Test -- scroll - assertVisible: "[Ad 5] SERP Ad (heuristic)" - tapOn: "[Ad 5] SERP Ad (heuristic)" - assertVisible: diff --git a/.maestro/privacy_tests/06_multi-tab.yaml b/.maestro/privacy_tests/06_multi-tab.yaml index 3097f33422..b55aa36cbc 100644 --- a/.maestro/privacy_tests/06_multi-tab.yaml +++ b/.maestro/privacy_tests/06_multi-tab.yaml @@ -1,4 +1,7 @@ appId: com.duckduckgo.mobile.ios +tags: + - privacy + --- # Set up @@ -9,14 +12,14 @@ appId: com.duckduckgo.mobile.ios visible: text: "Let’s Do It!" index: 0 - file: ./shared/onboarding.yaml + file: ../shared/onboarding.yaml # Load Site - assertVisible: id: "searchEntry" - tapOn: id: "searchEntry" -- inputText: "https://www.search-company.site/" +- inputText: "https://www.search-company.site/#ad-id-5" - pressKey: Enter # Manage onboarding @@ -25,12 +28,9 @@ appId: com.duckduckgo.mobile.ios visible: text: "Got It" index: 0 - file: ./shared/onboarding_browsing.yaml - -- assertVisible: "Search engine" + file: ../shared/onboarding_browsing.yaml # Test -- scroll - assertVisible: "[Ad 5] SERP Ad (heuristic)" - tapOn: "[Ad 5] SERP Ad (heuristic)" - assertVisible: @@ -70,10 +70,8 @@ appId: com.duckduckgo.mobile.ios - tapOn: "Done" - assertVisible: text: "Publisher site" -- assertVisible: - id: "Tabs" -- tapOn: - id: "Tabs" +- assertVisible: Tab Switcher +- tapOn: Tab Switcher - assertVisible: id: "Add" - tapOn: diff --git a/.maestro/privacy_tests/07_browser_restart_mid-session.yaml b/.maestro/privacy_tests/07_browser_restart_mid-session.yaml index b598aee466..e0e8cf0516 100644 --- a/.maestro/privacy_tests/07_browser_restart_mid-session.yaml +++ b/.maestro/privacy_tests/07_browser_restart_mid-session.yaml @@ -1,4 +1,7 @@ appId: com.duckduckgo.mobile.ios +tags: + - privacy + --- # Set up @@ -9,14 +12,14 @@ appId: com.duckduckgo.mobile.ios visible: text: "Let’s Do It!" index: 0 - file: ./shared/onboarding.yaml + file: ../shared/onboarding.yaml # Load Site - assertVisible: id: "searchEntry" - tapOn: id: "searchEntry" -- inputText: "https://www.search-company.site/" +- inputText: "https://www.search-company.site/#ad-id-5" - pressKey: Enter # Manage onboarding @@ -25,12 +28,10 @@ appId: com.duckduckgo.mobile.ios visible: text: "Got It" index: 0 - file: ./shared/onboarding_browsing.yaml + file: ../shared/onboarding_browsing.yaml -- assertVisible: "Search engine" # Test -- scroll - assertVisible: "[Ad 5] SERP Ad (heuristic)" - tapOn: "[Ad 5] SERP Ad (heuristic)" - assertVisible: diff --git a/.maestro/privacy_tests/08_navigation_with_back_forward.yaml b/.maestro/privacy_tests/08_navigation_with_back_forward.yaml index 9048df1f86..c8cbd3f381 100644 --- a/.maestro/privacy_tests/08_navigation_with_back_forward.yaml +++ b/.maestro/privacy_tests/08_navigation_with_back_forward.yaml @@ -1,4 +1,7 @@ appId: com.duckduckgo.mobile.ios +tags: + - privacy + --- # Set up @@ -9,14 +12,14 @@ appId: com.duckduckgo.mobile.ios visible: text: "Let’s Do It!" index: 0 - file: ./shared/onboarding.yaml + file: ../shared/onboarding.yaml # Load Site - assertVisible: id: "searchEntry" - tapOn: id: "searchEntry" -- inputText: "https://www.search-company.site/" +- inputText: "https://www.search-company.site/#ad-id-5" - pressKey: Enter # Manage onboarding @@ -25,12 +28,9 @@ appId: com.duckduckgo.mobile.ios visible: text: "Got It" index: 0 - file: ./shared/onboarding_browsing.yaml - -- assertVisible: "Search engine" + file: ../shared/onboarding_browsing.yaml # Test -- scroll - assertVisible: "[Ad 5] SERP Ad (heuristic)" - tapOn: "[Ad 5] SERP Ad (heuristic)" - assertVisible: diff --git a/.maestro/privacy_tests/09_navigation_with_refresh.yaml b/.maestro/privacy_tests/09_navigation_with_refresh.yaml index 03441fdaa0..30d58f8db4 100644 --- a/.maestro/privacy_tests/09_navigation_with_refresh.yaml +++ b/.maestro/privacy_tests/09_navigation_with_refresh.yaml @@ -1,4 +1,7 @@ appId: com.duckduckgo.mobile.ios +tags: + - privacy + --- # Set up @@ -9,14 +12,14 @@ appId: com.duckduckgo.mobile.ios visible: text: "Let’s Do It!" index: 0 - file: ./shared/onboarding.yaml + file: ../shared/onboarding.yaml # Load Site - assertVisible: id: "searchEntry" - tapOn: id: "searchEntry" -- inputText: "https://www.search-company.site/" +- inputText: "https://www.search-company.site/#ad-id-5" - pressKey: Enter # Manage onboarding @@ -25,12 +28,9 @@ appId: com.duckduckgo.mobile.ios visible: text: "Got It" index: 0 - file: ./shared/onboarding_browsing.yaml - -- assertVisible: "Search engine" + file: ../shared/onboarding_browsing.yaml # Test -- scroll - assertVisible: "[Ad 5] SERP Ad (heuristic)" - tapOn: "[Ad 5] SERP Ad (heuristic)" - assertVisible: diff --git a/.maestro/release_tests/bookmarks.yaml b/.maestro/release_tests/bookmarks.yaml index e9a2c19add..56a81943e9 100644 --- a/.maestro/release_tests/bookmarks.yaml +++ b/.maestro/release_tests/bookmarks.yaml @@ -1,7 +1,10 @@ # bookmarks.yaml - appId: com.duckduckgo.mobile.ios +tags: + - release + --- + # Set up - clearState - launchApp @@ -10,7 +13,7 @@ appId: com.duckduckgo.mobile.ios visible: text: "Let’s Do It!" index: 0 - file: ./shared/onboarding.yaml + file: ../shared/onboarding.yaml # Load Site - assertVisible: @@ -26,7 +29,7 @@ appId: com.duckduckgo.mobile.ios visible: text: "Got It" index: 0 - file: ./shared/onboarding_browsing.yaml + file: ../shared/onboarding_browsing.yaml # Bookmark site - assertVisible: "Browsing menu" diff --git a/.maestro/release_tests/browsing.yaml b/.maestro/release_tests/browsing.yaml index a49e77fe7e..3be02ab923 100644 --- a/.maestro/release_tests/browsing.yaml +++ b/.maestro/release_tests/browsing.yaml @@ -1,7 +1,10 @@ # browsing.yaml - appId: com.duckduckgo.mobile.ios +tags: + - release + --- + # Set up - clearState - launchApp @@ -10,7 +13,7 @@ appId: com.duckduckgo.mobile.ios visible: text: "Let’s Do It!" index: 0 - file: ./shared/onboarding.yaml + file: ../shared/onboarding.yaml # Load Site - assertVisible: diff --git a/.maestro/release_tests/emailprotection.yaml b/.maestro/release_tests/emailprotection.yaml new file mode 100644 index 0000000000..e4cc61fba4 --- /dev/null +++ b/.maestro/release_tests/emailprotection.yaml @@ -0,0 +1,24 @@ +appId: com.duckduckgo.mobile.ios +tags: + - release + +--- + +- clearState +- launchApp +- runFlow: + when: + visible: + text: "Let’s Do It!" + index: 0 + file: ../shared/onboarding.yaml + +- tapOn: Settings +- scroll +- scroll +- assertVisible: Email Protection +- tapOn: Email Protection +- assertVisible: + id: searchEntry +- assertVisible: https://duckduckgo.com/email/ +- assertVisible: Email privacy, simplified. \ No newline at end of file diff --git a/.maestro/release_tests/favorites.yaml b/.maestro/release_tests/favorites.yaml index d258be84ad..f33ae3bff7 100644 --- a/.maestro/release_tests/favorites.yaml +++ b/.maestro/release_tests/favorites.yaml @@ -1,7 +1,10 @@ # favorites.yaml - appId: com.duckduckgo.mobile.ios +tags: + - release + --- + # Set up - clearState - launchApp @@ -10,7 +13,7 @@ appId: com.duckduckgo.mobile.ios visible: text: "Let’s Do It!" index: 0 - file: ./shared/onboarding.yaml + file: ../shared/onboarding.yaml # Load Site - assertVisible: @@ -26,7 +29,7 @@ appId: com.duckduckgo.mobile.ios visible: text: "Got It" index: 0 - file: ./shared/onboarding_browsing.yaml + file: ../shared/onboarding_browsing.yaml # Add site to favorites from menu - assertVisible: "Browsing menu" diff --git a/.maestro/release_tests/tabs.yaml b/.maestro/release_tests/tabs.yaml index c0a4aa085f..fd9b7460fe 100644 --- a/.maestro/release_tests/tabs.yaml +++ b/.maestro/release_tests/tabs.yaml @@ -1,7 +1,10 @@ # tabs.yaml - appId: com.duckduckgo.mobile.ios +tags: + - release + --- + # Set up - clearState - launchApp @@ -10,14 +13,14 @@ appId: com.duckduckgo.mobile.ios visible: text: "Let’s Do It!" index: 0 - file: ./shared/onboarding.yaml + file: ../shared/onboarding.yaml # Load Site - assertVisible: id: "searchEntry" - tapOn: id: "searchEntry" -- inputText: "https://privacy-test-pages.glitch.me" +- inputText: "https://privacy-test-pages.site" - pressKey: Enter # Manage onboarding @@ -26,15 +29,13 @@ appId: com.duckduckgo.mobile.ios visible: text: "Got It" index: 0 - file: ./shared/onboarding_browsing.yaml + file: ../shared/onboarding_browsing.yaml - assertVisible: ".*Privacy Test Pages.*" # Add tab -- assertVisible: - id: "Tabs" -- tapOn: - id: "Tabs" +- assertVisible: Tab Switcher +- tapOn: Tab Switcher - assertVisible: ".*Privacy Test Pages.*" - assertVisible: id: "Add" @@ -49,23 +50,19 @@ appId: com.duckduckgo.mobile.ios - assertVisible: "Search engine" # Switch Tab -- assertVisible: - id: "Tabs" -- tapOn: - id: "Tabs" +- assertVisible: Tab Switcher +- tapOn: Tab Switcher - assertVisible: ".*Privacy Test Pages.*" - assertVisible: ".*Ad Click Flow.*" - assertVisible: "2 Private Tabs" -- tapOn: "Open \"Privacy Test Pages - Home\" at privacy-test-pages.glitch.me" +- tapOn: "Open \"Privacy Test Pages - Home\" at privacy-test-pages.site" - assertNotVisible: ".*Ad Click Flow.*" - assertVisible: ".*Privacy Test Pages.*" - tapOn: "Refresh Page" # Close Tab -- assertVisible: - id: "Tabs" -- tapOn: - id: "Tabs" +- assertVisible: Tab Switcher +- tapOn: Tab Switcher - assertVisible: ".*Privacy Test Pages.*" - assertVisible: ".*Ad Click Flow.*" - assertVisible: "2 Private Tabs" diff --git a/.maestro/shared/set_internal_user.yaml b/.maestro/shared/set_internal_user.yaml new file mode 100644 index 0000000000..97d6e6c9c4 --- /dev/null +++ b/.maestro/shared/set_internal_user.yaml @@ -0,0 +1,10 @@ +appId: com.duckduckgo.mobile.ios +--- + +- scroll +- scroll +- scroll +- assertVisible: Debug Menu +- tapOn: Debug Menu +- tapOn: Internal User State +- tapOn: Settings \ No newline at end of file diff --git a/.maestro/shared/sync_create.yaml b/.maestro/shared/sync_create.yaml new file mode 100644 index 0000000000..bc4bb338d0 --- /dev/null +++ b/.maestro/shared/sync_create.yaml @@ -0,0 +1,10 @@ +appId: com.duckduckgo.mobile.ios +--- + +- assertVisible: Sync & Back Up +- tapOn: Sync & Back Up +- assertVisible: Sync & Back Up +- tapOn: Start Sync & Back Up +- assertVisible: All Set! +- tapOn: Next +- assertVisible: Save Recovery Code? diff --git a/.maestro/shared/sync_delete.yaml b/.maestro/shared/sync_delete.yaml new file mode 100644 index 0000000000..54a78f3c15 --- /dev/null +++ b/.maestro/shared/sync_delete.yaml @@ -0,0 +1,9 @@ +appId: com.duckduckgo.mobile.ios +--- + +- assertVisible: Sync & Back Up +- scroll +- tapOn: + point: 50%,91% # TODO: Revisit after new setup flow has been implemented. +- assertVisible: Delete Server Data? +- tapOn: Delete Server Data \ No newline at end of file diff --git a/.maestro/sync_tests/01_create_account.yaml b/.maestro/sync_tests/01_create_account.yaml new file mode 100644 index 0000000000..5b48e6c829 --- /dev/null +++ b/.maestro/sync_tests/01_create_account.yaml @@ -0,0 +1,27 @@ +appId: com.duckduckgo.mobile.ios +tags: + - sync + +--- + +- clearState +- launchApp +- runFlow: + when: + visible: + text: "Let’s Do It!" + index: 0 + file: ../shared/onboarding.yaml + +- tapOn: Settings +- runFlow: + file: ../shared/set_internal_user.yaml +- runFlow: + file: ../shared/sync_create.yaml + + +# Clean up +- tapOn: Not Now +- assertVisible: Sync & Back Up +- runFlow: + file: ../shared/sync_delete.yaml diff --git a/.maestro/sync_tests/02_login_account.yaml b/.maestro/sync_tests/02_login_account.yaml new file mode 100644 index 0000000000..99a69684fa --- /dev/null +++ b/.maestro/sync_tests/02_login_account.yaml @@ -0,0 +1,42 @@ +appId: com.duckduckgo.mobile.ios +tags: + - sync + +--- + +# Create an account +- clearState +- launchApp +- runFlow: + when: + visible: + text: "Let’s Do It!" + index: 0 + file: ../shared/onboarding.yaml + +- tapOn: Settings +- runFlow: + file: ../shared/set_internal_user.yaml +- runFlow: + file: ../shared/sync_create.yaml + +# Copy Sync Code and Log Out +- tapOn: Copy Code +- tapOn: Not Now +- assertVisible: Sync & Back Up +- tapOn: Turn Off Sync & Back Up +- assertVisible: Turn Off Sync? +- tapOn: Remove + +# Login +- assertVisible: Sync & Back Up +- tapOn: Enter Text Code +- tapOn: Paste +- assertVisible: Device Synced! +- tapOn: Next +- tapOn: Not Now + +# Clean up +- assertVisible: Sync & Back Up +- runFlow: + file: ../shared/sync_delete.yaml diff --git a/.maestro/sync_tests/03_recover_account.yaml b/.maestro/sync_tests/03_recover_account.yaml new file mode 100644 index 0000000000..1cd62c3a81 --- /dev/null +++ b/.maestro/sync_tests/03_recover_account.yaml @@ -0,0 +1,71 @@ +appId: com.duckduckgo.mobile.ios +tags: + - sync + +--- + +- clearState +- launchApp +- runFlow: + when: + visible: + text: "Let’s Do It!" + index: 0 + file: ../shared/onboarding.yaml + +# This is a workaround to: +# - Put the code in the clipboard on Maestro Cloud +# - Prevent iOS from showing the Paste permission alert as Maestro can't handle it +- tapOn: + id: searchEntry +- inputText: ${CODE} + +- evalScript: ${output.counter = 0} +- repeat: + while: + true: ${output.counter < 3} + notVisible: Select All + commands: + - longPressOn: + id: "searchEntry" + - evalScript: ${output.counter = output.counter + 1} + +- tapOn: 'Select All' +- tapOn: Cut +- evalScript: ${output.counter = 0} +- repeat: + while: + true: ${output.counter < 3} + notVisible: Paste + commands: + - tapOn: + id: "searchEntry" + - evalScript: ${output.counter = output.counter + 1} +- tapOn: Paste +- tapOn: Cancel + +- tapOn: Close Tabs and Clear Data +- tapOn: Close Tabs and Clear Data +- runFlow: + when: + visible: + text: Cancel + commands: + - tapOn: Cancel +# + +# Recover Account test +- tapOn: Settings +- runFlow: + file: ../shared/set_internal_user.yaml +- assertVisible: Sync & Back Up +- tapOn: Sync & Back Up +- assertVisible: Sync & Back up +- tapOn: Recover Your Data +- tapOn: Enter Text Code +- tapOn: Paste +- assertVisible: Device Synced! +- tapOn: Next +- tapOn: Not Now +- tapOn: Settings +- tapOn: Done \ No newline at end of file diff --git a/.maestro/sync_tests/04_sync_data.yaml b/.maestro/sync_tests/04_sync_data.yaml new file mode 100644 index 0000000000..79f2f68cea --- /dev/null +++ b/.maestro/sync_tests/04_sync_data.yaml @@ -0,0 +1,181 @@ +appId: com.duckduckgo.mobile.ios +tags: + - sync + +--- + +- clearState +- launchApp +- runFlow: + when: + visible: + text: "Let’s Do It!" + index: 0 + file: ../shared/onboarding.yaml + +# Add local favorite and bookmark +- tapOn: + id: searchEntry +- inputText: www.duckduckgo.com +- pressKey: Enter +- runFlow: + when: + visible: + text: "Got It" + commands: + - tapOn: Got It +- tapOn: Browsing Menu +- tapOn: Add Favorite +- tapOn: + id: searchEntry +- inputText: www.spreadprivacy.com +- pressKey: Enter +- tapOn: Browsing Menu +- tapOn: Add Bookmark + +# Add local login +- tapOn: Browsing Menu +- tapOn: Settings +- tapOn: Logins +- tapOn: Add 24 +- tapOn: Title +- inputText: My Personal Website +- tapOn: username@example.com +- inputText: me@mypersonalwebsite.com +- tapOn: example.com +- inputText: mypersonalwebsite.com +- tapOn: Save +- tapOn: Logins +- tapOn: Settings +- tapOn: Done + +# Sync data +# This is a workaround to: +# - Put the code in the clipboard on Maestro Cloud +# - Prevent iOS from showing the Paste permission alert as Maestro can't handle it +- tapOn: + id: searchEntry +- inputText: ${CODE} + +- evalScript: ${output.counter = 0} +- repeat: + while: + true: ${output.counter < 3} + notVisible: Select All + commands: + - longPressOn: + id: "searchEntry" + - evalScript: ${output.counter = output.counter + 1} + +- tapOn: 'Select All' +- tapOn: Cut +- evalScript: ${output.counter = 0} +- repeat: + while: + true: ${output.counter < 3} + notVisible: Paste + commands: + - tapOn: + id: "searchEntry" + - evalScript: ${output.counter = output.counter + 1} +- tapOn: Paste +- tapOn: Cancel + +- tapOn: Close Tabs and Clear Data +- tapOn: Close Tabs and Clear Data +- runFlow: + when: + visible: + text: Cancel + commands: + - tapOn: Cancel +# + +- tapOn: Settings +- runFlow: + file: ../shared/set_internal_user.yaml +- assertVisible: Sync & Back Up +- tapOn: Sync & Back Up +- assertVisible: Sync & Back up +- tapOn: Recover Your Data +- tapOn: Enter Text Code +- tapOn: Paste +- assertVisible: Device Synced! +- tapOn: Next +- tapOn: Not Now +- tapOn: Settings +- tapOn: Done + +# Verify bookmarks and favorites have been merged +- tapOn: Bookmarks + +- assertVisible: Spread Privacy +- assertVisible: Stack Overflow - Where Developers Learn, Share, & Build Careers +- assertVisible: DuckDuckGo — Privacy, simplified. +- assertVisible: DuckDuckGo · GitHub +- assertVisible: "Wolfram|Alpha: Computational Intelligence" +- assertVisible: news +- assertVisible: code +- assertVisible: sports +- tapOn: news +- assertVisible: Breaking News, Latest News and Videos | CNN +- assertVisible: News, sport and opinion from the Guardian's global edition | The Guardian +- tapOn: Bookmarks +- tapOn: code +- assertVisible: "GitHub - duckduckgo/Android: DuckDuckGo Android App" +- assertVisible: "GitHub - duckduckgo/iOS: DuckDuckGo iOS Application" +- tapOn: Bookmarks +- tapOn: sports +- assertVisible: NFL.com | Official Site of the National Football League +- assertVisible: AS.com - Diario online deportivo. Fútbol, motor y mucho más +- tapOn: Bookmarks + +# Only expect local favorites when Share Favorites is off +- tapOn: Favorites +- assertVisible: DuckDuckGo — Privacy, simplified. + +# Enable Share Favorites and expect all favorites +- tapOn: Done +- tapOn: Settings +- tapOn: Sync & Back Up +- scroll +- assertVisible: Share Favorites +- tapOn: "0" +- tapOn: Settings +- tapOn: Done +- tapOn: Bookmarks +- tapOn: Favorites +- assertVisible: NFL.com | Official Site of the National Football League +- assertVisible: DuckDuckGo · GitHub +- assertVisible: Stack Overflow - Where Developers Learn, Share, & Build Careers +- tapOn: Done + +# Verify logins +- tapOn: Settings +- tapOn: Logins +- assertVisible: Unlock device to access saved Logins +- tapOn: Passcode field +- inputText: "0000" +- pressKey: Enter +- assertVisible: Dax Login +- tapOn: Dax Login +- assertVisible: daxthetest +- assertVisible: duckduckgo.com +- tapOn: Logins +- assertVisible: Github +- tapOn: Github +- assertVisible: githubusername +- assertVisible: github.com +- tapOn: Logins +- assertVisible: StackOverflow +- tapOn: StackOverflow +- assertVisible: stacker +- assertVisible: stackoverflow.com +- tapOn: Logins +- assertVisible: My Personal Website +- tapOn: My Personal Website +- assertVisible: me@mypersonalwebsite.com +- assertVisible: mypersonalwebsite.com +- tapOn: Logins +- tapOn: Settings +- tapOn: Done \ No newline at end of file diff --git a/Configuration/Version.xcconfig b/Configuration/Version.xcconfig index 86866ee7eb..698f293328 100644 --- a/Configuration/Version.xcconfig +++ b/Configuration/Version.xcconfig @@ -1 +1 @@ -MARKETING_VERSION = 7.92.0 +MARKETING_VERSION = 7.97.1 diff --git a/Core/AppDeepLinkSchemes.swift b/Core/AppDeepLinkSchemes.swift index c88f697cbc..38fa976b99 100644 --- a/Core/AppDeepLinkSchemes.swift +++ b/Core/AppDeepLinkSchemes.swift @@ -31,6 +31,8 @@ public enum AppDeepLinkSchemes: String, CaseIterable { case addFavorite = "ddgAddFavorite" + case openVPN = "ddgOpenVPN" + public var url: URL { URL(string: rawValue + "://")! } diff --git a/Core/AppPrivacyConfigurationDataProvider.swift b/Core/AppPrivacyConfigurationDataProvider.swift index 56755c581d..c984d0206f 100644 --- a/Core/AppPrivacyConfigurationDataProvider.swift +++ b/Core/AppPrivacyConfigurationDataProvider.swift @@ -23,8 +23,8 @@ import BrowserServicesKit final public class AppPrivacyConfigurationDataProvider: EmbeddedDataProvider { public struct Constants { - public static let embeddedDataETag = "\"54efb258435567856e087913f93d43df\"" - public static let embeddedDataSHA = "d0f9c70c5baba23a0966b277f8b4c949ecf01612558a50d2bda720fe1919b43f" + public static let embeddedDataETag = "\"b8a321aec612923a53b958fbdbfa541e\"" + public static let embeddedDataSHA = "af271d73da0863e678670fb0d9cf4f8269ddb7e584599bfb57d5a370961dbc4b" } public var embeddedDataEtag: String { diff --git a/Core/AppTrackerDataSetProvider.swift b/Core/AppTrackerDataSetProvider.swift index edcbbad058..c41f8ed1b5 100644 --- a/Core/AppTrackerDataSetProvider.swift +++ b/Core/AppTrackerDataSetProvider.swift @@ -23,8 +23,8 @@ import BrowserServicesKit final public class AppTrackerDataSetProvider: EmbeddedDataProvider { public struct Constants { - public static let embeddedDataETag = "\"a1bb76901a395eb251cd6f30f54037f1\"" - public static let embeddedDataSHA = "53cad742076d1382fba8db0e508307668eb0609738d526cb16cbc623b50410fc" + public static let embeddedDataETag = "\"3a50d09fd78a893f1a284051d1f777de\"" + public static let embeddedDataSHA = "2c1995807a61fd9fa311baab01633411282759732f098765f5380bda5e92b9e2" } public var embeddedDataEtag: String { diff --git a/Core/AppURLs.swift b/Core/AppURLs.swift index 6feb8e7407..e2250c3744 100644 --- a/Core/AppURLs.swift +++ b/Core/AppURLs.swift @@ -29,11 +29,15 @@ public extension URL { static let autocomplete = URL(string: "\(base)/ac/")! static let emailProtection = URL(string: "\(base)/email")! + static let emailProtectionSignUp = URL(string: "\(base)/email/start-incontext")! static let emailProtectionQuickLink = URL(string: AppDeepLinkSchemes.quickLink.appending("\(ddg.host!)/email"))! static let aboutLink = URL(string: AppDeepLinkSchemes.quickLink.appending("\(ddg.host!)/about"))! static let surrogates = URL(string: "\(staticBase)/surrogates.txt")! - static let privacyConfig = URL(string: "\(staticBase)/trackerblocking/config/v3/ios-config.json")! + + // The following URLs shall match the ones in update_embedded.sh. + // Danger checks that the URLs match on every PR. If the code changes, the regex that Danger uses may need an update. + static let privacyConfig = URL(string: "\(staticBase)/trackerblocking/config/v4/ios-config.json")! static let trackerDataSet = URL(string: "\(staticBase)/trackerblocking/v5/current/ios-tds.json")! static let bloomFilter = URL(string: "\(staticBase)/https/https-mobile-v2-bloom.bin")! static let bloomFilterSpec = URL(string: "\(staticBase)/https/https-mobile-v2-bloom-spec.json")! diff --git a/Core/BookmarksCachingSearch.swift b/Core/BookmarksCachingSearch.swift index 17b264b198..c43c3f2712 100644 --- a/Core/BookmarksCachingSearch.swift +++ b/Core/BookmarksCachingSearch.swift @@ -70,8 +70,8 @@ public class CoreDataBookmarksSearchStore: BookmarksSearchStore { fetchRequest.resultType = .dictionaryResultType fetchRequest.propertiesToFetch = [#keyPath(BookmarkEntity.title), #keyPath(BookmarkEntity.url), - #keyPath(BookmarkEntity.favoriteFolder), #keyPath(BookmarkEntity.objectID)] + fetchRequest.relationshipKeyPathsForPrefetching = [#keyPath(BookmarkEntity.favoriteFolders)] context.perform { let result = try? context.fetch(fetchRequest) as? [Dictionary] @@ -131,7 +131,7 @@ public class BookmarksCachingSearch: BookmarksStringSearch { self.init(objectID: objectID, title: title, url: url, - isFavorite: bookmark[#keyPath(BookmarkEntity.favoriteFolder)] != nil) + isFavorite: (bookmark[#keyPath(BookmarkEntity.favoriteFolders)] as? Set)?.isEmpty != true) } public func togglingFavorite() -> BookmarksStringSearchResult { diff --git a/Core/BookmarksExporter.swift b/Core/BookmarksExporter.swift index 53fb563af9..294b59a076 100644 --- a/Core/BookmarksExporter.swift +++ b/Core/BookmarksExporter.swift @@ -29,9 +29,11 @@ public enum BookmarksExporterError: Error { public struct BookmarksExporter { private(set) var coreDataStorage: CoreDataDatabase + private let favoritesDisplayMode: FavoritesDisplayMode - public init(coreDataStore: CoreDataDatabase) { + public init(coreDataStore: CoreDataDatabase, favoritesDisplayMode: FavoritesDisplayMode) { coreDataStorage = coreDataStore + self.favoritesDisplayMode = favoritesDisplayMode } public func exportBookmarksTo(url: URL) throws { @@ -64,7 +66,7 @@ public struct BookmarksExporter { content.append(Template.bookmark(level: level, title: entity.title!.escapedForHTML, url: entity.url!, - isFavorite: entity.isFavorite)) + isFavorite: entity.isFavorite(on: favoritesDisplayMode.displayedFolder))) } } return content diff --git a/Core/BookmarksImporter.swift b/Core/BookmarksImporter.swift index a98a7c6668..177dcf2955 100644 --- a/Core/BookmarksImporter.swift +++ b/Core/BookmarksImporter.swift @@ -42,8 +42,8 @@ final public class BookmarksImporter { private(set) var importedBookmarks: [BookmarkOrFolder] = [] private(set) var coreDataStorage: BookmarkCoreDataImporter - public init(coreDataStore: CoreDataDatabase) { - coreDataStorage = BookmarkCoreDataImporter(database: coreDataStore) + public init(coreDataStore: CoreDataDatabase, favoritesDisplayMode: FavoritesDisplayMode) { + coreDataStorage = BookmarkCoreDataImporter(database: coreDataStore, favoritesDisplayMode: favoritesDisplayMode) } func isDocumentInSafariFormat(_ document: Document) -> Bool { diff --git a/Core/BookmarksModelsErrorHandling.swift b/Core/BookmarksModelsErrorHandling.swift index aad7935bbe..5d25ef7c80 100644 --- a/Core/BookmarksModelsErrorHandling.swift +++ b/Core/BookmarksModelsErrorHandling.swift @@ -82,18 +82,22 @@ public extension BookmarkEditorViewModel { convenience init(editingEntityID: NSManagedObjectID, bookmarksDatabase: CoreDataDatabase, + favoritesDisplayMode: FavoritesDisplayMode, syncService: DDGSyncing?) { self.init(editingEntityID: editingEntityID, bookmarksDatabase: bookmarksDatabase, + favoritesDisplayMode: favoritesDisplayMode, errorEvents: BookmarksModelsErrorHandling(syncService: syncService)) } convenience init(creatingFolderWithParentID parentFolderID: NSManagedObjectID?, bookmarksDatabase: CoreDataDatabase, + favoritesDisplayMode: FavoritesDisplayMode, syncService: DDGSyncing?) { self.init(creatingFolderWithParentID: parentFolderID, bookmarksDatabase: bookmarksDatabase, + favoritesDisplayMode: favoritesDisplayMode, errorEvents: BookmarksModelsErrorHandling(syncService: syncService)) } } @@ -102,25 +106,25 @@ public extension BookmarkListViewModel { convenience init(bookmarksDatabase: CoreDataDatabase, parentID: NSManagedObjectID?, + favoritesDisplayMode: FavoritesDisplayMode, syncService: DDGSyncing?) { self.init(bookmarksDatabase: bookmarksDatabase, parentID: parentID, + favoritesDisplayMode: favoritesDisplayMode, errorEvents: BookmarksModelsErrorHandling(syncService: syncService)) } } public extension FavoritesListViewModel { - convenience init(bookmarksDatabase: CoreDataDatabase) { - self.init(bookmarksDatabase: bookmarksDatabase, - errorEvents: BookmarksModelsErrorHandling()) + convenience init(bookmarksDatabase: CoreDataDatabase, favoritesDisplayMode: FavoritesDisplayMode) { + self.init(bookmarksDatabase: bookmarksDatabase, errorEvents: BookmarksModelsErrorHandling(), favoritesDisplayMode: favoritesDisplayMode) } } public extension MenuBookmarksViewModel { convenience init(bookmarksDatabase: CoreDataDatabase, syncService: DDGSyncing?) { - self.init(bookmarksDatabase: bookmarksDatabase, - errorEvents: BookmarksModelsErrorHandling(syncService: syncService)) + self.init(bookmarksDatabase: bookmarksDatabase, errorEvents: BookmarksModelsErrorHandling(syncService: syncService)) } } diff --git a/Core/ContentBlocking.swift b/Core/ContentBlocking.swift index b38a36baf3..2767e48e4d 100644 --- a/Core/ContentBlocking.swift +++ b/Core/ContentBlocking.swift @@ -22,6 +22,7 @@ import BrowserServicesKit import Combine import Common + public final class ContentBlocking { public static let shared = ContentBlocking() @@ -34,6 +35,11 @@ public final class ContentBlocking { private let exceptionsSource: DefaultContentBlockerRulesExceptionsSource private let lastCompiledRulesStore: AppLastCompiledRulesStore + public var onCriticalError: (() -> Void)? { + didSet { + contentBlockingManager.onCriticalError = onCriticalError + } + } private init(privacyConfigurationManager: PrivacyConfigurationManaging? = nil) { let internalUserDecider = DefaultInternalUserDecider(store: InternalUserStore()) @@ -128,7 +134,7 @@ public final class ContentBlocking { } } - + public func makeAdClickAttributionDetection(tld: TLD) -> AdClickAttributionDetection { AdClickAttributionDetection(feature: adClickAttribution, tld: tld, diff --git a/Core/Core.h b/Core/Core.h index 8d22735937..ac21b9bd95 100644 --- a/Core/Core.h +++ b/Core/Core.h @@ -19,7 +19,6 @@ #import -#import "BloomFilterWrapper.h" //! Project version number for Core. diff --git a/Core/DefaultVariantManager.swift b/Core/DefaultVariantManager.swift index 7539b2981b..158b52d99d 100644 --- a/Core/DefaultVariantManager.swift +++ b/Core/DefaultVariantManager.swift @@ -27,6 +27,7 @@ extension FeatureName { // public static let experimentalFeature = FeatureName(rawValue: "experimentalFeature") public static let fixedUserAgent = FeatureName(rawValue: "fixedUserAgent") + public static let closestUserAgent = FeatureName(rawValue: "closestUserAgent") } public struct VariantIOS: Variant { @@ -61,8 +62,10 @@ public struct VariantIOS: Variant { VariantIOS(name: "sc", weight: doNotAllocate, isIncluded: When.always, features: []), VariantIOS(name: "sd", weight: doNotAllocate, isIncluded: When.always, features: []), VariantIOS(name: "se", weight: doNotAllocate, isIncluded: When.always, features: []), - VariantIOS(name: "me", weight: 1, isIncluded: When.always, features: []), - VariantIOS(name: "mf", weight: 1, isIncluded: When.always, features: [.fixedUserAgent]), + VariantIOS(name: "me", weight: doNotAllocate, isIncluded: When.always, features: []), + VariantIOS(name: "mf", weight: doNotAllocate, isIncluded: When.always, features: []), + VariantIOS(name: "mg", weight: 1, isIncluded: When.always, features: [.fixedUserAgent]), + VariantIOS(name: "mh", weight: 1, isIncluded: When.always, features: [.closestUserAgent]), returningUser ] diff --git a/Core/FeatureFlag.swift b/Core/FeatureFlag.swift index c31da129d1..2f0819907d 100644 --- a/Core/FeatureFlag.swift +++ b/Core/FeatureFlag.swift @@ -32,12 +32,14 @@ public enum FeatureFlag: String { case incontextSignup case appTrackingProtection case networkProtection + case networkProtectionWaitlistAccess + case networkProtectionWaitlistActive } extension FeatureFlag: FeatureFlagSourceProviding { public var source: FeatureFlagSource { switch self { - case .debugMenu, .sync, .appTrackingProtection, .networkProtection: + case .debugMenu, .sync, .appTrackingProtection, .networkProtection, .networkProtectionWaitlistAccess, .networkProtectionWaitlistActive: return .internalOnly case .autofillCredentialInjecting: return .remoteReleasable(.subfeature(AutofillSubfeature.credentialsAutofill)) diff --git a/Core/LegacyBookmarksStoreMigration.swift b/Core/LegacyBookmarksStoreMigration.swift index 248d96f12b..dfd6a4c371 100644 --- a/Core/LegacyBookmarksStoreMigration.swift +++ b/Core/LegacyBookmarksStoreMigration.swift @@ -39,7 +39,7 @@ public class LegacyBookmarksStoreMigration { } } else { // Initialize structure if needed - BookmarkUtils.prepareFoldersStructure(in: context) + BookmarkUtils.prepareLegacyFoldersStructure(in: context) if context.hasChanges { do { try context.save(onErrorFire: .bookmarksCouldNotPrepareDatabase) @@ -82,7 +82,8 @@ public class LegacyBookmarksStoreMigration { BookmarkUtils.prepareFoldersStructure(in: destination) guard let newRoot = BookmarkUtils.fetchRootFolder(destination), - let newFavoritesRoot = BookmarkUtils.fetchFavoritesFolder(destination) else { + let newFavoritesRoot = BookmarkUtils.fetchFavoritesFolder(withUUID: FavoritesFolderID.unified.rawValue, in: destination), + let newMobileFavoritesRoot = BookmarkUtils.fetchFavoritesFolder(withUUID: FavoritesFolderID.mobile.rawValue, in: destination) else { Pixel.fire(pixel: .bookmarksMigrationCouldNotPrepareDatabase) Thread.sleep(forTimeInterval: 2) fatalError("Could not write to Bookmarks DB") @@ -169,6 +170,8 @@ public class LegacyBookmarksStoreMigration { }() bookmark.addToFavorites(insertAt: 0, favoritesRoot: newFavoritesRoot) + bookmark.addToFavorites(insertAt: 0, + favoritesRoot: newMobileFavoritesRoot) } do { @@ -176,7 +179,7 @@ public class LegacyBookmarksStoreMigration { } catch { destination.reset() - BookmarkUtils.prepareFoldersStructure(in: destination) + BookmarkUtils.prepareLegacyFoldersStructure(in: destination) do { try destination.save(onErrorFire: .bookmarksMigrationCouldNotPrepareDatabaseOnFailedMigration) } catch { diff --git a/Core/PixelEvent.swift b/Core/PixelEvent.swift index 1b1193ddc2..37e48dcb73 100644 --- a/Core/PixelEvent.swift +++ b/Core/PixelEvent.swift @@ -203,7 +203,8 @@ extension Pixel { case autofillLoginsSaveLoginModalDisplayed case autofillLoginsSaveLoginModalConfirmed case autofillLoginsSaveLoginModalDismissed - + case autofillLoginsSaveLoginModalExcludeSiteConfirmed + case autofillLoginsSavePasswordModalDisplayed case autofillLoginsSavePasswordModalConfirmed case autofillLoginsSavePasswordModalDismissed @@ -238,6 +239,9 @@ extension Pixel { case autofillLoginsSettingsEnabled case autofillLoginsSettingsDisabled case autofillLoginsSettingsAddNewLoginErrorAttemptedToCreateDuplicate + case autofillLoginsSettingsResetExcludedDisplayed + case autofillLoginsSettingsResetExcludedConfirmed + case autofillLoginsSettingsResetExcludedDismissed case autofillLoginsPasswordGenerationPromptDisplayed case autofillLoginsPasswordGenerationPromptConfirmed @@ -245,6 +249,9 @@ extension Pixel { case autofillJSPixelFired(_ pixel: AutofillUserScript.JSPixel) + case navigationbarPositionBottom + case navigationBarPositionTop + case secureVaultInitError case secureVaultError @@ -309,6 +316,8 @@ extension Pixel { case networkProtectionClientFailedToEncodeRegisterKeyRequest case networkProtectionClientFailedToFetchRegisteredServers case networkProtectionClientFailedToParseRegisteredServersResponse + case networkProtectionClientFailedToFetchLocations + case networkProtectionClientFailedToParseLocationsResponse case networkProtectionClientFailedToEncodeRedeemRequest case networkProtectionClientInvalidInviteCode case networkProtectionClientFailedToRedeemInviteCode @@ -355,9 +364,6 @@ extension Pixel { case remoteMessageSecondaryActionClicked case remoteMessageSheet - // MARK: Return user measurement - case returnUser - // MARK: debug pixels case dbCrashDetected @@ -470,10 +476,15 @@ extension Pixel { case bookmarksMigrationCouldNotPrepareDatabaseOnFailedMigration case bookmarksMigrationCouldNotValidateDatabase case bookmarksMigrationCouldNotRemoveOldStore + case bookmarksMigrationCouldNotPrepareMultipleFavoriteFolders case syncFailedToMigrate case syncFailedToLoadAccount case syncFailedToSetupEngine + case syncBookmarksCountLimitExceededDaily + case syncCredentialsCountLimitExceededDaily + case syncBookmarksRequestSizeLimitExceededDaily + case syncCredentialsRequestSizeLimitExceededDaily case syncSentUnauthenticatedRequest case syncMetadataCouldNotLoadDatabase @@ -486,6 +497,7 @@ extension Pixel { case bookmarksCleanupFailed case bookmarksCleanupAttemptedWhileSyncWasEnabled + case favoritesCleanupFailed case credentialsDatabaseCleanupFailed case credentialsCleanupAttemptedWhileSyncWasEnabled @@ -576,7 +588,10 @@ extension Pixel.Event { case .autocompleteSelectedLocal: return "m_au_l" case .autocompleteSelectedRemote: return "m_au_r" - + + case .navigationbarPositionBottom: return "m_seturlbar_bottom" + case .navigationBarPositionTop: return "m_seturlbar_top" + case .feedbackPositive: return "mfbs_positive_submit" case .feedbackNegativePrefix(category: let category): return "mfbs_negative_\(category)" @@ -688,7 +703,8 @@ extension Pixel.Event { case .autofillLoginsSaveLoginModalDisplayed: return "m_autofill_logins_save_login_inline_displayed" case .autofillLoginsSaveLoginModalConfirmed: return "m_autofill_logins_save_login_inline_confirmed" case .autofillLoginsSaveLoginModalDismissed: return "m_autofill_logins_save_login_inline_dismissed" - + case .autofillLoginsSaveLoginModalExcludeSiteConfirmed: return "m_autofill_logins_save_login_exclude_site_confirmed" + case .autofillLoginsSavePasswordModalDisplayed: return "m_autofill_logins_save_password_inline_displayed" case .autofillLoginsSavePasswordModalConfirmed: return "m_autofill_logins_save_password_inline_confirmed" case .autofillLoginsSavePasswordModalDismissed: return "m_autofill_logins_save_password_inline_dismissed" @@ -730,6 +746,9 @@ extension Pixel.Event { case .autofillLoginsSettingsDisabled: return "m_autofill_logins_settings_disabled" case .autofillLoginsSettingsAddNewLoginErrorAttemptedToCreateDuplicate: return "m_autofill_logins_settings_add-new-login_error_attempted-to-create-duplicate" + case .autofillLoginsSettingsResetExcludedDisplayed: return "m_autofill_settings_reset_excluded_displayed" + case .autofillLoginsSettingsResetExcludedConfirmed: return "m_autofill_settings_reset_excluded_confirmed" + case .autofillLoginsSettingsResetExcludedDismissed: return "m_autofill_settings_reset_excluded_dismissed" case .autofillLoginsPasswordGenerationPromptDisplayed: return "m_autofill_logins_password_generation_prompt_displayed" case .autofillLoginsPasswordGenerationPromptConfirmed: return "m_autofill_logins_password_generation_prompt_confirmed" @@ -798,6 +817,9 @@ extension Pixel.Event { case .networkProtectionClientFailedToFetchRegisteredServers: return "m_netp_backend_api_error_failed_to_fetch_registered_servers" case .networkProtectionClientFailedToParseRegisteredServersResponse: return "m_netp_backend_api_error_parsing_device_registration_response_failed" + case .networkProtectionClientFailedToFetchLocations: return "m_netp_backend_api_error_failed_to_fetch_locations" + case .networkProtectionClientFailedToParseLocationsResponse: + return "m_netp_backend_api_error_parsing_locations_response_failed" case .networkProtectionClientFailedToEncodeRedeemRequest: return "m_netp_backend_api_error_encoding_redeem_request_body_failed" case .networkProtectionClientInvalidInviteCode: return "m_netp_backend_api_error_invalid_invite_code" case .networkProtectionClientFailedToRedeemInviteCode: return "m_netp_backend_api_error_failed_to_redeem_invite_code" @@ -945,10 +967,15 @@ extension Pixel.Event { return "m_d_bookmarks_migration_could_not_prepare_database_on_failed_migration" case .bookmarksMigrationCouldNotValidateDatabase: return "m_d_bookmarks_migration_could_not_validate_database" case .bookmarksMigrationCouldNotRemoveOldStore: return "m_d_bookmarks_migration_could_not_remove_old_store" + case .bookmarksMigrationCouldNotPrepareMultipleFavoriteFolders: return "m_d_bookmarks_migration_could_not_prepare_multiple_favorite_folders" case .syncFailedToMigrate: return "m_d_sync_failed_to_migrate" case .syncFailedToLoadAccount: return "m_d_sync_failed_to_load_account" case .syncFailedToSetupEngine: return "m_d_sync_failed_to_setup_engine" + case .syncBookmarksCountLimitExceededDaily: return "m_d_sync_bookmarks_count_limit_exceeded_daily" + case .syncCredentialsCountLimitExceededDaily: return "m_d_sync_credentials_count_limit_exceeded_daily" + case .syncBookmarksRequestSizeLimitExceededDaily: return "m_d_sync_bookmarks_request_size_limit_exceeded_daily" + case .syncCredentialsRequestSizeLimitExceededDaily: return "m_d_sync_credentials_request_size_limit_exceeded_daily" case .syncSentUnauthenticatedRequest: return "m_d_sync_sent_unauthenticated_request" case .syncMetadataCouldNotLoadDatabase: return "m_d_sync_metadata_could_not_load_database" @@ -962,6 +989,7 @@ extension Pixel.Event { case .bookmarksCleanupFailed: return "m_d_bookmarks_cleanup_failed" case .bookmarksCleanupAttemptedWhileSyncWasEnabled: return "m_d_bookmarks_cleanup_attempted_while_sync_was_enabled" + case .favoritesCleanupFailed: return "m_d_favorites_cleanup_failed" case .credentialsDatabaseCleanupFailed: return "m_d_credentials_database_cleanup_failed_2" case .credentialsCleanupAttemptedWhileSyncWasEnabled: return "m_d_credentials_cleanup_attempted_while_sync_was_enabled" @@ -980,7 +1008,6 @@ extension Pixel.Event { case .compilationFailed: return "m_d_compilation_failed" // MARK: - Return user measurement - case .returnUser: return "m_return_user" case .debugReturnUserAddATB: return "m_debug_return_user_add_atb" case .debugReturnUserReadATB: return "m_debug_return_user_read_atb" case .debugReturnUserUpdateATB: return "m_debug_return_user_update_atb" diff --git a/Core/ReturnUserMeasurement.swift b/Core/ReturnUserMeasurement.swift index 0dce183c42..f4a9507a08 100644 --- a/Core/ReturnUserMeasurement.swift +++ b/Core/ReturnUserMeasurement.swift @@ -45,9 +45,6 @@ class KeychainReturnUserMeasurement: ReturnUserMeasurement { } func installCompletedWithATB(_ atb: Atb) { - if let oldATB = readSecureATB() { - sendReturnUserMeasurement(oldATB, atb.version) - } writeSecureATB(atb.version) } @@ -88,34 +85,6 @@ class KeychainReturnUserMeasurement: ReturnUserMeasurement { } - private func readSecureATB() -> String? { - let query: [String: Any] = [ - kSecClass as String: kSecClassGenericPassword, - kSecAttrAccount as String: Self.SecureATBKeychainName, - kSecReturnData as String: kCFBooleanTrue!, - kSecMatchLimit as String: kSecMatchLimitOne - ] - - var dataTypeRef: AnyObject? - let status: OSStatus = SecItemCopyMatching(query as CFDictionary, &dataTypeRef) - if ![errSecSuccess, errSecItemNotFound].contains(status) { - fireDebugPixel(.debugReturnUserReadATB, errorCode: status) - } - - if let data = dataTypeRef as? Data { - return String(data: data, encoding: .utf8) - } - - return nil - } - - private func sendReturnUserMeasurement(_ oldATB: String, _ newATB: String) { - Pixel.fire(pixel: .returnUser, withAdditionalParameters: [ - PixelParameters.returnUserOldATB: oldATB, - PixelParameters.returnUserNewATB: newATB - ]) - } - private func fireDebugPixel(_ event: Pixel.Event, errorCode: OSStatus) { Pixel.fire(pixel: event, withAdditionalParameters: [ PixelParameters.returnUserErrorCode: "\(errorCode)" diff --git a/Core/StatisticsLoader.swift b/Core/StatisticsLoader.swift index e852d70540..8b7a6e481c 100644 --- a/Core/StatisticsLoader.swift +++ b/Core/StatisticsLoader.swift @@ -134,6 +134,7 @@ public class StatisticsLoader { public func storeUpdateVersionIfPresent(_ atb: Atb) { if let updateVersion = atb.updateVersion { statisticsStore.atb = updateVersion + statisticsStore.variant = nil returnUserMeasurement.updateStoredATB(atb) } } diff --git a/Core/SyncBookmarksAdapter.swift b/Core/SyncBookmarksAdapter.swift index 5578e2422c..5752c090dc 100644 --- a/Core/SyncBookmarksAdapter.swift +++ b/Core/SyncBookmarksAdapter.swift @@ -26,14 +26,38 @@ import Persistence import SyncDataProviders import WidgetKit +public protocol FavoritesDisplayModeStoring: AnyObject { + var favoritesDisplayMode: FavoritesDisplayMode { get set } +} + public final class SyncBookmarksAdapter { public private(set) var provider: BookmarksProvider? public let databaseCleaner: BookmarkDatabaseCleaner public let syncDidCompletePublisher: AnyPublisher public let widgetRefreshCancellable: AnyCancellable + public static let syncBookmarksPausedStateChanged = Notification.Name("com.duckduckgo.app.SyncPausedStateChanged") + public static let bookmarksSyncLimitReached = Notification.Name("com.duckduckgo.app.SyncBookmarksLimitReached") + + public var shouldResetBookmarksSyncTimestamp: Bool = false { + willSet { + assert(provider == nil, "Setting this value has no effect after provider has been instantiated") + } + } - public init(database: CoreDataDatabase) { + @UserDefaultsWrapper(key: .syncBookmarksPaused, defaultValue: false) + static public var isSyncBookmarksPaused: Bool { + didSet { + NotificationCenter.default.post(name: syncBookmarksPausedStateChanged, object: nil) + } + } + + @UserDefaultsWrapper(key: .syncBookmarksPausedErrorDisplayed, defaultValue: false) + static private var didShowBookmarksSyncPausedError: Bool + + public init(database: CoreDataDatabase, favoritesDisplayModeStorage: FavoritesDisplayModeStoring) { + self.database = database + self.favoritesDisplayModeStorage = favoritesDisplayModeStorage syncDidCompletePublisher = syncDidCompleteSubject.eraseToAnyPublisher() databaseCleaner = BookmarkDatabaseCleaner( bookmarkDatabase: database, @@ -49,6 +73,7 @@ public final class SyncBookmarksAdapter { databaseCleaner.cleanUpDatabaseNow() if shouldEnable { databaseCleaner.scheduleRegularCleaning() + handleFavoritesAfterDisablingSync() } else { databaseCleaner.cancelCleaningSchedule() } @@ -64,14 +89,33 @@ public final class SyncBookmarksAdapter { metadataStore: metadataStore, syncDidUpdateData: { [syncDidCompleteSubject] in syncDidCompleteSubject.send() + Self.isSyncBookmarksPaused = false + Self.didShowBookmarksSyncPausedError = false } ) + if shouldResetBookmarksSyncTimestamp { + provider.lastSyncTimestamp = nil + } syncErrorCancellable = provider.syncErrorPublisher .sink { error in switch error { case let syncError as SyncError: Pixel.fire(pixel: .syncBookmarksFailed, error: syncError) + switch syncError { + case .unexpectedStatusCode(409): + // If bookmarks count limit has been exceeded + Self.isSyncBookmarksPaused = true + DailyPixel.fire(pixel: .syncBookmarksCountLimitExceededDaily) + Self.notifyBookmarksSyncLimitReached() + case .unexpectedStatusCode(413): + // If bookmarks request size limit has been exceeded + Self.isSyncBookmarksPaused = true + DailyPixel.fire(pixel: .syncBookmarksRequestSizeLimitExceededDaily) + Self.notifyBookmarksSyncLimitReached() + default: + break + } default: let nsError = error as NSError if nsError.domain != NSURLErrorDomain { @@ -86,6 +130,36 @@ public final class SyncBookmarksAdapter { self.provider = provider } + static private func notifyBookmarksSyncLimitReached() { + if !Self.didShowBookmarksSyncPausedError { + NotificationCenter.default.post(name: Self.bookmarksSyncLimitReached, object: nil) + Self.didShowBookmarksSyncPausedError = true + } + } + + private func handleFavoritesAfterDisablingSync() { + let context = database.makeContext(concurrencyType: .privateQueueConcurrencyType) + + context.performAndWait { + do { + if favoritesDisplayModeStorage.favoritesDisplayMode.isDisplayUnified { + BookmarkUtils.copyFavorites(from: .unified, to: .mobile, clearingNonNativeFavoritesFolder: .desktop, in: context) + favoritesDisplayModeStorage.favoritesDisplayMode = .displayNative(.mobile) + } else { + BookmarkUtils.copyFavorites(from: .mobile, to: .unified, clearingNonNativeFavoritesFolder: .desktop, in: context) + } + try context.save() + } catch { + let nsError = error as NSError + let processedErrors = CoreDataErrorsParser.parse(error: nsError) + let params = processedErrors.errorPixelParameters + Pixel.fire(pixel: .favoritesCleanupFailed, error: error, withAdditionalParameters: params) + } + } + } + private var syncDidCompleteSubject = PassthroughSubject() private var syncErrorCancellable: AnyCancellable? + private let database: CoreDataDatabase + private let favoritesDisplayModeStorage: FavoritesDisplayModeStoring } diff --git a/Core/SyncCredentialsAdapter.swift b/Core/SyncCredentialsAdapter.swift index e4b96e01ef..e5c2101954 100644 --- a/Core/SyncCredentialsAdapter.swift +++ b/Core/SyncCredentialsAdapter.swift @@ -30,6 +30,17 @@ public final class SyncCredentialsAdapter { public private(set) var provider: CredentialsProvider? public let databaseCleaner: CredentialsDatabaseCleaner public let syncDidCompletePublisher: AnyPublisher + public static let syncCredentialsPausedStateChanged = SyncBookmarksAdapter.syncBookmarksPausedStateChanged + public static let credentialsSyncLimitReached = Notification.Name("com.duckduckgo.app.SyncCredentialsLimitReached") + + @UserDefaultsWrapper(key: .syncCredentialsPaused, defaultValue: false) + static public var isSyncCredentialsPaused: Bool { + didSet { + NotificationCenter.default.post(name: syncCredentialsPausedStateChanged, object: nil) + } + } + @UserDefaultsWrapper(key: .syncCredentialsPausedErrorDisplayed, defaultValue: false) + static private var didShowCredentialsSyncPausedError: Bool public init(secureVaultFactory: AutofillVaultFactory = AutofillSecureVaultFactory, secureVaultErrorReporter: SecureVaultErrorReporting) { syncDidCompletePublisher = syncDidCompleteSubject.eraseToAnyPublisher() @@ -63,6 +74,8 @@ public final class SyncCredentialsAdapter { metadataStore: metadataStore, syncDidUpdateData: { [weak self] in self?.syncDidCompleteSubject.send() + Self.isSyncCredentialsPaused = false + Self.didShowCredentialsSyncPausedError = false } ) @@ -71,6 +84,21 @@ public final class SyncCredentialsAdapter { switch error { case let syncError as SyncError: Pixel.fire(pixel: .syncCredentialsFailed, error: syncError) + + switch syncError { + case .unexpectedStatusCode(409): + // If credentials count limit has been exceeded + Self.isSyncCredentialsPaused = true + DailyPixel.fire(pixel: .syncCredentialsCountLimitExceededDaily) + Self.notifyCredentialsSyncLimitReached() + case .unexpectedStatusCode(413): + // If credentials request size limit has been exceeded + Self.isSyncCredentialsPaused = true + DailyPixel.fire(pixel: .syncCredentialsRequestSizeLimitExceededDaily) + Self.notifyCredentialsSyncLimitReached() + default: + break + } default: let nsError = error as NSError if nsError.domain != NSURLErrorDomain { @@ -91,6 +119,13 @@ public final class SyncCredentialsAdapter { } } + static private func notifyCredentialsSyncLimitReached() { + if !Self.didShowCredentialsSyncPausedError { + NotificationCenter.default.post(name: Self.credentialsSyncLimitReached, object: nil) + Self.didShowCredentialsSyncPausedError = true + } + } + private var syncDidCompleteSubject = PassthroughSubject() private var syncErrorCancellable: AnyCancellable? private let secureVaultErrorReporter: SecureVaultErrorReporting diff --git a/Core/SyncDataProviders.swift b/Core/SyncDataProviders.swift index e705889a54..a70b4acd4f 100644 --- a/Core/SyncDataProviders.swift +++ b/Core/SyncDataProviders.swift @@ -17,6 +17,7 @@ // limitations under the License. // +import Bookmarks import BrowserServicesKit import Combine import Common @@ -84,14 +85,16 @@ public class SyncDataProviders: DataProvidersSource { public init( bookmarksDatabase: CoreDataDatabase, secureVaultFactory: AutofillVaultFactory = AutofillSecureVaultFactory, - secureVaultErrorReporter: SecureVaultErrorReporting + secureVaultErrorReporter: SecureVaultErrorReporting, + settingHandlers: [SettingSyncHandler], + favoritesDisplayModeStorage: FavoritesDisplayModeStoring ) { self.bookmarksDatabase = bookmarksDatabase self.secureVaultFactory = secureVaultFactory self.secureVaultErrorReporter = secureVaultErrorReporter - bookmarksAdapter = SyncBookmarksAdapter(database: bookmarksDatabase) + bookmarksAdapter = SyncBookmarksAdapter(database: bookmarksDatabase, favoritesDisplayModeStorage: favoritesDisplayModeStorage) credentialsAdapter = SyncCredentialsAdapter(secureVaultFactory: secureVaultFactory, secureVaultErrorReporter: secureVaultErrorReporter) - settingsAdapter = SyncSettingsAdapter() + settingsAdapter = SyncSettingsAdapter(settingHandlers: settingHandlers) } private func initializeMetadataDatabaseIfNeeded() { diff --git a/Core/SyncSettingsAdapter.swift b/Core/SyncSettingsAdapter.swift index b6b5fd80ef..9bfe132bb4 100644 --- a/Core/SyncSettingsAdapter.swift +++ b/Core/SyncSettingsAdapter.swift @@ -30,7 +30,8 @@ public final class SyncSettingsAdapter { public private(set) var emailManager: EmailManager? public let syncDidCompletePublisher: AnyPublisher - public init() { + public init(settingHandlers: [SettingSyncHandler]) { + self.settingHandlers = settingHandlers syncDidCompletePublisher = syncDidCompleteSubject.eraseToAnyPublisher() } @@ -41,12 +42,14 @@ public final class SyncSettingsAdapter { guard provider == nil else { return } + let emailManager = EmailManager() + let emailProtectionSyncHandler = EmailProtectionSyncHandler(emailManager: emailManager) let provider = SettingsProvider( metadataDatabase: metadataDatabase, metadataStore: metadataStore, - emailManager: emailManager, + settingsHandlers: settingHandlers + [emailProtectionSyncHandler], syncDidUpdateData: { [weak self] in self?.syncDidCompleteSubject.send() } @@ -77,6 +80,7 @@ public final class SyncSettingsAdapter { self.emailManager = emailManager } + private let settingHandlers: [SettingSyncHandler] private var syncDidCompleteSubject = PassthroughSubject() private var syncErrorCancellable: AnyCancellable? } diff --git a/Core/UserAgentManager.swift b/Core/UserAgentManager.swift index 0e03659e2e..6434f24758 100644 --- a/Core/UserAgentManager.swift +++ b/Core/UserAgentManager.swift @@ -212,6 +212,8 @@ struct UserAgent { if DefaultVariantManager().isSupported(feature: .fixedUserAgent) { return ddgFixedLogic(forUrl: url, isDesktop: isDesktop, privacyConfig: privacyConfig) + } else if DefaultVariantManager().isSupported(feature: .closestUserAgent) { + return closestLogic(forUrl: url, isDesktop: isDesktop, privacyConfig: privacyConfig) } switch defaultPolicy(forConfig: privacyConfig) { diff --git a/Core/UserDefaults+NetworkProtection.swift b/Core/UserDefaults+NetworkProtection.swift new file mode 100644 index 0000000000..e137aa51c2 --- /dev/null +++ b/Core/UserDefaults+NetworkProtection.swift @@ -0,0 +1,40 @@ +// +// UserDefaults+NetworkProtection.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if NETWORK_PROTECTION + +import Foundation + +public extension UserDefaults { + static var networkProtectionGroupDefaults: UserDefaults { + let suiteName = "\(Global.groupIdPrefix).netp" + guard let defaults = UserDefaults(suiteName: suiteName) else { + fatalError("Failed to create netP UserDefaults") + } + return defaults + } +} + +public enum NetworkProtectionUserDefaultKeys { + + public static let lastSelectedServer = "com.duckduckgo.network-protection.last-selected-server" + +} + +#endif diff --git a/Core/UserDefaultsPropertyWrapper.swift b/Core/UserDefaultsPropertyWrapper.swift index 0d657b9533..d675ee1bfa 100644 --- a/Core/UserDefaultsPropertyWrapper.swift +++ b/Core/UserDefaultsPropertyWrapper.swift @@ -69,7 +69,7 @@ public struct UserDefaultsWrapper { case downloadedTrackerDataSetCount = "com.duckduckgo.app.downloadedTrackerDataSetCount" case downloadedPrivacyConfigurationCount = "com.duckduckgo.app.downloadedPrivacyConfigurationCount" case textSize = "com.duckduckgo.ios.textSize" - + case emailWaitlistShouldReceiveNotifications = "com.duckduckgo.ios.showWaitlistNotification" case unseenDownloadsAvailable = "com.duckduckgo.app.unseenDownloadsAvailable" @@ -81,7 +81,7 @@ public struct UserDefaultsWrapper { case autofillCredentialsSavePromptShowAtLeastOnce = "com.duckduckgo.ios.autofillCredentialsSavePromptShowAtLeastOnce" case autofillCredentialsHasBeenEnabledAutomaticallyIfNecessary = "com.duckduckgo.ios.autofillCredentialsHasBeenEnabledAutomaticallyIfNecessary" - + case featureFlaggingDidVerifyInternalUser = "com.duckduckgo.app.featureFlaggingDidVerifyInternalUser" case voiceSearchEnabled = "com.duckduckgo.app.voiceSearchEnabled" @@ -97,8 +97,16 @@ public struct UserDefaultsWrapper { case defaultBrowserUsageLastSeen = "com.duckduckgo.ios.default-browser-usage-last-seen" case syncEnvironment = "com.duckduckgo.ios.sync-environment" + case syncBookmarksPaused = "com.duckduckgo.ios.sync-bookmarksPaused" + case syncCredentialsPaused = "com.duckduckgo.ios.sync-credentialsPaused" + case syncBookmarksPausedErrorDisplayed = "com.duckduckgo.ios.sync-bookmarksPausedErrorDisplayed" + case syncCredentialsPausedErrorDisplayed = "com.duckduckgo.ios.sync-credentialsPausedErrorDisplayed" case networkProtectionDebugOptionAlwaysOnDisabled = "com.duckduckgo.network-protection.always-on.disabled" + case networkProtectionWaitlistTermsAndConditionsAccepted = "com.duckduckgo.ios.vpn.terms-and-conditions-accepted" + + case addressBarPosition = "com.duckduckgo.ios.addressbarposition" + case showFullSiteAddress = "com.duckduckgo.ios.showfullsiteaddress" } private let key: Key diff --git a/Core/ios-config.json b/Core/ios-config.json index b9847b8298..3283af4e90 100644 --- a/Core/ios-config.json +++ b/Core/ios-config.json @@ -1,6 +1,6 @@ { "readme": "https://github.com/duckduckgo/privacy-configuration", - "version": 1695828991975, + "version": 1700585688784, "features": { "adClickAttribution": { "readme": "https://help.duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/#3rd-party-tracker-loading-protection", @@ -63,32 +63,28 @@ "ampLinks": { "exceptions": [ { - "domain": "freecodecamp.org", - "reason": "Clicking 'get started' reloads the page and does not progress to the login page." + "domain": "freecodecamp.org" }, { - "domain": "www.audiosciencereview.com", - "reason": "Pages on the site end up in redirect loops in Firefox." + "domain": "www.audiosciencereview.com" }, { - "domain": "golf.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/812" + "domain": "golf.com" }, { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" + "domain": "thehustle.co" }, { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "earth.google.com" }, { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" + "domain": "iscorp.com" }, { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "marvel.com" + }, + { + "domain": "sundancecatalog.com" } ], "settings": { @@ -100,7 +96,6 @@ ], "keywords": [ "=amp", - "amp=", "&", "amp&", "/amp", @@ -115,185 +110,152 @@ ] }, "state": "enabled", - "hash": "87198c8b0bc8c044f0773599845fa371" + "hash": "5bfae22c5e0cb13c0a25ce80531827ff" + }, + "androidBrowserConfig": { + "exceptions": [], + "state": "disabled", + "hash": "728493ef7a1488e4781656d3f9db84aa" }, "autoconsent": { "exceptions": [ { - "domain": "allocine.fr", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1241" + "domain": "allocine.fr" + }, + { + "domain": "bild.de" }, { - "domain": "bild.de", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/326" + "domain": "derstandard.de" }, { - "domain": "derstandard.de", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/326" + "domain": "duden.de" }, { - "domain": "duden.de", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/326" + "domain": "ksta.de" }, { - "domain": "ksta.de", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/326" + "domain": "motherdenim.com" }, { - "domain": "motherdenim.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/326" + "domain": "nationalgrid.co.uk" }, { - "domain": "nationalgrid.co.uk", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/326" + "domain": "t-online.de" }, { - "domain": "t-online.de", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/326" + "domain": "tomsguide.com" }, { - "domain": "tomsguide.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/326" + "domain": "welt.de" }, { - "domain": "welt.de", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/326" + "domain": "zeitraum-moebel.de" }, { - "domain": "zeitraum-moebel.de", - "reason": "After the cookie popup is managed and disappears, a semi-transparent overlay remains on the page which prevents further interaction with the site." + "domain": "mathebibel.de" }, { - "domain": "mathebibel.de", - "reason": "Page renders but one cannot scroll (and no CMP is shown) for a few seconds." + "domain": "focus.de" }, { - "domain": "focus.de", - "reason": "Page renders but one cannot scroll (and no CMP is shown) for a few seconds." + "domain": "computerbild.de" }, { - "domain": "computerbild.de", - "reason": "Page renders but one cannot scroll (and no CMP is shown) for a few seconds." + "domain": "techtarget.com" }, { - "domain": "techtarget.com", - "reason": "Page renders but one cannot scroll (and no CMP is shown) for a few seconds." + "domain": "n-tv.de" }, { - "domain": "n-tv.de", - "reason": "Page renders but one cannot scroll (and no CMP is shown) for a few seconds." + "domain": "spiegel.de" }, { - "domain": "spiegel.de", - "reason": "CMP gets incorrectly handled, gets stuck in preferences dialogue." + "domain": "derstandard.at" }, { - "domain": "derstandard.at", - "reason": "CMP gets incorrectly handled / detected." + "domain": "concursolutions.com" }, { - "domain": "concursolutions.com", - "reason": "Page renders blank for several seconds before cookie management can complete." + "domain": "asus.com" }, { - "domain": "asus.com", - "reason": "Opt out is broken on the US version of the site" + "domain": "swm.de" }, { - "domain": "swm.de", - "reason": "infinite reload" + "domain": "heise.de" }, { - "domain": "heise.de", - "reason": "infinite reload" + "domain": "voici.fr" }, { - "domain": "voici.fr", - "reason": "https://github.com/duckduckgo/autoconsent/issues/66" + "domain": "tfl.gov.uk" }, { - "domain": "tfl.gov.uk", - "reason": "https://github.com/duckduckgo/autoconsent/issues/68" + "domain": "corporatefinanceinstitute.com" }, { - "domain": "corporatefinanceinstitute.com", - "reason": "CMP gets incorrectly handled / repeatedly opens tabs" + "domain": "kicker.de" }, { - "domain": "kicker.de", - "reason": "https://github.com/duckduckgo/autoconsent/issues/66" + "domain": "epojisteni.cz" }, { - "domain": "epojisteni.cz", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/326" + "domain": "nhm.ac.uk" }, { - "domain": "nhm.ac.uk", - "reason": "https://github.com/duckduckgo/autoconsent/issues/113" + "domain": "virginmoney.com" }, { - "domain": "virginmoney.com", - "reason": "https://github.com/duckduckgo/autoconsent/issues/127" + "domain": "shellenergy.co.uk" }, { - "domain": "shellenergy.co.uk", - "reason": "https://github.com/duckduckgo/autoconsent/issues/127" + "domain": "castorama.pl" }, { - "domain": "castorama.pl", - "reason": "https://github.com/duckduckgo/autoconsent/issues/128" + "domain": "bugatti-fashion.com" }, { - "domain": "bugatti-fashion.com", - "reason": "https://github.com/duckduckgo/autoconsent/issues/129" + "domain": "hl.co.uk" }, { - "domain": "hl.co.uk", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/326" + "domain": "jesus.de" }, { - "domain": "jesus.de", - "reason": "https://github.com/duckduckgo/autoconsent/issues/130" + "domain": "linux-praxis.de" }, { - "domain": "linux-praxis.de", - "reason": "https://github.com/duckduckgo/autoconsent/issues/130" + "domain": "gfds.de" }, { - "domain": "gfds.de", - "reason": "https://github.com/duckduckgo/autoconsent/issues/130" + "domain": "motorsport.com" }, { - "domain": "motorsport.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1250" + "domain": "paypal.com" }, { - "domain": "paypal.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1303" + "domain": "elmundotoday.com" }, { - "domain": "elmundotoday.com", - "reason": "https://github.com/duckduckgo/autoconsent/issues/254" + "domain": "yum.com" }, { - "domain": "yum.com", - "reason": "https://github.com/duckduckgo/autoconsent/issues/259" + "domain": "capital.fr" }, { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" + "domain": "metro.co.uk" }, { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "earth.google.com" }, { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" + "domain": "iscorp.com" }, { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "marvel.com" + }, + { + "domain": "sundancecatalog.com" } ], "settings": { @@ -302,77 +264,12 @@ ] }, "state": "enabled", - "hash": "6b201ccc9308a1e9b574b8a7da5a9b52" + "hash": "9ab9e1acdb6a8617c77109acc1e3943c" }, "autofill": { "exceptions": [ { - "domain": "containerstore.com", - "reason": "Generally poor UX - no username saved on signup, prompts for Duck address on 'forgot password'." - }, - { - "domain": "roll20.net", - "reason": "Performance issue for too many fields." - }, - { - "domain": "amazon.ca", - "reason": "Performance issue, infinite loop." - }, - { - "domain": "amazon.cn", - "reason": "Performance issue, infinite loop." - }, - { - "domain": "amazon.co.jp", - "reason": "Performance issue, infinite loop." - }, - { - "domain": "amazon.com", - "reason": "Performance issue, infinite loop." - }, - { - "domain": "amazon.com.au", - "reason": "Performance issue, infinite loop." - }, - { - "domain": "amazon.com.mx", - "reason": "Performance issue, infinite loop." - }, - { - "domain": "amazon.co.uk", - "reason": "Performance issue, infinite loop." - }, - { - "domain": "amazon.de", - "reason": "Performance issue, infinite loop." - }, - { - "domain": "amazon.es", - "reason": "Performance issue, infinite loop." - }, - { - "domain": "amazon.eu", - "reason": "Performance issue, infinite loop." - }, - { - "domain": "amazon.fr", - "reason": "Performance issue, infinite loop." - }, - { - "domain": "amazon.in", - "reason": "Performance issue, infinite loop." - }, - { - "domain": "amazon.it", - "reason": "Performance issue, infinite loop." - }, - { - "domain": "amazon.nl", - "reason": "Performance issue, infinite loop." - }, - { - "domain": "amazon.sa", - "reason": "Performance issue, infinite loop." + "domain": "roll20.net" } ], "state": "enabled", @@ -396,823 +293,640 @@ "autofillPasswordGeneration": { "state": "enabled", "minSupportedVersion": "7.75.0" + }, + "onByDefault": { + "state": "enabled", + "minSupportedVersion": "7.93.0", + "rollout": { + "steps": [ + { + "percent": 1 + }, + { + "percent": 10 + }, + { + "percent": 100 + } + ] + } } }, - "hash": "c427670d1be007f3bd4af4c0e83addfb" + "hash": "bd604dcd1f7bb584185f0c1cb94a5771" }, "clickToLoad": { "exceptions": [ { - "domain": "beatsense.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/950" + "domain": "beatsense.com" }, { - "domain": "discogs.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/950" + "domain": "discogs.com" }, { - "domain": "duckduckgo.com", - "reason": "Warnings are already displayed before embedded YouTube videos are loaded." + "domain": "duckduckgo.com" }, { - "domain": "khanacademy.org", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/945" + "domain": "khanacademy.org" }, { - "domain": "last.fm", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/950" + "domain": "last.fm" }, { - "domain": "turntable.fm", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/950" + "domain": "turntable.fm" }, { - "domain": "www.google.ad", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.ad" }, { - "domain": "www.google.ae", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.ae" }, { - "domain": "www.google.al", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.al" }, { - "domain": "www.google.am", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.am" }, { - "domain": "www.google.as", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.as" }, { - "domain": "www.google.at", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.at" }, { - "domain": "www.google.az", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.az" }, { - "domain": "www.google.ba", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.ba" }, { - "domain": "www.google.be", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.be" }, { - "domain": "www.google.bf", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.bf" }, { - "domain": "www.google.bg", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.bg" }, { - "domain": "www.google.bi", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.bi" }, { - "domain": "www.google.bj", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.bj" }, { - "domain": "www.google.bs", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.bs" }, { - "domain": "www.google.bt", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.bt" }, { - "domain": "www.google.by", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.by" }, { - "domain": "www.google.ca", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.ca" }, { - "domain": "www.google.cat", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.cat" }, { - "domain": "www.google.cd", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.cd" }, { - "domain": "www.google.cf", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.cf" }, { - "domain": "www.google.cg", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.cg" }, { - "domain": "www.google.ch", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.ch" }, { - "domain": "www.google.ci", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.ci" }, { - "domain": "www.google.cl", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.cl" }, { - "domain": "www.google.cm", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.cm" }, { - "domain": "www.google.cn", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.cn" }, { - "domain": "www.google.co.ao", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.co.ao" }, { - "domain": "www.google.co.bw", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.co.bw" }, { - "domain": "www.google.co.ck", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.co.ck" }, { - "domain": "www.google.co.cr", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.co.cr" }, { - "domain": "www.google.co.id", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.co.id" }, { - "domain": "www.google.co.il", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.co.il" }, { - "domain": "www.google.co.in", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.co.in" }, { - "domain": "www.google.co.jp", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.co.jp" }, { - "domain": "www.google.co.ke", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.co.ke" }, { - "domain": "www.google.co.kr", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.co.kr" }, { - "domain": "www.google.co.ls", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.co.ls" }, { - "domain": "www.google.co.ma", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.co.ma" }, { - "domain": "www.google.co.mz", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.co.mz" }, { - "domain": "www.google.co.nz", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.co.nz" }, { - "domain": "www.google.co.th", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.co.th" }, { - "domain": "www.google.co.tz", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.co.tz" }, { - "domain": "www.google.co.ug", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.co.ug" }, { - "domain": "www.google.co.uk", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.co.uk" }, { - "domain": "www.google.co.uz", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.co.uz" }, { - "domain": "www.google.co.ve", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.co.ve" }, { - "domain": "www.google.co.vi", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.co.vi" }, { - "domain": "www.google.co.za", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.co.za" }, { - "domain": "www.google.co.zm", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.co.zm" }, { - "domain": "www.google.co.zw", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.co.zw" }, { - "domain": "www.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com" }, { - "domain": "www.google.com.af", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.af" }, { - "domain": "www.google.com.ag", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.ag" }, { - "domain": "www.google.com.ai", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.ai" }, { - "domain": "www.google.com.ar", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.ar" }, { - "domain": "www.google.com.au", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.au" }, { - "domain": "www.google.com.bd", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.bd" }, { - "domain": "www.google.com.bh", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.bh" }, { - "domain": "www.google.com.bn", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.bn" }, { - "domain": "www.google.com.bo", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.bo" }, { - "domain": "www.google.com.br", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.br" }, { - "domain": "www.google.com.bz", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.bz" }, { - "domain": "www.google.com.co", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.co" }, { - "domain": "www.google.com.cu", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.cu" }, { - "domain": "www.google.com.cy", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.cy" }, { - "domain": "www.google.com.do", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.do" }, { - "domain": "www.google.com.ec", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.ec" }, { - "domain": "www.google.com.eg", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.eg" }, { - "domain": "www.google.com.et", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.et" }, { - "domain": "www.google.com.fj", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.fj" }, { - "domain": "www.google.com.gh", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.gh" }, { - "domain": "www.google.com.gi", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.gi" }, { - "domain": "www.google.com.gt", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.gt" }, { - "domain": "www.google.com.hk", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.hk" }, { - "domain": "www.google.com.jm", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.jm" }, { - "domain": "www.google.com.kh", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.kh" }, { - "domain": "www.google.com.kw", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.kw" }, { - "domain": "www.google.com.lb", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.lb" }, { - "domain": "www.google.com.ly", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.ly" }, { - "domain": "www.google.com.mm", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.mm" }, { - "domain": "www.google.com.mt", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.mt" }, { - "domain": "www.google.com.mx", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.mx" }, { - "domain": "www.google.com.my", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.my" }, { - "domain": "www.google.com.na", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.na" }, { - "domain": "www.google.com.ng", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.ng" }, { - "domain": "www.google.com.ni", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.ni" }, { - "domain": "www.google.com.np", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.np" }, { - "domain": "www.google.com.om", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.om" }, { - "domain": "www.google.com.pa", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.pa" }, { - "domain": "www.google.com.pe", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.pe" }, { - "domain": "www.google.com.pg", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.pg" }, { - "domain": "www.google.com.ph", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.ph" }, { - "domain": "www.google.com.pk", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.pk" }, { - "domain": "www.google.com.pr", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.pr" }, { - "domain": "www.google.com.py", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.py" }, { - "domain": "www.google.com.qa", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.qa" }, { - "domain": "www.google.com.sa", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.sa" }, { - "domain": "www.google.com.sb", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.sb" }, { - "domain": "www.google.com.sg", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.sg" }, { - "domain": "www.google.com.sl", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.sl" }, { - "domain": "www.google.com.sv", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.sv" }, { - "domain": "www.google.com.tj", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.tj" }, { - "domain": "www.google.com.tr", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.tr" }, { - "domain": "www.google.com.tw", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.tw" }, { - "domain": "www.google.com.ua", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.ua" }, { - "domain": "www.google.com.uy", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.uy" }, { - "domain": "www.google.com.vc", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.vc" }, { - "domain": "www.google.com.vn", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.com.vn" }, { - "domain": "www.google.cv", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.cv" }, { - "domain": "www.google.cz", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.cz" }, { - "domain": "www.google.de", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.de" }, { - "domain": "www.google.dj", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.dj" }, { - "domain": "www.google.dk", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.dk" }, { - "domain": "www.google.dm", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.dm" }, { - "domain": "www.google.dz", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.dz" }, { - "domain": "www.google.ee", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.ee" }, { - "domain": "www.google.es", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.es" }, { - "domain": "www.google.fi", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.fi" }, { - "domain": "www.google.fm", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.fm" }, { - "domain": "www.google.fr", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.fr" }, { - "domain": "www.google.ga", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.ga" }, { - "domain": "www.google.ge", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.ge" }, { - "domain": "www.google.gg", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.gg" }, { - "domain": "www.google.gl", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.gl" }, { - "domain": "www.google.gm", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.gm" }, { - "domain": "www.google.gr", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.gr" }, { - "domain": "www.google.gy", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.gy" }, { - "domain": "www.google.hn", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.hn" }, { - "domain": "www.google.hr", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.hr" }, { - "domain": "www.google.ht", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.ht" }, { - "domain": "www.google.hu", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.hu" }, { - "domain": "www.google.ie", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.ie" }, { - "domain": "www.google.im", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.im" }, { - "domain": "www.google.iq", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.iq" }, { - "domain": "www.google.is", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.is" }, { - "domain": "www.google.it", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.it" }, { - "domain": "www.google.je", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.je" }, { - "domain": "www.google.jo", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.jo" }, { - "domain": "www.google.kg", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.kg" }, { - "domain": "www.google.ki", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.ki" }, { - "domain": "www.google.kz", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.kz" }, { - "domain": "www.google.la", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.la" }, { - "domain": "www.google.li", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.li" }, { - "domain": "www.google.lk", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.lk" }, { - "domain": "www.google.lt", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.lt" }, { - "domain": "www.google.lu", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.lu" }, { - "domain": "www.google.lv", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.lv" }, { - "domain": "www.google.md", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.md" }, { - "domain": "www.google.me", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.me" }, { - "domain": "www.google.mg", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.mg" }, { - "domain": "www.google.mk", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.mk" }, { - "domain": "www.google.ml", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.ml" }, { - "domain": "www.google.mn", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.mn" }, { - "domain": "www.google.ms", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.ms" }, { - "domain": "www.google.mu", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.mu" }, { - "domain": "www.google.mv", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.mv" }, { - "domain": "www.google.mw", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.mw" }, { - "domain": "www.google.ne", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.ne" }, { - "domain": "www.google.nl", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.nl" }, { - "domain": "www.google.no", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.no" }, { - "domain": "www.google.nr", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.nr" }, { - "domain": "www.google.nu", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.nu" }, { - "domain": "www.google.pl", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.pl" }, { - "domain": "www.google.pn", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.pn" }, { - "domain": "www.google.ps", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.ps" }, { - "domain": "www.google.pt", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.pt" }, { - "domain": "www.google.ro", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.ro" }, { - "domain": "www.google.rs", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.rs" }, { - "domain": "www.google.ru", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.ru" }, { - "domain": "www.google.rw", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.rw" }, { - "domain": "www.google.sc", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.sc" }, { - "domain": "www.google.se", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.se" }, { - "domain": "www.google.sh", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.sh" }, { - "domain": "www.google.si", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.si" }, { - "domain": "www.google.sk", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.sk" }, { - "domain": "www.google.sm", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.sm" }, { - "domain": "www.google.sn", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.sn" }, { - "domain": "www.google.so", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.so" }, { - "domain": "www.google.sr", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.sr" }, { - "domain": "www.google.st", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.st" }, { - "domain": "www.google.td", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.td" }, { - "domain": "www.google.tg", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.tg" }, { - "domain": "www.google.tl", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.tl" }, { - "domain": "www.google.tm", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.tm" }, { - "domain": "www.google.tn", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.tn" }, { - "domain": "www.google.to", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.to" }, { - "domain": "www.google.tt", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.tt" }, { - "domain": "www.google.vg", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.vg" }, { - "domain": "www.google.vu", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.vu" }, { - "domain": "www.google.ws", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/911" + "domain": "www.google.ws" }, { - "domain": "freenom.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1109" + "domain": "freenom.com" }, { - "domain": "isra.org", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1245" + "domain": "isra.org" }, { - "domain": "iamexpat.nl", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1247" + "domain": "iamexpat.nl" }, { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" + "domain": "pocketbook.digital" }, { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "earth.google.com" }, { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" + "domain": "iscorp.com" }, { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "marvel.com" + }, + { + "domain": "sundancecatalog.com" } ], "settings": { @@ -1230,25 +944,21 @@ } }, "state": "disabled", - "hash": "396f535f481c52cc31108ac8a0f7e53a" + "hash": "36e8971fa9bb204b78a5929a14a108dd" }, "clickToPlay": { "exceptions": [ { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" + "domain": "earth.google.com" }, { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "iscorp.com" }, { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" + "domain": "marvel.com" }, { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "sundancecatalog.com" } ], "settings": { @@ -1261,37 +971,40 @@ } }, "state": "disabled", - "hash": "849beddfafbff071cd4a08697c265a38" + "hash": "4390af06f967ef97a827aeab0ac0d1ca" }, "contentBlocking": { "state": "enabled", "exceptions": [ { - "domain": "strava.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/748" + "domain": "strava.com" + }, + { + "domain": "welt.de" }, { - "domain": "welt.de", - "reason": "Video pauses at about 13-15 seconds in. Playing the video again results in a single frame rendering without progressing to the next frame." + "domain": "optout.aboutads.info" }, { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" + "domain": "optout.networkadvertising.org" }, { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "ads.google.com" }, { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" + "domain": "earth.google.com" }, { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "iscorp.com" + }, + { + "domain": "marvel.com" + }, + { + "domain": "sundancecatalog.com" } ], - "hash": "acdaea57d6585b8b4c15750545c477ff" + "hash": "71ad0f38f9ce2bbacae5ff7db05511ec" }, "cookie": { "settings": { @@ -1322,151 +1035,265 @@ }, "exceptions": [ { - "domain": "nespresso.com", - "reason": "Clicking 'Continue' after filling out details for account creation yields an error." + "domain": "nespresso.com" + }, + { + "domain": "optout.aboutads.info" + }, + { + "domain": "optout.networkadvertising.org" }, { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" + "domain": "earth.google.com" }, { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "iscorp.com" }, { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" + "domain": "marvel.com" }, { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "sundancecatalog.com" } ], "state": "disabled", - "hash": "d391a768cdcd83fe39d2229cfca7239c" + "hash": "e41dbd0af3841636937030d91bc305b1" }, "customUserAgent": { "settings": { + "defaultPolicy": "ddgFixed", + "ddgFixedSites": [], + "ddgDefaultSites": [ + { + "domain": "duckduckgo.com", + "reason": "Internal exclusion to roll out experiment" + } + ], + "closestUserAgent": { + "versions": [ + "392", + "390", + "387", + "385", + "384", + "381", + "380", + "378", + "375", + "373", + "372", + "369", + "368", + "366", + "363", + "362", + "359", + "357", + "355", + "354", + "352", + "350", + "347", + "346", + "344", + "342", + "339", + "337", + "335", + "333", + "331", + "329", + "328", + "326", + "323", + "321", + "320", + "318", + "315", + "313", + "312", + "309", + "308", + "306", + "304", + "302", + "300", + "298", + "295", + "293", + "291", + "289", + "287", + "285", + "284", + "281", + "279", + "278", + "276", + "274", + "271", + "269", + "268", + "266", + "263", + "262", + "260", + "257", + "256", + "254", + "251", + "249", + "248", + "245", + "244", + "241", + "239", + "238", + "236", + "234", + "232", + "230", + "228", + "226", + "223", + "222", + "220", + "218", + "215", + "213", + "211", + "209", + "208", + "205", + "203", + "202", + "199", + "197", + "196", + "194", + "191", + "190", + "187", + "185", + "184", + "182", + "180", + "178", + "176", + "173", + "171", + "169", + "167", + "165", + "163", + "161", + "160", + "157", + "155", + "154", + "152", + "150", + "148" + ] + }, "omitApplicationSites": [ { - "domain": "thingiverse.com", - "reason": "Site loads blank and does not proceed." + "domain": "thingiverse.com" }, { - "domain": "qubushotel.com", - "reason": "Homepage UI elements appear squashed together, preventing interaction with the site." + "domain": "qubushotel.com" }, { - "domain": "digid.nl", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/602" + "domain": "digid.nl" }, { - "domain": "intymna.pl", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/667" + "domain": "intymna.pl" }, { - "domain": "sme.sk", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/667" + "domain": "sme.sk" }, { - "domain": "tiktok.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/667" + "domain": "tiktok.com" }, { - "domain": "wykop.pl", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/667" + "domain": "wykop.pl" }, { - "domain": "nypost.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/667" + "domain": "nypost.com" }, { - "domain": "crunchynihongo.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1201" + "domain": "crunchynihongo.com" }, { - "domain": "hulu.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1145" + "domain": "vsp.com" }, { - "domain": "vsp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1292" + "domain": "earth.google.com" }, { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" + "domain": "iscorp.com" }, { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "marvel.com" }, { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" + "domain": "sundancecatalog.com" }, { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "cvs.com" }, { - "domain": "cvs.com", - "reason": "Navigation section at the top of the main page renders as text." + "domain": "facebook.com" }, { - "domain": "facebook.com", - "reason": "When going to the main login page, there is a banner at the top which warns of an 'Unsupported browser'." + "domain": "finewineandgoodspirits.com" }, { - "domain": "instagram.com", - "reason": "A banner at the bottom which warns of an 'Unsupported browser' and interferes with rest of the UI." + "domain": "formula1.com" }, { - "domain": "republicservices.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/667" + "domain": "hulu.com" }, { - "domain": "xfinity.com", - "reason": "Stream page shows an incompatibility warning and does not load further." + "domain": "instagram.com" + }, + { + "domain": "republicservices.com" + }, + { + "domain": "xfinity.com" }, { - "domain": "homedepot.ca", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/554" + "domain": "homedepot.ca" }, { - "domain": "unclaimedmoneyinfo.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/667" + "domain": "unclaimedmoneyinfo.com" }, { - "domain": "timesmachine.nytimes.com", - "reason": "Site is blank (never loads)" + "domain": "timesmachine.nytimes.com" } ], "omitVersionSites": [ { - "domain": "crunchynihongo.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1201" + "domain": "crunchynihongo.com" }, { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" + "domain": "earth.google.com" }, { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "iscorp.com" }, { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" + "domain": "marvel.com" }, { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "sundancecatalog.com" } ] }, "exceptions": [], "state": "enabled", - "hash": "8b39b35be3b5b92c999539eb1ebc252f" + "hash": "b101ded240bc51fce0ece83191b2da62" }, "duckPlayer": { "exceptions": [], @@ -1537,96 +1364,19 @@ "elementHiding": { "exceptions": [ { - "domain": "duckduckgo.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1236" - }, - { - "domain": "bild.de", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/589" - }, - { - "domain": "derstandard.at", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1101" - }, - { - "domain": "foxnews.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/965" - }, - { - "domain": "kbb.com", - "reason": "Adblocker wall" - }, - { - "domain": "wiwo.de", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/592" - }, - { - "domain": "metro.co.uk", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/592" - }, - { - "domain": "blick.ch", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/592" - }, - { - "domain": "thechive.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/592" - }, - { - "domain": "bizjournals.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/592" - }, - { - "domain": "slate.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/592" - }, - { - "domain": "dailycaller.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/592" - }, - { - "domain": "dailymail.co.uk", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/592" - }, - { - "domain": "eltiempo.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/592" - }, - { - "domain": "dailyherald.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/592" - }, - { - "domain": "publico.es", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/592" - }, - { - "domain": "rawstory.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/592" - }, - { - "domain": "allgemeine-zeitung.de", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/592" - }, - { - "domain": "thehindu.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/592" + "domain": "duckduckgo.com" }, { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" + "domain": "earth.google.com" }, { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "iscorp.com" }, { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" + "domain": "marvel.com" }, { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "sundancecatalog.com" } ], "settings": { @@ -1780,6 +1530,10 @@ "selector": "#topBanner", "type": "hide-empty" }, + { + "selector": ".top-ad", + "type": "hide-empty" + }, { "selector": ".ad-banner-container", "type": "hide-empty" @@ -1832,6 +1586,10 @@ "selector": ".ad_container", "type": "closest-empty" }, + { + "selector": ".ad-container--leaderboard", + "type": "hide-empty" + }, { "selector": ".ads_container", "type": "hide-empty" @@ -1884,6 +1642,14 @@ "selector": ".ad-slot", "type": "closest-empty" }, + { + "selector": "#ad-top", + "type": "hide-empty" + }, + { + "selector": "#ad-wrap", + "type": "hide-empty" + }, { "selector": ".ad-wrap", "type": "closest-empty" @@ -1900,6 +1666,50 @@ "selector": "[class^='adWrapper']", "type": "closest-empty" }, + { + "selector": "[class*='container--ads']", + "type": "hide-empty" + }, + { + "selector": "[class*='l-ad']", + "type": "hide-empty" + }, + { + "selector": "[class*='page-ad']", + "type": "hide-empty" + }, + { + "selector": "[class*='module__ad']", + "type": "hide-empty" + }, + { + "selector": "[class*='advertising']", + "type": "hide-empty" + }, + { + "selector": "[class*='AdvertisingSlot']", + "type": "hide-empty" + }, + { + "selector": "[class*='AdSlot']", + "type": "hide-empty" + }, + { + "selector": "[class*='werbung']", + "type": "hide-empty" + }, + { + "selector": "[class*='am-bazaar-ad']", + "type": "hide-empty" + }, + { + "selector": ".adBanner", + "type": "hide-empty" + }, + { + "selector": ".adModule", + "type": "hide-empty" + }, { "selector": "[class^='Display_displayAd']", "type": "hide-empty" @@ -1932,6 +1742,10 @@ "selector": "[data-ad]", "type": "hide-empty" }, + { + "selector": ".instream_ad", + "type": "hide-empty" + }, { "selector": ".adthrive", "type": "hide-empty" @@ -1991,16 +1805,16 @@ { "selector": "#ez-content-blocker-container", "type": "hide" - }, - { - "selector": "#notify-adblock", - "type": "hide" } ], "styleTagExceptions": [ { "domain": "github.com", "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1058" + }, + { + "domain": "pocketbook.digital", + "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1365" } ], "hideTimeouts": [ @@ -2009,13 +1823,16 @@ 300, 500, 1000, + 1500, 2000, - 3000 + 3000, + 5000 ], "unhideTimeouts": [ 1250, 2250, - 3000 + 3250, + 5250 ], "mediaAndFormSelectors": "video,canvas,embed,object,audio,map,form,input,textarea,select,option", "adLabelStrings": [ @@ -2026,6 +1843,7 @@ "advertisements", "advertisment", "advertisementclose", + "advertisementadvertisement", "advertisementcontinue reading the main story", "advertisement\ncontinue reading the main story", "advertisement\n\ncontinue reading the main story", @@ -2033,10 +1851,12 @@ "advertisementhide ad", "advertisement - scroll to continue", "advertisement · scroll to continue", + "advertisement - story continues below", "advertising", "advertising\nskip ad", "advertising\nskip ad\nskip ad\nskip ad", "ad feedback", + "annonse", "anzeige", "close ad", "close this ad", @@ -2044,14 +1864,18 @@ "sponsored", "sponsorisé", "story continues below advertisement", + "story continues below advertisementadvertisement", + "oglas", "publicité", "publicidade", + "- publicidade -", "reklama", "skip ad", "sponsored news", "continue reading the main story", "this advertisement has not loaded yet, but your article continues below.", - "story continues below\nthis advertisement has not loaded yet, but your article continues below." + "story continues below\nthis advertisement has not loaded yet, but your article continues below.", + "upgrade to flickr pro to hide these ads" ], "domains": [ { @@ -2063,6 +1887,48 @@ } ] }, + { + "domain": "acidadeon.com", + "rules": [ + { + "selector": "[class*='mrf-adv']", + "type": "hide-empty" + }, + { + "selector": "[class*='td-block']", + "type": "hide-empty" + } + ] + }, + { + "domain": "allgemeine-zeitung.de", + "rules": [ + { + "type": "disable-default" + }, + { + "selector": ".adSlot", + "type": "hide-empty" + }, + { + "selector": ".adBorder", + "type": "hide-empty" + }, + { + "selector": ".nativeAd", + "type": "closest-empty" + } + ] + }, + { + "domain": "androidpolice.com", + "rules": [ + { + "selector": "[class*='ad-zone']", + "type": "hide" + } + ] + }, { "domain": "apnews.com", "rules": [ @@ -2072,6 +1938,15 @@ } ] }, + { + "domain": "avito.ru", + "rules": [ + { + "selector": "[class*='advert-mimicry-block']", + "type": "hide" + } + ] + }, { "domain": "bbc.com", "rules": [ @@ -2094,6 +1969,31 @@ } ] }, + { + "domain": "bild.de", + "rules": [ + { + "type": "disable-default" + }, + { + "selector": "[id^='mrec']", + "type": "hide-empty" + }, + { + "selector": "#superbanner", + "type": "hide-empty" + } + ] + }, + { + "domain": "bizjournals.com", + "rules": [ + { + "selector": ".adwrap", + "type": "closest-empty" + } + ] + }, { "domain": "bleacherreport.com", "rules": [ @@ -2112,6 +2012,15 @@ } ] }, + { + "domain": "blick.ch", + "rules": [ + { + "selector": "[id*='appnexus-placement-']", + "type": "hide-empty" + } + ] + }, { "domain": "bloomberg.com", "rules": [ @@ -2214,11 +2123,27 @@ ] }, { - "domain": "dallasnews.com", + "domain": "dailyherald.com", "rules": [ { - "selector": "[data-targeting]", + "selector": ".instoryAdBlock", + "type": "hide" + } + ] + }, + { + "domain": "dailymail.co.uk", + "rules": [ + { + "type": "disable-default" + }, + { + "selector": "[class*='dmg-ads']", "type": "hide-empty" + }, + { + "selector": ".mol-ads-label-container", + "type": "closest-empty" } ] }, @@ -2243,6 +2168,15 @@ } ] }, + { + "domain": "deseret.com", + "rules": [ + { + "selector": "[data-targeting]", + "type": "override" + } + ] + }, { "domain": "ebay.com", "rules": [ @@ -2289,12 +2223,25 @@ ] }, { - "domain": "express.de", + "domain": "examiner.com.au", "rules": [ { - "selector": ".dm-slot", + "selector": "[class*='flex items']", "type": "hide-empty" - } + }, + { + "selector": ".hidden", + "type": "hide-empty" + } + ] + }, + { + "domain": "express.de", + "rules": [ + { + "selector": ".dm-slot", + "type": "hide-empty" + } ] }, { @@ -2331,6 +2278,15 @@ } ] }, + { + "domain": "flickr.com", + "rules": [ + { + "selector": ".feed-b", + "type": "hide-empty" + } + ] + }, { "domain": "focus.de", "rules": [ @@ -2356,6 +2312,27 @@ } ] }, + { + "domain": "foodnetwork.co.uk", + "rules": [ + { + "selector": ".bg-bodyBg", + "type": "hide-empty" + }, + { + "selector": ".module--ad-module-primary", + "type": "hide-empty" + }, + { + "selector": "#mtf-1", + "type": "closest-empty" + }, + { + "selector": "#btf-1", + "type": "closest-empty" + } + ] + }, { "domain": "football365.com", "rules": [ @@ -2394,9 +2371,46 @@ { "domain": "foxnews.com", "rules": [ + { + "type": "disable-default" + }, { "selector": ".vendor-unit", "type": "hide-empty" + }, + { + "selector": ".pre-content", + "type": "hide-empty" + }, + { + "selector": "[class*='rr-ad-']", + "type": "hide-empty" + }, + { + "selector": ".ad-h-250", + "type": "hide-empty" + }, + { + "selector": ".sticky-pre-header", + "type": "hide-empty" + }, + { + "selector": ".adhesion-ad", + "type": "hide-empty" + }, + { + "selector": ".sticky-pre-header-inner", + "type": "hide-empty" + }, + { + "selector": ".site-header", + "type": "modify-style", + "values": [ + { + "property": "min-height", + "value": "50px" + } + ] } ] }, @@ -2558,6 +2572,15 @@ } ] }, + { + "domain": "ilfattoquotidiano.it", + "rules": [ + { + "selector": "[class*='adv']", + "type": "hide-empty" + } + ] + }, { "domain": "indeed.com", "rules": [ @@ -2593,6 +2616,15 @@ } ] }, + { + "domain": "insider.com", + "rules": [ + { + "selector": ".in-post-sticky", + "type": "hide-empty" + } + ] + }, { "domain": "investing.com", "rules": [ @@ -2623,6 +2655,19 @@ } ] }, + { + "domain": "komputerswiat.pl", + "rules": [ + { + "selector": "[class*='extraList']", + "type": "hide-empty" + }, + { + "selector": "[class*='placeholder']", + "type": "hide-empty" + } + ] + }, { "domain": "leboncoin.fr", "rules": [ @@ -2680,6 +2725,18 @@ } ] }, + { + "domain": "metro.co.uk", + "rules": [ + { + "type": "disable-default" + }, + { + "selector": ".ad-slot-container", + "type": "hide-empty" + } + ] + }, { "domain": "mirror.co.uk", "rules": [ @@ -2706,6 +2763,15 @@ } ] }, + { + "domain": "n1info.si", + "rules": [ + { + "selector": "[class*='banner-placeholder']", + "type": "hide-empty" + } + ] + }, { "domain": "nasdaq.com", "rules": [ @@ -2885,6 +2951,28 @@ } ] }, + { + "domain": "paypal.com", + "rules": [ + { + "selector": "#gslFrame", + "type": "hide" + } + ] + }, + { + "domain": "petapixel.com", + "rules": [ + { + "selector": ".banners", + "type": "hide-empty" + }, + { + "selector": "#ppvideoadvertisement", + "type": "closest-empty" + } + ] + }, { "domain": "peterboroughtoday.co.uk", "rules": [ @@ -2937,6 +3025,19 @@ } ] }, + { + "domain": "publico.es", + "rules": [ + { + "selector": ".pb-ads", + "type": "hide-empty" + }, + { + "selector": "#sc_intxt_container", + "type": "hide" + } + ] + }, { "domain": "qz.com", "rules": [ @@ -2946,6 +3047,35 @@ } ] }, + { + "domain": "rawstory.com", + "rules": [ + { + "selector": ".container_proper-ad-unit", + "type": "hide" + }, + { + "selector": ".controlled_via_ad_manager", + "type": "hide" + }, + { + "selector": ".mgid_3x2", + "type": "hide-empty" + }, + { + "selector": ".proper-ad-unit", + "type": "hide" + }, + { + "selector": "[id^='rc-widget-']", + "type": "hide-empty" + }, + { + "selector": "#story-top-ad", + "type": "hide" + } + ] + }, { "domain": "reddit.com", "rules": [ @@ -2963,6 +3093,15 @@ } ] }, + { + "domain": "refinery29.com", + "rules": [ + { + "selector": ".section-ad", + "type": "hide-empty" + } + ] + }, { "domain": "reuters.com", "rules": [ @@ -3027,6 +3166,15 @@ } ] }, + { + "domain": "sfchronicle.com", + "rules": [ + { + "selector": "[class*='belowMastheadWrapper']", + "type": "hide-empty" + } + ] + }, { "domain": "si.com", "rules": [ @@ -3049,6 +3197,19 @@ } ] }, + { + "domain": "slate.com", + "rules": [ + { + "selector": ".slate-ad", + "type": "hide-empty" + }, + { + "selector": ".top-ad", + "type": "hide" + } + ] + }, { "domain": "snopes.com", "rules": [ @@ -3058,6 +3219,23 @@ } ] }, + { + "domain": "spanishdict.com", + "rules": [ + { + "selector": "[id*='adSide']", + "type": "hide-empty" + }, + { + "selector": "[id*='adTop']", + "type": "hide-empty" + }, + { + "selector": "[id*='adMiddle']", + "type": "hide-empty" + } + ] + }, { "domain": "spankbang.com", "rules": [ @@ -3067,6 +3245,24 @@ } ] }, + { + "domain": "sportbible.com", + "rules": [ + { + "selector": "[class*='Advert']", + "type": "hide" + } + ] + }, + { + "domain": "takealot.com", + "rules": [ + { + "selector": "[class*='ad-slot_']", + "type": "override" + } + ] + }, { "domain": "target.com", "rules": [ @@ -3085,6 +3281,33 @@ } ] }, + { + "domain": "thegatewaypundit.com", + "rules": [ + { + "selector": ".code-block", + "type": "hide" + } + ] + }, + { + "domain": "thehour.com", + "rules": [ + { + "selector": ".belowMastheadWrapper", + "type": "hide-empty" + } + ] + }, + { + "domain": "thehindu.com", + "rules": [ + { + "selector": "#articledivrec", + "type": "hide-empty" + } + ] + }, { "domain": "thetimes.co.uk", "rules": [ @@ -3102,6 +3325,15 @@ } ] }, + { + "domain": "thewordfinder.com", + "rules": [ + { + "selector": "[id*='adngin']", + "type": "closest-empty" + } + ] + }, { "domain": "thingiverse.com", "rules": [ @@ -3111,6 +3343,15 @@ } ] }, + { + "domain": "tinybeans.com", + "rules": [ + { + "selector": ".tb-ad", + "type": "hide-empty" + } + ] + }, { "domain": "tripadvisor.ca", "rules": [ @@ -3196,6 +3437,15 @@ } ] }, + { + "domain": "tvtropes.org", + "rules": [ + { + "selector": "[class*='-fad-unit']", + "type": "hide-empty" + } + ] + }, { "domain": "uol.com.br", "rules": [ @@ -3211,6 +3461,10 @@ { "selector": "[class^='Ad__Container-']", "type": "hide" + }, + { + "selector": "#ac-lre-player", + "type": "hide-empty" } ] }, @@ -3240,6 +3494,15 @@ } ] }, + { + "domain": "westernjournal.com", + "rules": [ + { + "selector": ".sponsor", + "type": "hide-empty" + } + ] + }, { "domain": "washingtonpost.com", "rules": [ @@ -3257,6 +3520,15 @@ } ] }, + { + "domain": "whitakerfuneralhome.com", + "rules": [ + { + "selector": ".top-banner", + "type": "override" + } + ] + }, { "domain": "winnipegfreepress.com", "rules": [ @@ -3266,12 +3538,24 @@ } ] }, + { + "domain": "wiwo.de", + "rules": [ + { + "type": "disable-default" + } + ] + }, { "domain": "wsj.com", "rules": [ { "selector": "#cx-what-to-read-next", "type": "closest-empty" + }, + { + "selector": "[class*='WSJTheme--adWrapper']", + "type": "hide-empty" } ] }, @@ -3299,6 +3583,10 @@ { "selector": ".darla", "type": "closest-empty" + }, + { + "selector": "[data-content='Advertisement']", + "type": "hide-empty" } ] }, @@ -3392,27 +3680,6 @@ } ] }, - { - "domain": "privacy-test-pages.glitch.me", - "rules": [ - { - "selector": ".hide-test", - "type": "hide" - }, - { - "selector": ".hide-empty-test", - "type": "hide-empty" - }, - { - "selector": ".closest-empty-test", - "type": "closest-empty" - }, - { - "selector": "[class^='ad-container']", - "type": "override" - } - ] - }, { "domain": "privacy-test-pages.site", "rules": [ @@ -3437,73 +3704,67 @@ ] }, "state": "enabled", - "hash": "d0034c140c0ee9ce9015b74b428523b3" + "hash": "c747ff47f18924f7ddde7cea3874e3bc" }, "exceptionHandler": { "exceptions": [ { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" + "domain": "earth.google.com" }, { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "iscorp.com" }, { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" + "domain": "marvel.com" }, { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "sundancecatalog.com" } ], "state": "disabled", - "hash": "f3fcace884f1423ec9c111587966d0bb" + "hash": "5e792dd491428702bc0104240fbce0ce" }, "fingerprintingAudio": { "state": "disabled", "exceptions": [ { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" + "domain": "litebluesso.usps.gov" + }, + { + "domain": "earth.google.com" }, { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "iscorp.com" }, { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" + "domain": "marvel.com" }, { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "sundancecatalog.com" } ], - "hash": "47ea2125485511e0d94a8e767b246068" + "hash": "f25a8f2709e865c2bd743828c7ee2f77" }, "fingerprintingBattery": { "exceptions": [ { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" + "domain": "litebluesso.usps.gov" }, { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "earth.google.com" }, { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" + "domain": "iscorp.com" }, { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "marvel.com" + }, + { + "domain": "sundancecatalog.com" } ], "state": "enabled", - "hash": "0f48731cc79a1d8d063df9f955f6acd4" + "hash": "440f8d663d59430c93d66208655d9238" }, "fingerprintingCanvas": { "settings": { @@ -3511,132 +3772,104 @@ }, "exceptions": [ { - "domain": "adidas.com", - "reason": "When adding an item to the cart, there is an error 'An unexpected problem occurred' and the item is not added to the cart." + "domain": "adidas.com" + }, + { + "domain": "adidas.co.uk" }, { - "domain": "adidas.co.uk", - "reason": "When adding an item to the cart, there is an error 'An unexpected problem occurred' and the item is not added to the cart." + "domain": "amtrak.com" }, { - "domain": "amtrak.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/499" + "domain": "att.com" }, { - "domain": "att.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "bloomingdales.com" }, { - "domain": "bloomingdales.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/379" + "domain": "capitalone.com" }, { - "domain": "capitalone.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "chase.com" }, { - "domain": "chase.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/499" + "domain": "citi.com" }, { - "domain": "citi.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/499" + "domain": "emirates.com" }, { - "domain": "emirates.com", - "reason": "After filling out flight information and clicking 'Search flights', a blank page is shown for several seconds before the page redirects." + "domain": "fedex.com" }, { - "domain": "fedex.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/927" + "domain": "finishline.com" }, { - "domain": "finishline.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1015" + "domain": "gynzykids.com" }, { - "domain": "gynzykids.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/908" + "domain": "hm.com" }, { - "domain": "hm.com", - "reason": "When adding an item to cart, there is an error 'something went wrong', and the item does not get added to the cart." + "domain": "ikea.com" }, { - "domain": "ikea.com", - "reason": "When creating an account, after filling out details and going to e-mail confirmation, there is an error 'Something went wrong', and the e-mail may not be received. Clicking 'Send new code' appears successful, but after entering the code there is often another account creation error." + "domain": "litebluesso.usps.gov" }, { - "domain": "manulife.ca", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/499" + "domain": "manulife.ca" }, { - "domain": "navyfederal.org", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "navyfederal.org" }, { - "domain": "northernrailway.co.uk", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/350" + "domain": "northernrailway.co.uk" }, { - "domain": "online-calculator.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/875" + "domain": "online-calculator.com" }, { - "domain": "online-stopwatch.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/875" + "domain": "online-stopwatch.com" }, { - "domain": "spirit.com", - "reason": "When attempting to sign in, a semi-transparent overlay appears over the page which prevents further interaction with the site." + "domain": "spirit.com" }, { - "domain": "suncoastcreditunion.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "suncoastcreditunion.com" }, { - "domain": "thetrainline.com", - "reason": "After filling out travel info and clicking 'Get times & tickets', there is an error warning that 'Something went wrong', which prevents further interaction." + "domain": "thetrainline.com" }, { - "domain": "usps.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "usps.com" }, { - "domain": "walgreens.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/499" + "domain": "walgreens.com" }, { - "domain": "wellsfargo.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "wellsfargo.com" }, { - "domain": "xfinity.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/788" + "domain": "xfinity.com" }, { - "domain": "godaddy.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/499" + "domain": "godaddy.com" }, { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" + "domain": "earth.google.com" }, { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "iscorp.com" }, { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" + "domain": "marvel.com" }, { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "sundancecatalog.com" } ], "state": "disabled", - "hash": "3b3d0307a60663097a8efad33af4fd08" + "hash": "ea4c565bae27996f0d651300d757594c" }, "fingerprintingHardware": { "settings": { @@ -3653,236 +3886,44 @@ }, "exceptions": [ { - "domain": "www.ticketmaster.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/575" - }, - { - "domain": "gamestop.com", - "reason": "Clicking 'add to cart' causes a spinner to load briefly, and the item doesn't get added to the cart." - }, - { - "domain": "godaddy.com", - "reason": "After entering login details and clicking to log in, the site shows an adwall and prevents login." - }, - { - "domain": "fedex.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/927" - }, - { - "domain": "realestate.com.au", - "reason": "Site loads blank and does not proceed." - }, - { - "domain": "secureserver.net", - "reason": "After entering login details and clicking to log in, the site shows an adwall and prevents login." - }, - { - "domain": "hyatt.com", - "reason": "Site loads blank and does not proceed." - }, - { - "domain": "bank.marksandspencer.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" - }, - { - "domain": "www7.marksandspencer.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" - }, - { - "domain": "fidelity.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" - }, - { - "domain": "citi.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" - }, - { - "domain": "americanexpress.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "capitalone.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "marcus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "bankofamerica.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "wellsfargo.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "usbank.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "citizensbankonline.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "sofi.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "regions.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "usaa.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "boh.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "santander.com.br", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "pnc.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "edwardjones.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "ally.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "metrobank.com.ph", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "key.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "synchronybank.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "oldglorybank.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "santander.com.mx", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "secureinternetbank.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "bankfirst.com.au", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "fnbo.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "salliemae.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "bnc.bank", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "kanza.bank", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "prosperity.bank", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "firstwestern.bank", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "penncrest.bank", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "northwest.bank", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "bell.bank", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "dollar.bank", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "dellsbank.bank", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "rcbbank.bank", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" + "domain": "www.ticketmaster.com" }, { - "domain": "ebt.bank", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" + "domain": "gamestop.com" }, { - "domain": "mybct.bank", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" + "domain": "godaddy.com" }, { - "domain": "wells.bank", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" + "domain": "fedex.com" }, { - "domain": "365.bank", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" + "domain": "litebluesso.usps.gov" }, { - "domain": "atom.bank", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" + "domain": "realestate.com.au" }, { - "domain": "ibanking-services.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" + "domain": "secureserver.net" }, { - "domain": "bmo.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" + "domain": "hyatt.com" }, { - "domain": "tsb.co.uk", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" + "domain": "earth.google.com" }, { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" + "domain": "iscorp.com" }, { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "marvel.com" }, { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" - }, - { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "sundancecatalog.com" } ], "state": "enabled", - "hash": "f28bbd2f54f441ea5f830398df41f45e" + "hash": "911cbc583f376d416ac75c57ecc577f1" }, "fingerprintingScreenSize": { "settings": { @@ -3905,323 +3946,128 @@ }, "exceptions": [ { - "domain": "fedex.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/927" - }, - { - "domain": "gamestop.com", - "reason": "Clicking 'add to cart' causes a spinner to load briefly, and the item doesn't get added to the cart." - }, - { - "domain": "godaddy.com", - "reason": "After entering login details and clicking to log in, the site shows an adwall and prevents login." - }, - { - "domain": "hyatt.com", - "reason": "Site loads blank and does not proceed." - }, - { - "domain": "secureserver.net", - "reason": "After entering login details and clicking to log in, the site shows an adwall and prevents login." - }, - { - "domain": "bank.marksandspencer.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" - }, - { - "domain": "www7.marksandspencer.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" - }, - { - "domain": "fidelity.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "fedex.com" }, { - "domain": "citi.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "gamestop.com" }, { - "domain": "americanexpress.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" + "domain": "godaddy.com" }, { - "domain": "capitalone.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" + "domain": "hyatt.com" }, { - "domain": "marcus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" + "domain": "litebluesso.usps.gov" }, { - "domain": "bankofamerica.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" + "domain": "secureserver.net" }, { - "domain": "wellsfargo.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" + "domain": "earth.google.com" }, { - "domain": "usbank.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" + "domain": "iscorp.com" }, { - "domain": "citizensbankonline.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" + "domain": "marvel.com" }, { - "domain": "sofi.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "regions.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "usaa.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "boh.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "santander.com.br", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "pnc.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "edwardjones.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "ally.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "metrobank.com.ph", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "key.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "synchronybank.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "oldglorybank.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "santander.com.mx", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "secureinternetbank.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "bankfirst.com.au", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "fnbo.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "salliemae.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "bnc.bank", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "kanza.bank", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "prosperity.bank", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "firstwestern.bank", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "penncrest.bank", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "northwest.bank", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "bell.bank", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "dollar.bank", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "dellsbank.bank", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "rcbbank.bank", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "ebt.bank", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "mybct.bank", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "wells.bank", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "365.bank", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "atom.bank", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "ibanking-services.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "bmo.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "tsb.co.uk", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1265" - }, - { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" - }, - { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" - }, - { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" - }, - { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "sundancecatalog.com" } ], "state": "enabled", - "hash": "702a26f39577785255f35af7fc7f2578" + "hash": "0fb22f84b750e0d29bad55bd95d9ce2b" }, "fingerprintingTemporaryStorage": { "exceptions": [ { - "domain": "fedex.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/927" + "domain": "fedex.com" + }, + { + "domain": "litebluesso.usps.gov" }, { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" + "domain": "earth.google.com" }, { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "iscorp.com" }, { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" + "domain": "marvel.com" }, { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "sundancecatalog.com" } ], "state": "enabled", - "hash": "4d9f5564b76c44f76b90692d98230d78" + "hash": "568a23faa984c8e7eda002294ad8f82f" }, "googleRejected": { "exceptions": [ { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" + "domain": "earth.google.com" }, { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "iscorp.com" }, { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" + "domain": "marvel.com" }, { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "sundancecatalog.com" } ], "state": "disabled", - "hash": "f3fcace884f1423ec9c111587966d0bb" + "hash": "5e792dd491428702bc0104240fbce0ce" }, "gpc": { "state": "enabled", "exceptions": [ { - "domain": "allegiantair.com", - "reason": "Example URL: https://www.allegiantair.com/seating-checking-boarding; Clicking the 'show details' button in the FAQ sections does nothing." + "domain": "abcnews.go.com" + }, + { + "domain": "allegiantair.com" + }, + { + "domain": "boston.com" + }, + { + "domain": "costco.com" }, { - "domain": "boston.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/700" + "domain": "crunchyroll.com" }, { - "domain": "costco.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/644" + "domain": "eventbrite.com" }, { - "domain": "eventbrite.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/700" + "domain": "duluthtrading.com" }, { - "domain": "duluthtrading.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/700" + "domain": "web.whatsapp.com" }, { - "domain": "web.whatsapp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/620" + "domain": "verizon.com" }, { - "domain": "verizon.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1282" + "domain": "chime.com" }, { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" + "domain": "earth.google.com" }, { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "iscorp.com" }, { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" + "domain": "marvel.com" }, { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "sundancecatalog.com" + }, + { + "domain": "oreillyauto.com" } ], "settings": { @@ -4230,11 +4076,10 @@ "globalprivacycontrol.org", "washingtonpost.com", "nytimes.com", - "privacy-test-pages.glitch.me", "privacy-test-pages.site" ] }, - "hash": "9962dbae2a6a668fbdae3b20f02bcbad" + "hash": "9f8ae6a01924a47b3ca6ce18f001cbd9" }, "harmfulApis": { "settings": { @@ -4337,50 +4182,47 @@ }, "exceptions": [ { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" + "domain": "earth.google.com" }, { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "iscorp.com" }, { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" + "domain": "marvel.com" }, { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "sundancecatalog.com" } ], "state": "disabled", - "hash": "0381a7afb0f7453acb315ab3e0884eaa" + "hash": "44d3e707cba3ee0a3578f52dc2ce2aa4" }, "https": { "state": "enabled", "exceptions": [ { - "domain": "act.alz.org", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1158" + "domain": "act.alz.org" + }, + { + "domain": "amica.com" }, { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" + "domain": "jp.square-enix.com" }, { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "earth.google.com" }, { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" + "domain": "iscorp.com" }, { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "marvel.com" + }, + { + "domain": "sundancecatalog.com" } ], - "hash": "8de33b0e54940edad6504523c8db58ff" + "hash": "f772808ed34cc9ea8cbcbb7cdaf74429" }, "incontextSignup": { "exceptions": [], @@ -4395,29 +4237,32 @@ "state": "disabled", "features": { "rollout": { - "state": "disabled" + "state": "enabled", + "rollout": { + "steps": [ + { + "percent": 0.5 + } + ] + } } }, "exceptions": [], - "hash": "c1293c7b2687c8a2f7c5433a4d7a330d" + "hash": "429cea8d27316dc62af04159ec7c42b5" }, "navigatorInterface": { "exceptions": [ { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" + "domain": "earth.google.com" }, { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "iscorp.com" }, { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" + "domain": "marvel.com" }, { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "sundancecatalog.com" } ], "settings": { @@ -4428,17 +4273,20 @@ ] }, "state": "enabled", - "hash": "002c32fcc15897c2ffae8045ac0bdcb3" + "hash": "698de7b963d7d7942c5c5d1e986bb1b1" }, "networkProtection": { - "state": "disabled", + "state": "enabled", "features": { + "waitlistBetaActive": { + "state": "enabled" + }, "waitlist": { - "state": "disabled" + "state": "enabled" } }, "exceptions": [], - "hash": "bf4c9cd751a7626bd89136f6cc98ccf1" + "hash": "7899368978e0f3aaf7eb141a027e4150" }, "newTabContinueSetUp": { "exceptions": [], @@ -4451,140 +4299,127 @@ }, "exceptions": [ { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" + "domain": "earth.google.com" }, { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "iscorp.com" }, { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" + "domain": "marvel.com" }, { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "sundancecatalog.com" } ], "state": "disabled", - "hash": "81f357de4bfc7abeddf1a3a3fb1fc7a9" + "hash": "841fa92b9728c9754f050662678f82c7" + }, + "privacyDashboard": { + "exceptions": [], + "features": { + "highlightedProtectionsToggle": { + "state": "disabled", + "rollout": { + "steps": [] + } + } + }, + "state": "disabled", + "hash": "dede7e70939822f5ecb9eb5fae577fa3" }, "referrer": { "exceptions": [ { - "domain": "atlassian.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1092" + "domain": "atlassian.com" }, { - "domain": "learning.edx.org", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/934" + "domain": "learning.edx.org" }, { - "domain": "login-seconnecter.ca", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1097" + "domain": "login-seconnecter.ca" }, { - "domain": "canadapost-postescanada.ca", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/647" + "domain": "canadapost-postescanada.ca" }, { - "domain": "player.vimeo.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/931" + "domain": "player.vimeo.com" }, { - "domain": "xcelenergy.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/941" + "domain": "xcelenergy.com" }, { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" + "domain": "earth.google.com" }, { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "iscorp.com" }, { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" + "domain": "marvel.com" }, { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "sundancecatalog.com" } ], "state": "disabled", - "hash": "6fb4590e0f11214e27a5386f24f1962f" + "hash": "0d3df0f7c24ebde89d2dced4e2d34322" }, "requestFilterer": { "state": "disabled", "exceptions": [ { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" + "domain": "earth.google.com" }, { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "iscorp.com" }, { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" + "domain": "marvel.com" }, { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "sundancecatalog.com" } ], "settings": { "windowInMs": 0 }, - "hash": "c208c59a304e1d48cdd9ba85c0c8db25" + "hash": "0fff8017d8ea4b5609b8f5c110be1401" }, "runtimeChecks": { "state": "disabled", "exceptions": [ { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" + "domain": "earth.google.com" }, { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "iscorp.com" }, { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" + "domain": "marvel.com" }, { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "sundancecatalog.com" } ], "settings": {}, - "hash": "ca5b15f5db8334f68f29726b21d09c4a" + "hash": "800a19533c728bbec7e31e466f898268" }, "serviceworkerInitiatedRequests": { "exceptions": [ { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" + "domain": "earth.google.com" }, { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "iscorp.com" }, { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" + "domain": "marvel.com" }, { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "sundancecatalog.com" } ], "state": "disabled", - "hash": "f3fcace884f1423ec9c111587966d0bb" + "hash": "5e792dd491428702bc0104240fbce0ce" }, "trackerAllowlist": { "state": "enabled", @@ -4596,8 +4431,7 @@ "rule": "tlx.3lift.com/header/auction", "domains": [ "aternos.org" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/328" + ] } ] }, @@ -4607,8 +4441,7 @@ "rule": "mp.4dex.io/prebid", "domains": [ "aternos.org" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/328" + ] } ] }, @@ -4618,8 +4451,7 @@ "rule": "prebid.a-mo.net/a/c", "domains": [ "aternos.org" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/328" + ] } ] }, @@ -4629,8 +4461,7 @@ "rule": "acsbapp.com", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/629" + ] } ] }, @@ -4640,22 +4471,30 @@ "rule": "s7.addthis.com/js/300/addthis_widget.js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/427" + ] }, { "rule": "s7.addthis.com/l10n/", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/427" + ] }, { "rule": "s7.addthis.com/static/", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/427" + ] + } + ] + }, + "addtoany.com": { + "rules": [ + { + "rule": "static.addtoany.com/menu/page.js", + "domains": [ + "frankspeech.com", + "x22report.com" + ] } ] }, @@ -4665,15 +4504,13 @@ "rule": "adx.adform.net/adx/openrtb", "domains": [ "aternos.org" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/328" + ] }, { "rule": "c1.adform.net/serving/cookie/match", "domains": [ "dhl.de" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/340" + ] } ] }, @@ -4683,8 +4520,7 @@ "rule": "static.ads-twitter.com/uwt.js", "domains": [ "hentaihaven.xxx" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/452" + ] } ] }, @@ -4694,22 +4530,45 @@ "rule": "static.adsafeprotected.com/favicon.ico", "domains": [ "tf1info.fr" - ], - "reason": "Adwall displays over video and prevents video from being played." + ] }, { "rule": "static.adsafeprotected.com/iasPET.1.js", "domains": [ "corriere.it" - ], - "reason": "Example URL: https://www.corriere.it/video-articoli/2022/07/13/missione-wwf-liberare-mare-plastica/9abb64de-029d-11ed-a0cc-ad3c68cacbae.shtml;,Clicking on the video to play causes a still frame to show and the video does not continue." + ] }, { "rule": "static.adsafeprotected.com/vans-adapter-google-ima.js", "domains": [ "nhl.com" - ], - "reason": "Videos show a spinner and never load." + ] + } + ] + }, + "adserver.adtech.advertising.com": { + "rules": [ + { + "rule": "adserver.adtech.advertising.com/pubapi/3.0/1/54669.7/0/0/ADTECH;v=2;cmd=bid;cors=yes", + "domains": [ + "collider.com" + ] + } + ] + }, + "adswizz.com": { + "rules": [ + { + "rule": "synchrobox.adswizz.com", + "domains": [ + "tunein.com" + ] + }, + { + "rule": "adswizz.com/adswizz/js/SynchroClient2.js", + "domains": [ + "tunein.com" + ] } ] }, @@ -4721,8 +4580,7 @@ "adamtheautomator.com", "gardeningknowhow.com", "packhacker.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1122" + ] } ] }, @@ -4732,8 +4590,18 @@ "rule": "ahacdn.me", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/447" + ] + } + ] + }, + "aimbase.com": { + "rules": [ + { + "rule": "ws.aimbase.com/Scripts/awa.js", + "domains": [ + "chriscraft.com", + "regulatormarine.com" + ] } ] }, @@ -4743,8 +4611,7 @@ "rule": "login.fidelity.com.febsec-fidelity.com.akadns.net", "domains": [ "fidelity.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/677" + ] } ] }, @@ -4754,8 +4621,7 @@ "rule": "assets.aldi-digital.co.uk/assets/050b4966c22c430e5c9308903ebb87e1/dist/scripts/main.js", "domains": [ "aldi.co.uk" - ], - "reason": "Product lists don't render." + ] } ] }, @@ -4765,8 +4631,7 @@ "rule": "alicdn.com", "domains": [ "aliexpress.us" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/460" + ] } ] }, @@ -4775,14 +4640,39 @@ { "rule": "c.amazon-adsystem.com/aax2/apstag.js", "domains": [ + "4029tv.com", "cnn.com", "corriere.it", "eurogamer.net", + "foxweather.com", + "kcci.com", + "kcra.com", + "ketv.com", + "kmbc.com", + "koat.com", + "koco.com", + "ksbw.com", + "mynbc5.com", "seattletimes.com", + "thesurfersview.com", + "wapt.com", + "wbaltv.com", "wcvb.com", - "wildrivers.lostcoastoutpost.com" - ], - "reason": "corriere.it - ,Example URL: https://www.corriere.it/video-articoli/2022/07/13/missione-wwf-liberare-mare-plastica/9abb64de-029d-11ed-a0cc-ad3c68cacbae.shtml;,Clicking on the video to play causes a still frame to show and the video does not continue.,eurogamer.net, seattletimes.com - An unskippable adwall appears which prevents interaction with the page.,cnn.com - https://github.com/duckduckgo/privacy-configuration/issues/1220,wcvb.com - https://github.com/duckduckgo/privacy-configuration/issues/1088,wildrivers.lostcoastoutpost.com - https://github.com/duckduckgo/privacy-configuration/issues/1252" + "wdsu.com", + "wesh.com", + "wgal.com", + "wildrivers.lostcoastoutpost.com", + "wisn.com", + "wlky.com", + "wlwt.com", + "wmtw.com", + "wmur.com", + "wpbf.com", + "wtae.com", + "wvtm13.com", + "wxii12.com", + "wyff4.com" + ] } ] }, @@ -4792,8 +4682,7 @@ "rule": "prime.amazon.dev", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/943" + ] } ] }, @@ -4803,8 +4692,7 @@ "rule": "elb.amazonaws.com/public/digital-experience/js/common.js", "domains": [ "cigna.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/820" + ] } ] }, @@ -4814,8 +4702,7 @@ "rule": "analytics.analytics-egain.com/onetag/", "domains": [ "support.norton.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1156" + ] } ] }, @@ -4825,8 +4712,7 @@ "rule": "tag.aticdn.net", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/919" + ] } ] }, @@ -4836,8 +4722,7 @@ "rule": "att.com/scripts/att_common.js", "domains": [ "directv.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1107" + ] } ] }, @@ -4847,8 +4732,7 @@ "rule": "aweber.com/form/", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1107" + ] } ] }, @@ -4858,8 +4742,7 @@ "rule": "amp.azure.net/libs/amp/", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/504" + ] } ] }, @@ -4869,8 +4752,7 @@ "rule": "orderweb-cdn-endpoint-centralus.azureedge.net/js/chunk-vendors.js", "domains": [ "chipotle.com" - ], - "reason": "Site loads blank and cannot be interacted with." + ] } ] }, @@ -4880,8 +4762,7 @@ "rule": "marvel-b1-cdn.bc0a.com/f00000000269380/www.beretta.com/assets/", "domains": [ "beretta.com" - ], - "reason": "Example URL1: https://www.beretta.com/en-us/womens-clothing/caps-and-hats/;,Example URL2: https://www.beretta.com/en-us/mens-clothing/caps-and-hats/lp-trucker-hat/;,Various product and product-related images do not render - e.g., main product images on product pages, product images in product listings, etc." + ] } ] }, @@ -4891,50 +4772,43 @@ "rule": "r.bing.com/rp/", "domains": [ "" - ], - "reason": "See https://github.com/duckduckgo/privacy-configuration/issues/321.,These requests are associated with map/location functionality on websites." + ] }, { "rule": "bing.com/th", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/765" + ] }, { "rule": "www.bing.com/api/maps/mapcontrol", "domains": [ "" - ], - "reason": "See https://github.com/duckduckgo/privacy-configuration/issues/321.,This request is associated with map/location functionality on websites." + ] }, { "rule": "www.bing.com/api/v6/Places/AutoSuggest", "domains": [ "" - ], - "reason": "See https://github.com/duckduckgo/privacy-configuration/issues/321.,This request is associated with map/location auto-suggest functionality on websites." + ] }, { "rule": "www.bing.com/maps/sdk/mapcontrol", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/321" + ] }, { "rule": "www.bing.com/maps/sdkrelease/mapcontrol", "domains": [ "" - ], - "reason": "See https://github.com/duckduckgo/privacy-configuration/issues/321.,This request is associated with map/location functionality on websites." + ] }, { "rule": "www.bing.com/rp/", "domains": [ "" - ], - "reason": "See https://github.com/duckduckgo/privacy-configuration/issues/321.,These requests are associated with map/location functionality on websites." + ] } ] }, @@ -4944,8 +4818,7 @@ "rule": "option.boldapps.net/js/options.js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1222" + ] } ] }, @@ -4955,8 +4828,7 @@ "rule": "captcha-delivery.com", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/501" + ] } ] }, @@ -4966,8 +4838,7 @@ "rule": "htlb.casalemedia.com/cygnus", "domains": [ "aternos.org" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/328" + ] } ] }, @@ -4977,8 +4848,7 @@ "rule": "edge1.certona.net/cd/dd7aa8af/www.asics.com/scripts/resonance.js", "domains": [ "asics.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1038" + ] } ] }, @@ -4988,8 +4858,7 @@ "rule": "civiccomputing\\.com\\/9\\/cookieControl-9\\.x\\.min\\.js", "domains": [ "collisionconf.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/979" + ] } ] }, @@ -4999,64 +4868,55 @@ "rule": "cdnjs.cloudflare.com/cdn-cgi/scripts/.*/cloudflare-static/rocket-loader.min.js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1107" + ] }, { "rule": "cdnjs.cloudflare.com/ajax/libs/leaflet/", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1107" + ] }, { "rule": "cdnjs.cloudflare.com/ajax/libs/three.js/", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1107" + ] }, { "rule": "cdnjs.cloudflare.com/ajax/libs/vue/", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1107" + ] }, { "rule": "cdnjs.cloudflare.com/ajax/libs/video.js/", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1107" + ] }, { "rule": "cdnjs.cloudflare.com/ajax/libs/headjs/", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1107" + ] }, { "rule": "cdnjs.cloudflare.com/ajax/libs/hola_player/", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1107" + ] }, { "rule": "cdnjs.cloudflare.com/ajax/libs/fingerprintjs2/1.8.6/fingerprint2.min.js", "domains": [ "winnipegfreepress.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1066" + ] }, { "rule": "challenges.cloudflare.com", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/641" + ] } ] }, @@ -5066,30 +4926,32 @@ "rule": "d3oxtup47gylpj.cloudfront.net/theme/onlyfans/spa/chunk-vendors.js", "domains": [ "onlyfans.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/440" + ] }, { "rule": "d3nn82uaxijpm6.cloudfront.net/", "domains": [ "strava.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/748" + ] }, { "rule": "d9k0w0y3delq8.cloudfront.net", "domains": [ "hoyolab.com", "hoyoverse.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/754" + ] }, { "rule": "d2s6j0ghajv79z.cloudfront.net", "domains": [ "sigalert.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/774" + ] + }, + { + "rule": "dbukjj6eu5tsf.cloudfront.net/assets.sidearmsports.com/common/js/20170825/video.js", + "domains": [ + "" + ] } ] }, @@ -5099,20 +4961,54 @@ "rule": "cmpv2.computerworld.com/", "domains": [ "computerworld.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/344" + ] } ] }, "connatix.com": { "rules": [ + { + "rule": "cd.connatix.com", + "domains": [ + "" + ] + }, + { + "rule": "cds.connatix.com", + "domains": [ + "" + ] + }, + { + "rule": "cdn.connatix.com", + "domains": [ + "" + ] + }, + { + "rule": "capi.connatix.com", + "domains": [ + "" + ] + }, + { + "rule": "vid.connatix.com", + "domains": [ + "" + ] + }, + { + "rule": "img.connatix.com", + "domains": [ + "" + ] + }, { "rule": "connatix.com", "domains": [ "accuweather.com", "dailymail.co.uk" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/768" + ] } ] }, @@ -5122,8 +5018,7 @@ "rule": "cookielaw.org", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/914" + ] } ] }, @@ -5133,8 +5028,7 @@ "rule": "cookie-cdn.cookiepro.com", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/466" + ] } ] }, @@ -5144,22 +5038,19 @@ "rule": "cdn.cquotient.com/js/v2/gretel.min.js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/486" + ] }, { "rule": "e.cquotient.com/recs/", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/486" + ] }, { "rule": "p.cquotient.com/pebble", "domains": [ "scheels.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/486" + ] } ] }, @@ -5169,8 +5060,7 @@ "rule": "crisp.chat", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1107" + ] } ] }, @@ -5180,8 +5070,7 @@ "rule": "bidder.criteo.com/cdb", "domains": [ "aternos.org" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/328" + ] } ] }, @@ -5191,8 +5080,7 @@ "rule": "cudasvc.com/url", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1107" + ] } ] }, @@ -5202,22 +5090,19 @@ "rule": "cxense.com/public/widget", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1143" + ] }, { "rule": "cxense.com/cx.js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1143" + ] }, { "rule": "cxense.com/cx.cce.js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1143" + ] } ] }, @@ -5227,8 +5112,7 @@ "rule": "datadome.co", "domains": [ "thetrainline.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/817" + ] } ] }, @@ -5238,8 +5122,7 @@ "rule": "daumcdn.net", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/444" + ] } ] }, @@ -5250,8 +5133,7 @@ "domains": [ "dhl.de", "homedepot.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/393" + ] } ] }, @@ -5261,8 +5143,7 @@ "rule": "spcmp.r53.derstandard.de/", "domains": [ "derstandard.de" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/344" + ] } ] }, @@ -5272,35 +5153,36 @@ "rule": "doubleclick.net/ondemand/hls/content/", "domains": [ "history.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1185" + ] }, { "rule": "securepubads.g.doubleclick.net/gampad/ads", "domains": [ "ah.nl", "rocketnews24.com" - ], - "reason": "ah.nl - 'Bonus offer' elements do not render and are not clickable.,rocketnews24.com - https://github.com/duckduckgo/privacy-configuration/issues/846" + ] }, { "rule": "pubads.g.doubleclick.net/gampad/ads", "domains": [ "crunchyroll.com", - "fifa.com", "nhl.com", "rocketnews24.com", "viki.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1185,rocketnews24.com - https://github.com/duckduckgo/privacy-configuration/issues/846,crunchyroll.com - https://github.com/duckduckgo/privacy-configuration/issues/1140" + ] }, { "rule": "pubads.g.doubleclick.net/ssai/event/", "domains": [ "cbssports.com", "rocketnews24.com" - ], - "reason": "cbssports.com - Live videos do not load or render.,rocketnews24.com - https://github.com/duckduckgo/privacy-configuration/issues/846" + ] + }, + { + "rule": "pubads.g.doubleclick.net/ssai/pods/", + "domains": [ + "foxweather.com" + ] }, { "rule": "securepubads.g.doubleclick.net/tag/js/gpt.js", @@ -5310,8 +5192,7 @@ "rocketnews24.com", "wunderground.com", "youmath.it" - ], - "reason": "ah.nl - 'Bonus offer' elements do not render and are not clickable.,nytimes.com - https://github.com/duckduckgo/privacy-configuration/issues/1045,wunderground.com - Video element does not display.,youmath.it - Adwall displays which prevents page interaction and resets the page view when clicked.,rocketnews24.com - https://github.com/duckduckgo/privacy-configuration/issues/846" + ] }, { "rule": "securepubads.g.doubleclick.net/gpt/pubads_impl_", @@ -5319,8 +5200,7 @@ "ah.nl", "rocketnews24.com", "wunderground.com" - ], - "reason": "ah.nl - 'Bonus offer' elements do not render and are not clickable.,wunderground.com - Video element does not display.,rocketnews24.com - https://github.com/duckduckgo/privacy-configuration/issues/846" + ] }, { "rule": "securepubads.g.doubleclick.net/pagead/ppub_config", @@ -5328,15 +5208,13 @@ "rocketnews24.com", "weather.com", "wunderground.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/415,rocketnews24.com - https://github.com/duckduckgo/privacy-configuration/issues/846,wunderground.com - https://github.com/duckduckgo/privacy-configuration/issues/956" + ] }, { "rule": "doubleclick.net", "domains": [ "rocketnews24.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/846" + ] } ] }, @@ -5346,8 +5224,7 @@ "rule": "driftt.com", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1107" + ] } ] }, @@ -5360,8 +5237,17 @@ "brooklinen.com", "carters.com", "seatosummit.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/535" + ] + } + ] + }, + "eccmp.com": { + "rules": [ + { + "rule": "eccmp.com/sts/scripts/conversen-SDK.js", + "domains": [ + "pch.com" + ] } ] }, @@ -5371,30 +5257,19 @@ "rule": "scene7.com.edgekey.net/s7viewersdk", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1107" - }, - { - "rule": "alicdn.com.edgekey.net/", - "domains": [ - "aliexpress.com", - "aliexpress.us" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/570" + ] }, { "rule": "nintendo.com.edgekey.net/account/js/common.js", "domains": [ "nintendo.com" - ], - "reason": "CNAME version of nintendo.com exception" + ] }, { "rule": "cdn.agoda.net.edgekey.net/", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/771" + ] } ] }, @@ -5404,8 +5279,7 @@ "rule": "sky.com.edgesuite.net/assets/sky_common.js", "domains": [ "sky.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/342" + ] }, { "rule": "a.espncdn.com.stls.edgesuite.net/", @@ -5417,8 +5291,7 @@ "espn.com.mx", "espn.in", "espnfc.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/750" + ] } ] }, @@ -5428,8 +5301,7 @@ "rule": "ensighten.com", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + ] } ] }, @@ -5439,8 +5311,7 @@ "rule": "ezodn.com/cmp", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/468" + ] } ] }, @@ -5450,15 +5321,13 @@ "rule": "videosvc.ezoic.com/play", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/468" + ] }, { "rule": "video-streaming.ezoic.com", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/468" + ] } ] }, @@ -5468,8 +5337,7 @@ "rule": "g.ezoic.net", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/468" + ] } ] }, @@ -5479,8 +5347,7 @@ "rule": "sf.ezoiccdn.com", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/468" + ] } ] }, @@ -5492,8 +5359,13 @@ "bandsintown.com", "nextdoor.co.uk", "nextdoor.com" - ], - "reason": "bandsintown.com - Ticket page renders blank. With this exception the page redirects to ticketspice.com.,nextdoor.co.uk, nextdoor.com - Facebook login option appears greyed out and cannot be clicked." + ] + }, + { + "rule": "connect.facebook.net/en_US/all.js", + "domains": [ + "nordicwellness.se" + ] } ] }, @@ -5503,8 +5375,7 @@ "rule": "mslc-prod-herokuapp-com.global.ssl.fastly.net/main.8736233213226195.js", "domains": [ "masslottery.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/332" + ] }, { "rule": "ticketmaster4.map.fastly.net/eps-d", @@ -5513,15 +5384,13 @@ "ticketmaster.com", "ticketmaster.com.au", "ticketmaster.com.mx" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/575" + ] }, { "rule": "target-opus.map.fastly.net/", "domains": [ "target.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/635" + ] } ] }, @@ -5530,9 +5399,9 @@ { "rule": "app.five9.com", "domains": [ + "gmsdnv.com", "machiassavings.bank" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1111" + ] } ] }, @@ -5542,8 +5411,7 @@ "rule": "flowplayer.org", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1107" + ] } ] }, @@ -5580,8 +5448,7 @@ "tubi.tv", "tubi.video", "tubitv.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/460" + ] } ] }, @@ -5591,8 +5458,7 @@ "rule": "2a7e9.v.fwmrm.net/ad/g/1", "domains": [ "channel4.com" - ], - "reason": "Unskippable adblock warning when trying to play a video." + ] } ] }, @@ -5602,15 +5468,13 @@ "rule": "api.geetest.com", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/462" + ] }, { "rule": "static.geetest.com", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/462" + ] } ] }, @@ -5620,15 +5484,13 @@ "rule": "gapl.hit.gemius.pl/gplayer.js", "domains": [ "tvp.pl" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/376" + ] }, { "rule": "pro.hit.gemius.pl/gstream.js", "domains": [ "tvp.pl" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/376" + ] } ] }, @@ -5637,9 +5499,8 @@ { "rule": "geoip-js.com/js/apis/geoip2/v2.1/geoip2.js", "domains": [ - "yourrewardcard.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1288" + "" + ] } ] }, @@ -5649,15 +5510,13 @@ "rule": "cdn.getshogun.com", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/450" + ] }, { "rule": "lib.getshogun.com", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/450" + ] } ] }, @@ -5667,23 +5526,21 @@ "rule": "google-analytics.com/analytics.js", "domains": [ "doterra.com", - "easyjet.com" - ], - "reason": "doterra.com - For doterra.com/login/loading, the page shows a loading indicator and never redirects.,easyjet.com - Clicking 'Show Worldwide flights' after entering parameters for a worldwide flight in the flight viewing form does nothing." + "easyjet.com", + "worlddutyfree.com" + ] }, { "rule": "www.google-analytics.com/plugins/ua/ecommerce.js", "domains": [ "doterra.com" - ], - "reason": "For doterra.com/login/loading, the page shows a loading indicator and never redirects." + ] }, { "rule": "www.google-analytics.com/collect", "domains": [ "youmath.it" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1147" + ] } ] }, @@ -5693,8 +5550,7 @@ "rule": "maps.google.co.uk/maps", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/472" + ] } ] }, @@ -5704,64 +5560,77 @@ "rule": "accounts.google.com/o/oauth2/iframerpc", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/489" + ] }, { "rule": "accounts.google.com/o/oauth2/iframe", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/489" + ] }, { "rule": "apis.google.com/js/platform.js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/489" + ] }, { "rule": "apis.google.com/_/scs/abc-static/_/js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/489" + ] }, { "rule": "cse.google.com/cse.js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/475" + ] }, { "rule": "cse.google.com/cse/element/", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/475" + ] }, { "rule": "google.com/cse/cse.js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/475" + ] }, { "rule": "www.google.com/cse/static/", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/475" + ] + }, + { + "rule": "www.google.com/url", + "domains": [ + "" + ] }, { "rule": "www.google.com/maps/", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/472" + ] + } + ] + }, + "googleapis.com": { + "rules": [ + { + "rule": "imasdk.googleapis.com/js/sdkloader/ima3.js", + "domains": [ + "bloomberg.com", + "games.washingtonpost.com", + "metro.co.uk", + "nfl.com", + "paper-io.com", + "rawstory.com", + "usatoday.com" + ] } ] }, @@ -5771,8 +5640,7 @@ "rule": "googleoptimize.com/optimize.js", "domains": [ "motherdenim.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1117" + ] } ] }, @@ -5781,13 +5649,21 @@ { "rule": "pagead2.googlesyndication.com/pagead/js/adsbygoogle.js", "domains": [ + "drakescans.com", "duden.de", "magicgameworld.com", "rocketnews24.com", "youmath.it", "zefoy.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/388,rocketnews24.com - https://github.com/duckduckgo/privacy-configuration/issues/846" + ] + }, + { + "rule": "pagead2.googlesyndication.com/pagead/show_ads.js", + "domains": [ + "luckylandslots.com", + "rocketnews24.com", + "zefoy.com" + ] }, { "rule": "tpc.googlesyndication.com/pagead/js/loader21.html", @@ -5796,16 +5672,14 @@ "rocketnews24.com", "rumble.com", "zefoy.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/388,rocketnews24.com - https://github.com/duckduckgo/privacy-configuration/issues/846" + ] }, { "rule": "googlesyndication.com", "domains": [ "rocketnews24.com", "zefoy.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/388,rocketnews24.com - https://github.com/duckduckgo/privacy-configuration/issues/846" + ] } ] }, @@ -5815,8 +5689,7 @@ "rule": "googletagmanager.com/gtag/js", "domains": [ "abril.com.br" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/929" + ] } ] }, @@ -5940,6 +5813,7 @@ "thecw38.com", "thecw46.com", "thecwtc.com", + "thenationaldesk.com", "turnto10.com", "univisionseattle.com", "upnorthlive.com", @@ -5967,8 +5841,7 @@ "wutv29.com", "wvah.com", "wwmt.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1173" + ] } ] }, @@ -5978,15 +5851,23 @@ "rule": "tags.asics.com.greylabeldelivery.com", "domains": [ "asics.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/379" + ] }, { "rule": "tags.focus.de.greylabeldelivery.com/focus-web/prod/utag.js", "domains": [ "focus.de" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1043" + ] + } + ] + }, + "groovehq.com": { + "rules": [ + { + "rule": "widget.cluster.groovehq.com/_next/static/chunks/", + "domains": [ + "" + ] } ] }, @@ -5996,15 +5877,13 @@ "rule": "maps.gstatic.com", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/472" + ] }, { "rule": "www.gstatic.com/_/mss/boq-identity/_/js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/489" + ] } ] }, @@ -6014,8 +5893,7 @@ "rule": "cdn.heapanalytics.com", "domains": [ "mejuri.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1081" + ] } ] }, @@ -6025,8 +5903,23 @@ "rule": "htlbid.com/v3/dangerousminds.net/htlbid.js", "domains": [ "dangerousminds.net" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1270" + ] + } + ] + }, + "hubspot.com": { + "rules": [ + { + "rule": "api.hubspot.com/livechat-public/v1/message/public", + "domains": [ + "pippintitle.com" + ] + }, + { + "rule": "no-cache.hubspot.com/cta/default/", + "domains": [ + "" + ] } ] }, @@ -6036,8 +5929,7 @@ "rule": "mpsnare.iesnare.com/snare.js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/669" + ] } ] }, @@ -6047,8 +5939,7 @@ "rule": "iheart.com", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/760" + ] } ] }, @@ -6059,8 +5950,7 @@ "domains": [ "cox.com", "cox.net" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/948" + ] } ] }, @@ -6070,36 +5960,31 @@ "rule": "inq.com/chatrouter", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/479" + ] }, { "rule": "inq.com/chatskins", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/479" + ] }, { "rule": "inq.com/tagserver/init", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/479" + ] }, { "rule": "inq.com/tagserver/launch", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/479" + ] }, { "rule": "inq.com/tagserver/postToServer", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/479" + ] } ] }, @@ -6109,16 +5994,14 @@ "rule": "platform.instagram.com/en_US/embeds.js", "domains": [ "livejournal.com" - ], - "reason": "Instagram embeds as main article content render as grey boxes." + ] }, { "rule": "www.instagram.com/embed.js", "domains": [ "buzzfeed.com", "livejournal.com" - ], - "reason": "Instagram embeds as main article content render as grey boxes." + ] } ] }, @@ -6128,8 +6011,7 @@ "rule": "api.ipify.org/", "domains": [ "mass.gov" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1239" + ] } ] }, @@ -6139,19 +6021,33 @@ "rule": "assets.jimstatic.com", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/496" + ] + } + ] + }, + "jsdelivr.net": { + "rules": [ + { + "rule": "cdn.jsdelivr.net/npm/@fingerprintjs/fingerprintjs@3/dist/fp.js", + "domains": [ + "sbermarket.ru" + ] } ] }, "kampyle.com": { "rules": [ + { + "rule": "nebula-cdn.kampyle.com/wu/392339/onsite/embed.js", + "domains": [ + "lowes.com" + ] + }, { "rule": "kampyle.com", "domains": [ "basspro.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/783" + ] } ] }, @@ -6161,22 +6057,19 @@ "rule": "na-library.klarnaservices.com/lib.js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1266" + ] }, { "rule": "eu-library.klarnaservices.com/lib.js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1266" + ] }, { "rule": "osm.library.klarnaservices.com/lib.js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1266" + ] } ] }, @@ -6187,15 +6080,21 @@ "domains": [ "fearofgod.com", "shopyalehome.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/362" + ] }, { "rule": "static.klaviyo.com/onsite/js/klaviyo.js", "domains": [ - "kidsguide.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1277" + "essentialpraxis.com", + "kidsguide.com", + "urbanebikes.com" + ] + }, + { + "rule": "a.klaviyo.com/media/js/onsite/onsite.js", + "domains": [ + "tanglefree.com" + ] }, { "rule": "klaviyo.com/", @@ -6203,8 +6102,7 @@ "andieswim.com", "footweartruth.com", "kmail-lists.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/362" + ] } ] }, @@ -6214,8 +6112,13 @@ "rule": "www.lightboxcdn.com/vendor/c605dbd7-cbfb-4e9b-801e-387b0656384c/user.js", "domains": [ "andieswim.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1285" + ] + }, + { + "rule": "lightboxcdn.com/vendor/.*/user.js", + "domains": [ + "nascar.com" + ] } ] }, @@ -6225,8 +6128,18 @@ "rule": "cdn.listrakbi.com/scripts/script.js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/457" + ] + } + ] + }, + "live.primis.tech": { + "rules": [ + { + "rule": "live.primis.tech/live/liveView.php", + "domains": [ + "belfastlive.co.uk", + "cornwalllive.com" + ] } ] }, @@ -6236,8 +6149,7 @@ "rule": "livechatinc.com", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/412" + ] } ] }, @@ -6247,8 +6159,7 @@ "rule": "liveperson.net", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/900" + ] } ] }, @@ -6258,8 +6169,7 @@ "rule": "cloudfront.loggly.com/js/loggly.tracker-2.1.min.js", "domains": [ "rte.ie" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/681" + ] } ] }, @@ -6269,8 +6179,7 @@ "rule": "lpsnmedia.net", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/900" + ] } ] }, @@ -6281,8 +6190,7 @@ "domains": [ "shein.co.uk", "shein.com" - ], - "reason": "When attempting to create an account (after filling out registration form and clicking 'Register'), there is an 'access timed out' warning, and account creation does not proceed." + ] } ] }, @@ -6292,8 +6200,7 @@ "rule": "service.maxymiser.net", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1030" + ] } ] }, @@ -6303,15 +6210,13 @@ "rule": "cdn.medallia.com/react-surveys/", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/884" + ] }, { "rule": "survey.medallia.com/", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/884" + ] } ] }, @@ -6321,8 +6226,7 @@ "rule": "frontend.medicare.gov/static/js/2.6c6651b4.chunk.js", "domains": [ "medicare.gov" - ], - "reason": "Navigation bar at top of site does not display, preventing easy access to e.g., site login.,Note that this CNAMEs to iservprod.medicare.gov.edgekey.net at time of mitiagtion." + ] } ] }, @@ -6332,8 +6236,7 @@ "rule": "memberful.com/embed.js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1107" + ] } ] }, @@ -6343,8 +6246,17 @@ "rule": "monetate.net", "domains": [ "kleen-ritecorp.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1231" + ] + } + ] + }, + "myfonts.net": { + "rules": [ + { + "rule": "hello.myfonts.net/count/3f4e3a", + "domains": [ + "ottolenghi.co.uk" + ] } ] }, @@ -6354,8 +6266,7 @@ "rule": "nc0.co/vaa/Bootstrap.js", "domains": [ "virginatlantic.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/826" + ] } ] }, @@ -6365,8 +6276,7 @@ "rule": "nextdoor.com", "domains": [ "nextdoor.co.uk" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/460" + ] } ] }, @@ -6376,8 +6286,27 @@ "rule": "cdn.accounts.nintendo.com/account/js/common.js", "domains": [ "nintendo.com" - ], - "reason": "Accounts page renders blank. Download buttons show loading stars and never finish loading. Pricing information doesn't load. Note that cdn.accounts.nintendo.com CNAMEs to star.accounts.nintendo.com.edgekey.net at the time of mitigation." + ] + } + ] + }, + "nosto.com": { + "rules": [ + { + "rule": "connect.nosto.com/script/shopify/nosto.js", + "domains": [ + "oneill.com" + ] + } + ] + }, + "npttech.com": { + "rules": [ + { + "rule": "npttech.com/advertising.js", + "domains": [ + "blick.ch" + ] } ] }, @@ -6387,8 +6316,7 @@ "rule": "nuance.com", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/479" + ] } ] }, @@ -6398,8 +6326,7 @@ "rule": "omappapi.com", "domains": [ "dogfoodadvisor.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1113" + ] } ] }, @@ -6409,8 +6336,7 @@ "rule": "hb-api.omnitagjs.com/hb-api/prebid/v1", "domains": [ "aternos.org" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/328" + ] } ] }, @@ -6420,22 +6346,19 @@ "rule": "bankofamerica.tt.omtrdc.net/m2/bankofamerica/mbox/json", "domains": [ "bankofamerica.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/798" + ] }, { "rule": "cigna.sc.omtrdc.net/public/digital-experience/js/common.js", "domains": [ "cigna.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/820" + ] }, { "rule": "omtrdc.net", "domains": [ "pizzahut.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/805" + ] } ] }, @@ -6445,8 +6368,7 @@ "rule": "static.onlyfans.com", "domains": [ "onlyfans.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/440" + ] } ] }, @@ -6456,8 +6378,7 @@ "rule": "venatusmedia-d.openx.net/w/1.0/arj", "domains": [ "aternos.org" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/328" + ] } ] }, @@ -6467,9 +6388,9 @@ "rule": "secure.widget.cloud.opta.net/v3/v3.opta-widgets.js", "domains": [ "abc.net.au", - "emol.com" - ], - "reason": "abc.net.au - Error message displays in place of scoreboard (scoreboard does not show).,emol.com - Scoreboard does not render." + "emol.com", + "oufc.co.uk" + ] } ] }, @@ -6479,8 +6400,7 @@ "rule": "optimizely.com/datafiles/", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/916" + ] } ] }, @@ -6490,8 +6410,13 @@ "rule": "dsar.api.osano.com/", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/529" + ] + }, + { + "rule": "cmp.osano.com", + "domains": [ + "" + ] } ] }, @@ -6501,15 +6426,13 @@ "rule": "go.pardot.com", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1180" + ] }, { "rule": "storage.pardot.com", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1180" + ] } ] }, @@ -6519,8 +6442,17 @@ "rule": "patreon.com/becomePatronButton.bundle.js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1107" + ] + } + ] + }, + "permutive.app": { + "rules": [ + { + "rule": "edge.permutive.app", + "domains": [ + "globaltv.com" + ] } ] }, @@ -6530,8 +6462,7 @@ "rule": "plotrabbit.com", "domains": [ "cbssports.com" - ], - "reason": "Live videos do not load or render." + ] } ] }, @@ -6541,15 +6472,13 @@ "rule": "images.primaryarms.com/f00000000191638/www.primaryarms.com/SSP%20Applications/NetSuite%20Inc.%20-%20SCA%20Mont%20Blanc/Development/img/", "domains": [ "primaryarms.com" - ], - "reason": "Product images on the main page don't render.,Note that this CNAMEs to marvel-b4-cdn.bc0a.com at time of mitigation." + ] }, { "rule": "images.primaryarms.com/f00000000191638/www.primaryarms.com/core/media/media.nl", "domains": [ "primaryarms.com" - ], - "reason": "Images in the large scrolling image banner on main page do not render.,Note that this CNAMEs to marvel-b4-cdn.bc0a.com at time of mitigation." + ] } ] }, @@ -6559,8 +6488,7 @@ "rule": "sdk.privacy-center.org", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/339" + ] } ] }, @@ -6570,8 +6498,7 @@ "rule": "privacy-mgmt.com/", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/344" + ] } ] }, @@ -6581,106 +6508,91 @@ "rule": "cmp-consent-tool.privacymanager.io/latest/605.js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/334" + ] }, { "rule": "cmp-consent-tool.privacymanager.io/latest/650.js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/334" + ] }, { "rule": "cmp-consent-tool.privacymanager.io/latest/847.js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/334" + ] }, { "rule": "cmp-consent-tool.privacymanager.io/latest/assets/icons/.*.svg", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/334" + ] }, { "rule": "cmp-consent-tool.privacymanager.io/latest/defaultTheme.css", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/334" + ] }, { "rule": "cmp-consent-tool.privacymanager.io/latest/index.html", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/334" + ] }, { "rule": "cmp-consent-tool.privacymanager.io/latest/main.js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/334" + ] }, { "rule": "cmp-consent-tool.privacymanager.io/latest/polyfills.js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/334" + ] }, { "rule": "cmp-consent-tool.privacymanager.io/latest/runtime.js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/334" + ] }, { "rule": "cmp-consent-tool.privacymanager.io/latest/vendor.js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/334" + ] }, { "rule": "gdpr.privacymanager.io/1/gdpr.bundle.js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/334" + ] }, { "rule": "gdpr.privacymanager.io/latest/gdpr.bundle.js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/334" + ] }, { "rule": "gdpr-wrapper.privacymanager.io/gdpr/.*/gdpr-liveramp.js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/334" + ] }, { "rule": "gdpr-wrapper.privacymanager.io/gdpr/.*/manager-logo.png", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/334" + ] }, { "rule": "vendors.privacymanager.io/vendor-list.json", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/334" + ] } ] }, @@ -6690,8 +6602,17 @@ "rule": "proofpoint.com/v2/url", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1107" + ] + } + ] + }, + "protection-widget.route.com": { + "rules": [ + { + "rule": "protection-widget.route.com/protect.core.js", + "domains": [ + "littleunicorn.com" + ] } ] }, @@ -6701,15 +6622,13 @@ "rule": "ads.pubmatic.com/AdServer/", "domains": [ "hindustantimes.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/388" + ] }, { "rule": "hbopenbid.pubmatic.com/translator", "domains": [ "aternos.org" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/328" + ] } ] }, @@ -6719,8 +6638,7 @@ "rule": "qualtrics.com", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1196" + ] } ] }, @@ -6730,8 +6648,7 @@ "rule": "cmp.quantcast.com", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1125" + ] } ] }, @@ -6741,8 +6658,7 @@ "rule": "secure.quantserve.com/quant.js", "domains": [ "aternos.org" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/328" + ] } ] }, @@ -6752,15 +6668,13 @@ "rule": "embed.reddit.com/", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/369" + ] }, { "rule": "gql.reddit.com/", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/369" + ] } ] }, @@ -6770,8 +6684,7 @@ "rule": "redditstatic.com/shreddit/", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/369" + ] } ] }, @@ -6781,8 +6694,17 @@ "rule": "rncdn7.com", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/436" + ] + } + ] + }, + "rokt.com": { + "rules": [ + { + "rule": "rokt.com/wsdk/integrations/snippet.js", + "domains": [ + "pch.com" + ] } ] }, @@ -6792,8 +6714,7 @@ "rule": "rumble.com/j/p/ui.r2.js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/631" + ] } ] }, @@ -6803,8 +6724,7 @@ "rule": "ak.sail-horizon.com/spm/spm.v1.min.js", "domains": [ "financialpost.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/785" + ] } ] }, @@ -6814,15 +6734,13 @@ "rule": "scene7.com/is/image/", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/356" + ] }, { "rule": "scene7.com/s7viewersdk", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/356" + ] } ] }, @@ -6832,8 +6750,7 @@ "rule": "searchspring.io", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/435" + ] } ] }, @@ -6843,8 +6760,7 @@ "rule": "cdn.segment.com", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/484" + ] } ] }, @@ -6854,8 +6770,7 @@ "rule": "shop.app/pay/session", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1107" + ] } ] }, @@ -6865,8 +6780,7 @@ "rule": "deo.shopeemobile.com/shopee", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/442" + ] } ] }, @@ -6876,15 +6790,13 @@ "rule": "shopifyapps.com/locale_bar/script.js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1107" + ] }, { "rule": "shopifyapps.com/selectors/script.js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1107" + ] } ] }, @@ -6894,8 +6806,7 @@ "rule": "cdn.shortpixel.ai/assets/js/bundles/spai-lib-bg", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/712" + ] } ] }, @@ -6905,8 +6816,7 @@ "rule": "snapkit.com/js/v1/create.js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1107" + ] } ] }, @@ -6916,8 +6826,7 @@ "rule": "cdn.speedcurve.com/js/lux.js", "domains": [ "inquirer.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/869" + ] } ] }, @@ -6927,8 +6836,7 @@ "rule": "sp-spiegel-de.spiegel.de/", "domains": [ "spiegel.de" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/344" + ] } ] }, @@ -6938,8 +6846,7 @@ "rule": "spot.im/", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/622" + ] } ] }, @@ -6949,8 +6856,27 @@ "rule": "strpst.com", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/438" + ] + } + ] + }, + "succeedscene.com": { + "rules": [ + { + "rule": "succeedscene.com", + "domains": [ + "" + ] + } + ] + }, + "taboola.com": { + "rules": [ + { + "rule": "cdn.taboola.com/libtrc/tipranks-tipranks/loader.js", + "domains": [ + "tipranks.com" + ] } ] }, @@ -6960,8 +6886,7 @@ "rule": "visitor-service-us-east-1.tealiumiq.com/asics/main/", "domains": [ "asics.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/379" + ] } ] }, @@ -6971,8 +6896,7 @@ "rule": "techlab-cdn.com/collect", "domains": [ "jcrew.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1094" + ] } ] }, @@ -6982,8 +6906,7 @@ "rule": "theplatform.com", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1107" + ] } ] }, @@ -6993,33 +6916,49 @@ "rule": "tidiochat.com", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1107" + ] + } + ] + }, + "tiktok.com": { + "rules": [ + { + "rule": "www.tiktok.com/embed", + "domains": [ + "" + ] } ] }, "tiqcdn.com": { "rules": [ { - "rule": "tags.tiqcdn.com/utag/bofa/main/prod/utag.sync.js", + "rule": "tags.tiqcdn.com/utag/.*/utag.js", "domains": [ - "bankofamerica.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/798" + "" + ] }, { - "rule": "tags.tiqcdn.com/utag/lgi/vm-uk/prod/utag.77.js", + "rule": "tags.tiqcdn.com/utag/.*/utag..*.js", "domains": [ - "virginmedia.com" - ], - "reason": "Chat button appears faded and cannot be interacted with." + "" + ] }, { - "rule": "tags.tiqcdn.com/utag/rccl/gdp/prod/utag.js", + "rule": "tags.tiqcdn.com/utag/", + "domains": [ + "" + ] + } + ] + }, + "trackjs.com": { + "rules": [ + { + "rule": "cdn.trackjs.com/agent/v3/latest/t.js", "domains": [ - "royalcaribbean.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1255" + "delta.com" + ] } ] }, @@ -7029,8 +6968,7 @@ "rule": "widget.trustpilot.com/bootstrap/v5/tp.widget.bootstrap.min.js", "domains": [ "domesticandgeneral.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/466" + ] } ] }, @@ -7040,15 +6978,13 @@ "rule": "platform.twitter.com/embed/embed", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/717" + ] }, { "rule": "platform.twitter.com/widgets/tweet_button", "domains": [ "winnipegfreepress.com" - ], - "reason": "Tweet button does not render." + ] } ] }, @@ -7058,15 +6994,13 @@ "rule": "api.usabilla.com", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/482" + ] }, { "rule": "w.usabilla.com", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/482" + ] } ] }, @@ -7076,57 +7010,49 @@ "rule": "api.usercentrics.eu/settings", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/477" + ] }, { "rule": "api.usercentrics.eu/tcf", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/477" + ] }, { "rule": "api.usercentrics.eu/translations", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/477" + ] }, { "rule": "app.usercentrics.eu/browser", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/477" + ] }, { "rule": "app.usercentrics.eu/session/", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/477" + ] }, { "rule": "graphql.usercentrics.eu/graphql", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/477" + ] }, { "rule": "privacy-proxy.usercentrics.eu/latest/", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/477" + ] }, { "rule": "aggregator.service.usercentrics.eu/aggregate", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/477" + ] } ] }, @@ -7136,8 +7062,7 @@ "rule": "cdn.viafoura.net", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/763" + ] } ] }, @@ -7147,8 +7072,7 @@ "rule": "cdn.viglink.com/api/vglnk.js", "domains": [ "9to5mac.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1204" + ] } ] }, @@ -7158,8 +7082,7 @@ "rule": "voxmedia.com/sso/unison_request", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1107" + ] } ] }, @@ -7169,8 +7092,7 @@ "rule": "js.wpadmngr.com/static/adManager.js", "domains": [ "luscious.net" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/958" + ] } ] }, @@ -7180,22 +7102,25 @@ "rule": "frontend.vh.yandex.ru/player/", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/366" + ] }, { "rule": "strm.yandex.ru/get/", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/366" + ] }, { "rule": "strm.yandex.ru/vh-special-converted/vod-content/", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/366" + ] + }, + { + "rule": "yandex.ru/map-widget/", + "domains": [ + "" + ] } ] }, @@ -7205,8 +7130,7 @@ "rule": "yotpo.com", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/348" + ] } ] }, @@ -7216,22 +7140,25 @@ "rule": "cdn.yottaa.com/rapid.min.", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/492" + ] }, { "rule": "cdn.yottaa.com/rapid.security.min.", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/830" + ] }, { "rule": "rapid-cdn.yottaa.com/rapid/lib/ows8CdAyrC5lTw.js", "domains": [ "scheels.com" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/492" + ] + }, + { + "rule": "rapid-cdn.yottaa.com/rapid/lib/3ZzYwky2C-3YQw.js", + "domains": [ + "fashionnova.com" + ] } ] }, @@ -7241,8 +7168,7 @@ "rule": "vjs.zencdn.net", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1107" + ] } ] }, @@ -7252,8 +7178,7 @@ "rule": "seattle-times.zeustechnology.com/main.js", "domains": [ "seattletimes.com" - ], - "reason": "An unskippable adwall appears which prevents interaction with the page." + ] } ] }, @@ -7263,8 +7188,17 @@ "rule": "zip.co/v1/quadpay.js", "domains": [ "" - ], - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1107" + ] + } + ] + }, + "zopim.com": { + "rules": [ + { + "rule": "zopim.com", + "domains": [ + "" + ] } ] }, @@ -7274,8 +7208,7 @@ "rule": "lp-03.chat.online.citi.com", "domains": [ "" - ], - "reason": "CNAME ENTRY GENERATED FROM: liveperson.net" + ] } ] }, @@ -7285,8 +7218,7 @@ "rule": "lp-07.customermessaging.bupa.com.au", "domains": [ "" - ], - "reason": "CNAME ENTRY GENERATED FROM: liveperson.net" + ] } ] }, @@ -7296,15 +7228,13 @@ "rule": "lp-07.messaging.optus.com.au", "domains": [ "" - ], - "reason": "CNAME ENTRY GENERATED FROM: liveperson.net" + ] }, { "rule": "lptag.messaging.optus.com.au", "domains": [ "" - ], - "reason": "CNAME ENTRY GENERATED FROM: liveperson.net" + ] } ] }, @@ -7314,8 +7244,7 @@ "rule": "evaluation.canadapost-postescanada.ca", "domains": [ "" - ], - "reason": "CNAME ENTRY GENERATED FROM: qualtrics.com" + ] } ] }, @@ -7325,8 +7254,17 @@ "rule": "feedback.goto.com", "domains": [ "" - ], - "reason": "CNAME ENTRY GENERATED FROM: qualtrics.com" + ] + } + ] + }, + "yandex.tm": { + "rules": [ + { + "rule": "mc.yandex.tm/map-widget/", + "domains": [ + "" + ] } ] } @@ -7334,23 +7272,19 @@ }, "exceptions": [ { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" + "domain": "earth.google.com" }, { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "iscorp.com" }, { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" + "domain": "marvel.com" }, { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "sundancecatalog.com" } ], - "hash": "f9b5279848f985f769d973e76d09b28b" + "hash": "f5e54d051c76c97e2ebaf3014037d10a" }, "trackingCookies1p": { "settings": { @@ -7361,24 +7295,20 @@ }, "exceptions": [ { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" + "domain": "earth.google.com" }, { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "iscorp.com" }, { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" + "domain": "marvel.com" }, { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "sundancecatalog.com" } ], "state": "disabled", - "hash": "0bea41340334fc80820a598f8b892e30" + "hash": "4dddf681372a2aea9788090b13db6e6f" }, "trackingCookies3p": { "settings": { @@ -7386,46 +7316,37 @@ }, "exceptions": [ { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" + "domain": "earth.google.com" }, { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "iscorp.com" }, { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" + "domain": "marvel.com" }, { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "sundancecatalog.com" } ], "state": "disabled", - "hash": "81f357de4bfc7abeddf1a3a3fb1fc7a9" + "hash": "841fa92b9728c9754f050662678f82c7" }, "trackingParameters": { "exceptions": [ { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" + "domain": "earth.google.com" }, { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "iscorp.com" }, { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" + "domain": "marvel.com" }, { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "sundancecatalog.com" }, { - "domain": "theverge.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1326" + "domain": "theverge.com" } ], "settings": { @@ -7458,7 +7379,7 @@ ] }, "state": "enabled", - "hash": "aab19f2fd45ded0765912ee266c309df" + "hash": "f2437495da4898e8e048643ab38ef372" }, "userAgentRotation": { "settings": { @@ -7466,24 +7387,20 @@ }, "exceptions": [ { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" + "domain": "earth.google.com" }, { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "iscorp.com" }, { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" + "domain": "marvel.com" }, { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "sundancecatalog.com" } ], "state": "disabled", - "hash": "9775f6a60d1271671e6ef7a7cd2811be" + "hash": "f65d10dfdf6739feab99a08d42734747" }, "voiceSearch": { "exceptions": [], @@ -7493,20 +7410,16 @@ "webCompat": { "exceptions": [ { - "domain": "earth.google.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1099" + "domain": "earth.google.com" }, { - "domain": "iscorp.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" + "domain": "iscorp.com" }, { - "domain": "marvel.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1194" + "domain": "marvel.com" }, { - "domain": "paramountplus.com", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/1085" + "domain": "sundancecatalog.com" } ], "state": "enabled", @@ -7539,13 +7452,31 @@ } ] }, - "hash": "40742df45658ad78c016f5a99178482d" + "hash": "4e94cff79e689ff320de22a57e242bdd" }, "windowsPermissionUsage": { "exceptions": [], "state": "disabled", "hash": "728493ef7a1488e4781656d3f9db84aa" }, + "windowsStartupBoost": { + "exceptions": [ + { + "domain": "earth.google.com" + }, + { + "domain": "iscorp.com" + }, + { + "domain": "marvel.com" + }, + { + "domain": "sundancecatalog.com" + } + ], + "state": "disabled", + "hash": "5e792dd491428702bc0104240fbce0ce" + }, "windowsWaitlist": { "exceptions": [], "state": "enabled", @@ -7555,22 +7486,7 @@ "exceptions": [], "state": "enabled", "hash": "52857469413a66e8b0c7b00de5589162" - }, - "incrementalRolloutTest2": { - "state": "enabled", - "features": { - "rollout": { - "state": "disabled" - } - }, - "exceptions": [], - "hash": "54776d4aa010391dc14a53ad69cd1777" } }, - "unprotectedTemporary": [ - { - "domain": "vinted.fr", - "reason": "https://github.com/duckduckgo/privacy-configuration/issues/794" - } - ] + "unprotectedTemporary": [] } \ No newline at end of file diff --git a/Core/trackerData.json b/Core/trackerData.json index 3de737a946..84975e1a36 100644 --- a/Core/trackerData.json +++ b/Core/trackerData.json @@ -1,7 +1,7 @@ { "_builtWith": { - "tracker-radar": "6eb8582c70d4e62ccb4b23113fc8bfdeb9b945b8ba986a60e71ab693af87e755-4013b4e91930c643394cb31c6c745356f133b04f", - "tracker-surrogates": "d61691a2fdf9f4dc062a8d248fd1e78c20b5b892" + "tracker-radar": "ae2fbc01abd26abf1903208a5cabbaed6a96ab2aa13779d2125a98fc45fee2cd-4013b4e91930c643394cb31c6c745356f133b04f", + "tracker-surrogates": "abd6067fac9693cc5a43d48931b111ca08cb0d5a" }, "readme": "https://github.com/duckduckgo/tracker-blocklists", "trackers": { @@ -136,45 +136,14 @@ "Ad Motivated Tracking", "Advertising" ], - "default": "ignore", + "default": "block", "rules": [ { - "rule": "2mdn\\.net/dot\\.gif" - }, - { - "rule": "2mdn\\.net/10533936/1679336805544/" - }, - { - "rule": "2mdn\\.net/8762481/1679421215390" - }, - { - "rule": "2mdn\\.net/879366/" - }, - { - "rule": "2mdn\\.net/ads/" - }, - { - "rule": "2mdn\\.net/creatives/assets/" - }, - { - "rule": "2mdn\\.net/dfp/" - }, - { - "rule": "2mdn\\.net/dynamic/2/" - }, - { - "rule": "2mdn\\.net/sadbundle/" - }, - { - "rule": "2mdn\\.net/simgad/[0-9]+" - }, - { - "rule": "2mdn\\.net/videoplayback" + "rule": "2mdn\\.net/instream/html5/ima3\\.js", + "surrogate": "google-ima.js" }, { "rule": "2mdn\\.net/instream/video/client\\.js", - "fingerprinting": 1, - "cookies": 0, "exceptions": { "domains": [ "dailywire.com", @@ -482,7 +451,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -493,7 +462,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -539,7 +508,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -822,7 +791,17 @@ "Social - Share", "Third-Party Analytics Marketing" ], - "default": "block" + "default": "block", + "rules": [ + { + "rule": "static\\.addtoany\\.com/menu/page\\.js", + "exceptions": { + "domains": [ + "tiny.cc" + ] + } + } + ] }, "adelixir.com": { "domain": "adelixir.com", @@ -2417,7 +2396,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -2428,7 +2407,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -2472,7 +2451,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -2483,7 +2462,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -2506,7 +2485,7 @@ "default": "block", "rules": [ { - "rule": "amazon-adsystem\\.com\\/aax2\\/amzn_ads\\.js", + "rule": "amazon-adsystem\\.com/aax2/amzn_ads\\.js", "surrogate": "amzn_ads.js" }, { @@ -2519,9 +2498,16 @@ "foxbusiness.com", "foxnews.com", "fyi.tv", + "gamingbible.co.uk", + "gamingbible.com", "history.com", + "ladbible.com", "mylifetime.com", - "sueddeutsche.de" + "sportbible.com", + "sueddeutsche.de", + "tyla.com", + "unilad.co.uk", + "unilad.com" ] } } @@ -2610,7 +2596,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -2749,7 +2735,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -2760,7 +2746,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -3079,7 +3065,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -3206,7 +3192,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -3330,7 +3316,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -3714,7 +3700,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -3725,7 +3711,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -3736,7 +3722,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -3747,7 +3733,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -3813,7 +3799,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -3865,7 +3851,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -3876,7 +3862,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -4013,7 +3999,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -4364,7 +4350,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -4399,7 +4385,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -4474,14 +4460,7 @@ "fingerprinting": 2, "cookies": 0.000824, "categories": [], - "default": "ignore", - "rules": [ - { - "rule": "boldchat\\.com/aid/.*/vms\\.js", - "fingerprinting": 2, - "cookies": 0.000201 - } - ] + "default": "ignore" }, "booking.com": { "domain": "booking.com", @@ -4850,7 +4829,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -4861,7 +4840,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -5063,7 +5042,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -5105,7 +5084,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -5128,7 +5107,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -5163,7 +5142,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -5192,7 +5171,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -5480,7 +5459,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -5505,11 +5484,11 @@ "default": "block", "rules": [ { - "rule": "chartbeat\\.com\\/chartbeat\\.js", + "rule": "chartbeat\\.com/chartbeat\\.js", "surrogate": "chartbeat.js" }, { - "rule": "chartbeat\\.com\\/js\\/chartbeat\\.js", + "rule": "chartbeat\\.com/js/chartbeat\\.js", "surrogate": "chartbeat.js" } ] @@ -5597,7 +5576,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -5608,7 +5587,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -5619,7 +5598,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -5630,7 +5609,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -5667,7 +5646,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -5722,7 +5701,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -6258,7 +6237,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -6417,7 +6396,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -6733,7 +6712,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -6744,7 +6723,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -6868,7 +6847,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -7064,7 +7043,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -7100,7 +7079,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -7111,7 +7090,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -7122,7 +7101,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -7705,7 +7684,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -7716,7 +7695,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -7727,7 +7706,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -7761,9 +7740,14 @@ "default": "ignore", "rules": [ { - "rule": "dbukjj6eu5tsf\\.cloudfront\\.net\\/assets\\.sidearmsports\\.com\\/common\\/js\\/20170825\\/video\\.js", + "rule": "dbukjj6eu5tsf\\.cloudfront\\.net/assets\\.sidearmsports\\.com/common/js/20170825/video\\.js", "fingerprinting": 3, - "cookies": 0 + "cookies": 0, + "exceptions": { + "domains": [ + "utsports.com" + ] + } } ] }, @@ -7803,7 +7787,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -7926,7 +7910,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -7937,7 +7921,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -8015,7 +7999,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -8026,7 +8010,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -8141,7 +8125,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -8214,7 +8198,7 @@ "surrogate": "gpt.js" }, { - "rule": "doubleclick\\.net\\/tag\\/js\\/gpt\\.js", + "rule": "doubleclick\\.net/tag/js/gpt\\.js", "surrogate": "gpt.js" }, { @@ -8257,6 +8241,14 @@ "sbs.com.au" ] } + }, + { + "rule": "g\\.doubleclick\\.net/pagead/ads", + "exceptions": { + "domains": [ + "fukuishimbun.co.jp" + ] + } } ] }, @@ -8284,7 +8276,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -8295,7 +8287,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -8821,7 +8813,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -8832,7 +8824,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -8873,7 +8865,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -9289,7 +9281,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -9825,11 +9817,6 @@ ], "default": "ignore", "rules": [ - { - "rule": "facebook\\.net/.*/all\\.js", - "fingerprinting": 1, - "cookies": 0.0000408 - }, { "rule": "facebook\\.net/.*/fbevents\\.js", "fingerprinting": 1, @@ -9899,7 +9886,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -9910,7 +9897,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -9921,7 +9908,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -9961,7 +9948,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -10003,7 +9990,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -10077,7 +10064,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -10145,7 +10132,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -10156,7 +10143,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -10204,7 +10191,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -10330,7 +10317,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -10490,7 +10477,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -10501,7 +10488,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -10512,7 +10499,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -10539,7 +10526,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -10589,7 +10576,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -10636,7 +10623,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -10814,14 +10801,7 @@ "fingerprinting": 1, "cookies": 0.0000408, "categories": [], - "default": "ignore", - "rules": [ - { - "rule": "geoip-js\\.com\\/js\\/apis\\/geoip2\\/v2\\.1\\/geoip2\\.js", - "fingerprinting": 1, - "cookies": 0.0000204 - } - ] + "default": "ignore" }, "getblue.io": { "domain": "getblue.io", @@ -11115,7 +11095,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -11155,7 +11135,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -11220,7 +11200,7 @@ "default": "block", "rules": [ { - "rule": "google-analytics\\.com\\/ga\\.js", + "rule": "google-analytics\\.com/ga\\.js", "surrogate": "ga.js" }, { @@ -11235,19 +11215,19 @@ } }, { - "rule": "google-analytics\\.com\\/inpage_linkid\\.js", + "rule": "google-analytics\\.com/inpage_linkid\\.js", "surrogate": "inpage_linkid.js" }, { - "rule": "google-analytics\\.com\\/plugins\\/ga\\/inpage_linkid\\.js", + "rule": "google-analytics\\.com/plugins/ga/inpage_linkid\\.js", "surrogate": "inpage_linkid.js" }, { - "rule": "google-analytics\\.com\\/cx\\/api\\.js", + "rule": "google-analytics\\.com/cx/api\\.js", "surrogate": "api.js" }, { - "rule": "google-analytics\\.com\\/gtm\\/js", + "rule": "google-analytics\\.com/gtm/js", "surrogate": "gtm.js" } ] @@ -11502,7 +11482,33 @@ "Ad Motivated Tracking", "Advertising" ], - "default": "block" + "default": "ignore", + "rules": [ + { + "rule": "googleadservices\\.com/gampad/cookie\\.js" + }, + { + "rule": "googleadservices\\.com/pagead/conversion\\.js" + }, + { + "rule": "googleadservices\\.com/gampad/google_service\\.js" + }, + { + "rule": "googleadservices\\.com/gampad/google_ads\\.js" + }, + { + "rule": "googleadservices\\.com/favicon\\.ico" + }, + { + "rule": "googleadservices\\.com/ga/phone" + }, + { + "rule": "googleadservices\\.com/ccm/conversion/845940546/" + }, + { + "rule": "googleadservices\\.com/pagead/conversion/" + } + ] }, "googlehosted.com": { "domain": "googlehosted.com", @@ -11616,12 +11622,31 @@ "default": "block", "rules": [ { - "rule": "googlesyndication\\.com\\/adsbygoogle\\.js", + "rule": "googlesyndication\\.com/adsbygoogle\\.js", "surrogate": "adsbygoogle.js" }, { - "rule": "googlesyndication\\.com\\/pagead\\/js\\/adsbygoogle\\.js", + "rule": "pagead2\\.googlesyndication\\.com/pagead/js/adsbygoogle\\.js", + "surrogate": "adsbygoogle.js", + "exceptions": { + "domains": [ + "dronedj.com", + "fukuishimbun.co.jp", + "spaceexplored.com" + ] + } + }, + { + "rule": "googlesyndication\\.com/pagead/js/adsbygoogle\\.js", "surrogate": "adsbygoogle.js" + }, + { + "rule": "pagead2\\.googlesyndication\\.com/pagead/managed/js/adsense", + "exceptions": { + "domains": [ + "fukuishimbun.co.jp" + ] + } } ] }, @@ -11815,7 +11840,7 @@ "default": "block", "rules": [ { - "rule": "googletagservices\\.com\\/gpt\\.js", + "rule": "googletagservices\\.com/gpt\\.js", "surrogate": "gpt.js" }, { @@ -11848,7 +11873,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -11935,7 +11960,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -11946,7 +11971,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -11986,11 +12011,6 @@ "rule": "groovehq\\.com\\/api\\/shim\\/27299f7da6676b065f217a683a418325", "fingerprinting": 2, "cookies": 0 - }, - { - "rule": "groovehq\\.com\\/_next\\/static\\/chunks\\/9fd8c5e27f99fce506e2e5d3b010ddba7982b0f2\\.7fb5a86b2706698b7a7e\\.js", - "fingerprinting": 2, - "cookies": 0 } ] }, @@ -12178,7 +12198,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -12218,7 +12238,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -12229,7 +12249,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -12240,7 +12260,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -12251,7 +12271,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -12577,7 +12597,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -12588,7 +12608,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -12599,7 +12619,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -12908,14 +12928,6 @@ ] } }, - { - "rule": "hubspot\\.com/api/livechat-public/v1/", - "exceptions": { - "types": [ - "xmlhttprequest" - ] - } - }, { "rule": "hubspot\\.com/hubfs/", "exceptions": { @@ -13255,7 +13267,13 @@ "Content Delivery", "Embedded Content" ], - "default": "ignore" + "default": "ignore", + "rules": [ + { + "rule": "imasdk\\.googleapis\\.com/js/sdkloader/ima3\\.js", + "surrogate": "google-ima.js" + } + ] }, "imedia.cz": { "domain": "imedia.cz", @@ -14240,18 +14258,6 @@ } ] }, - "ipify.org": { - "domain": "ipify.org", - "owner": { - "name": "ipify.org", - "displayName": "ipify.org" - }, - "prevalence": 0.00555, - "fingerprinting": 1, - "cookies": 0.000116, - "categories": [], - "default": "block" - }, "iplsc.com": { "domain": "iplsc.com", "owner": { @@ -14522,7 +14528,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -15207,7 +15213,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -15282,7 +15288,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -15333,7 +15339,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -15484,25 +15490,7 @@ "Embedded Content", "Third-Party Analytics Marketing" ], - "default": "ignore", - "rules": [ - { - "rule": "lightboxcdn\\.com/vendor/.*/user\\.js", - "fingerprinting": 3, - "cookies": 0.0000749 - }, - { - "rule": "lightboxcdn\\.com\\/w37htfhcq2\\/vendor\\/721dac7b-a982-4a33-afe7-ffe31144fbdd\\/user\\.js", - "fingerprinting": 3, - "cookies": 0.0000136 - }, - { - "rule": "lightboxcdn\\.com\\/vendor\\/b90a8cf1-793b-4cc5-b282-f863e44f81fa\\/lightbox_inline\\.js", - "fingerprinting": 0, - "cookies": 0, - "comment": "pixel" - } - ] + "default": "ignore" }, "lijit.com": { "domain": "lijit.com", @@ -15653,7 +15641,60 @@ "Ad Motivated Tracking", "Advertising" ], - "default": "block" + "default": "ignore", + "rules": [ + { + "rule": "linksynergy\\.com/rcs" + }, + { + "rule": "linksynergy\\.com/jsp" + }, + { + "rule": "linksynergy\\.com/cs" + }, + { + "rule": "linksynergy\\.com/act\\.php" + }, + { + "rule": "linksynergy\\.com/t" + }, + { + "rule": "linksynergy\\.com/consent/v1/p" + }, + { + "rule": "linksynergy\\.com/imp" + }, + { + "rule": "linksynergy\\.com/consent/v3/p" + }, + { + "rule": "linksynergy\\.com/phoenix/phoenix-2\\.26\\.min\\.js" + }, + { + "rule": "linksynergy\\.com/privacy/ad_choices\\.png" + }, + { + "rule": "linksynergy\\.com/fs-bin/show" + }, + { + "rule": "linksynergy\\.com/cc" + }, + { + "rule": "linksynergy\\.com/js/" + }, + { + "rule": "linksynergy\\.com/wakeup/" + }, + { + "rule": "linksynergy\\.com/advertisers/owllab9118/" + }, + { + "rule": "linksynergy\\.com/pix/[0-9]+" + }, + { + "rule": "linksynergy\\.com/[0-9]+\\.ct\\.js" + } + ] }, "list-manage.com": { "domain": "list-manage.com", @@ -16155,7 +16196,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -16166,7 +16207,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -16194,7 +16235,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -16413,7 +16454,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -16454,7 +16495,8 @@ "Embedded Content", "Social - Share" ], - "default": "ignore" + "default": "ignore", + "rules": [] }, "mailchimp.com": { "domain": "mailchimp.com", @@ -16721,7 +16763,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -16732,7 +16774,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -16917,7 +16959,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -16966,7 +17008,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -17240,7 +17282,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -17251,7 +17293,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -17814,7 +17856,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -18041,7 +18083,20 @@ "fingerprinting": 0, "cookies": 0.00373, "categories": [], - "default": "block" + "default": "block", + "rules": [ + { + "rule": "hello\\.myfonts\\.net/count/", + "exceptions": { + "domains": [ + "condor.com" + ], + "types": [ + "stylesheet" + ] + } + } + ] }, "myregistry.com": { "domain": "myregistry.com", @@ -18105,7 +18160,30 @@ "fingerprinting": 2, "cookies": 0.00189, "categories": [], - "default": "block" + "default": "ignore", + "rules": [ + { + "rule": "nakanohito\\.jp/b3/bi\\.js" + }, + { + "rule": "nakanohito\\.jp/b3/" + }, + { + "rule": "nakanohito\\.jp/uhj2/uh\\.js" + }, + { + "rule": "nakanohito\\.jp/cm/inner\\.css" + }, + { + "rule": "nakanohito\\.jp/ua/uwa\\.js" + }, + { + "rule": "nakanohito\\.jp/ua/" + }, + { + "rule": "nakanohito\\.jp/chatbot_pc\\.css" + } + ] }, "nanorep.co": { "domain": "nanorep.co", @@ -18126,7 +18204,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -18265,7 +18343,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -18359,6 +18437,15 @@ ], "default": "block", "rules": [ + { + "rule": "js-agent\\.newrelic\\.com", + "exceptions": { + "domains": [ + "chaturbate.com", + "johnlewis.com" + ] + } + }, { "rule": "newrelic\\.com", "exceptions": { @@ -18615,7 +18702,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -18712,11 +18799,7 @@ "rules": [ { "rule": "npttech\\.com/advertising\\.js", - "exceptions": { - "types": [ - "script" - ] - } + "surrogate": "noop.js" } ] }, @@ -18734,7 +18817,17 @@ "categories": [ "Analytics" ], - "default": "block" + "default": "block", + "rules": [ + { + "rule": "nr-data\\.net", + "exceptions": { + "domains": [ + "chaturbate.com" + ] + } + } + ] }, "nrich.ai": { "domain": "nrich.ai", @@ -18845,7 +18938,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -20023,7 +20116,7 @@ "default": "block", "rules": [ { - "rule": "outbrain\\.com\\/outbrain\\.js", + "rule": "outbrain\\.com/outbrain\\.js", "surrogate": "outbrain.js" } ] @@ -20034,7 +20127,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -20255,7 +20348,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -20266,7 +20359,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -20277,7 +20370,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -20377,7 +20470,39 @@ "Audience Measurement", "Third-Party Analytics Marketing" ], - "default": "block" + "default": "ignore", + "rules": [ + { + "rule": "parsely\\.com/plogger/" + }, + { + "rule": "parsely\\.com/videoplugins/brightcove/videojs-parsely-loader-v1-latest\\.min\\.js" + }, + { + "rule": "parsely\\.com/videoplugins/brightcove/videojs-parsely-v1-latest\\.min\\.js" + }, + { + "rule": "parsely\\.com/v2/profile" + }, + { + "rule": "parsely\\.com/px/" + }, + { + "rule": "parsely\\.com/v2/related" + }, + { + "rule": "parsely\\.com/v2/analytics/posts" + }, + { + "rule": "parsely\\.com/v2/similar" + }, + { + "rule": "parsely\\.com/keys/adweek\\.com/p\\.js" + }, + { + "rule": "parsely\\.com/v2/search" + } + ] }, "partplanes.com": { "domain": "partplanes.com", @@ -20385,7 +20510,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -20592,7 +20717,17 @@ "fingerprinting": 2, "cookies": 0.00574, "categories": [], - "default": "block" + "default": "block", + "rules": [ + { + "rule": "edge\\.permutive\\.app", + "exceptions": { + "domains": [ + "globaltv.com" + ] + } + } + ] }, "permutive.com": { "domain": "permutive.com", @@ -20862,7 +20997,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -20891,7 +21026,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -20902,7 +21037,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -20913,7 +21048,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -20977,7 +21112,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -21057,7 +21192,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -21105,7 +21240,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -21116,7 +21251,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -21133,11 +21268,6 @@ "categories": [], "default": "ignore", "rules": [ - { - "rule": "powr\\.io\\/powr\\.js", - "fingerprinting": 1, - "cookies": 0.00000681 - }, { "rule": "powr\\.io\\/popup\\/u\\/61af4f5f_1680291259", "fingerprinting": 1, @@ -21161,7 +21291,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -21172,7 +21302,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -21207,7 +21337,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -21218,7 +21348,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -21351,7 +21481,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -21409,14 +21539,7 @@ "fingerprinting": 1, "cookies": 0.000197, "categories": [], - "default": "ignore", - "rules": [ - { - "rule": "providesupport\\.com\\/sjs\\/static\\.js", - "fingerprinting": 1, - "cookies": 0.000123 - } - ] + "default": "ignore" }, "pswec.com": { "domain": "pswec.com", @@ -21442,7 +21565,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -21913,7 +22036,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -21924,7 +22047,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -21935,7 +22058,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -22005,7 +22128,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -22016,7 +22139,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -22169,7 +22292,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -22180,7 +22303,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -22222,7 +22345,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -22233,7 +22356,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -22244,7 +22367,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -22514,7 +22637,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -22525,7 +22648,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -22586,7 +22709,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -22644,7 +22767,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -22655,7 +22778,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -22755,7 +22878,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -22778,7 +22901,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -22789,7 +22912,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -23122,7 +23245,23 @@ "Ad Motivated Tracking", "Advertising" ], - "default": "block" + "default": "block", + "rules": [ + { + "rule": "micro\\.rubiconproject\\.com/prebid/dynamic/.*\\.js", + "exceptions": { + "domains": [ + "gamingbible.co.uk", + "gamingbible.com", + "ladbible.com", + "sportbible.com", + "tyla.com", + "unilad.co.uk", + "unilad.com" + ] + } + } + ] }, "ruralrobin.com": { "domain": "ruralrobin.com", @@ -23130,7 +23269,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -23234,7 +23373,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -23631,7 +23770,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -23706,7 +23845,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -23717,7 +23856,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -23761,7 +23900,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -23772,7 +23911,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -23783,7 +23922,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -23794,7 +23933,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -23874,7 +24013,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -23896,7 +24035,7 @@ "default": "block", "rules": [ { - "rule": "scorecardresearch\\.com\\/beacon\\.js", + "rule": "scorecardresearch\\.com/beacon\\.js", "surrogate": "beacon.js" } ] @@ -23907,7 +24046,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -23958,7 +24097,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -24094,7 +24233,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -24223,7 +24362,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -24234,7 +24373,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -24481,7 +24620,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -24492,7 +24631,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -24503,7 +24642,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -24784,7 +24923,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -24819,7 +24958,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -24860,7 +24999,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -24976,7 +25115,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -24987,7 +25126,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25016,7 +25155,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25112,7 +25251,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25169,7 +25308,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25180,7 +25319,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25301,7 +25440,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25458,7 +25597,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25484,7 +25623,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25495,7 +25634,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25558,7 +25697,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25569,7 +25708,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25580,7 +25719,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25811,7 +25950,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25838,7 +25977,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25849,7 +25988,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25860,7 +25999,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25890,7 +26029,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25901,7 +26040,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25929,7 +26068,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25940,7 +26079,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -25973,7 +26112,7 @@ "cookies": 0.0000136 }, { - "rule": "storage\\.googleapis\\.com/code\\.snapengage\\.com/", + "rule": "storage\\.googleapis\\.com/code\\.snapengage\\.com/js/", "fingerprinting": 3, "cookies": 0.0000136 }, @@ -26028,7 +26167,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -26076,7 +26215,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -26087,7 +26226,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -26098,7 +26237,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -26109,7 +26248,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -26120,7 +26259,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -26131,7 +26270,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -26203,7 +26342,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -26214,7 +26353,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -26268,7 +26407,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -26470,7 +26609,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -26778,7 +26917,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -26789,7 +26928,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -26800,7 +26939,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -27051,7 +27190,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -27062,7 +27201,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -27133,7 +27272,60 @@ "fingerprinting": 1, "cookies": 0.0436, "categories": [], - "default": "block" + "default": "ignore", + "rules": [ + { + "rule": "tiktok\\.com/i18n/pixel/events\\.js" + }, + { + "rule": "tiktok\\.com/i18n/pixel/static/identify_d1af3\\.js" + }, + { + "rule": "tiktok\\.com/api/v2/pixel" + }, + { + "rule": "tiktok\\.com/i18n/pixel/sdk\\.js" + }, + { + "rule": "tiktok\\.com/api/v2/performance_interaction" + }, + { + "rule": "tiktok\\.com/i18n/pixel/config\\.js" + }, + { + "rule": "tiktok\\.com/i18n/pixel/disable_cookie" + }, + { + "rule": "tiktok\\.com/i18n/pixel/static/identify_821f6\\.js" + }, + { + "rule": "tiktok\\.com/v1/user/webid" + }, + { + "rule": "tiktok\\.com/service/2/abtest_config/" + }, + { + "rule": "tiktok\\.com/web/resource" + }, + { + "rule": "tiktok\\.com/v1/list" + }, + { + "rule": "tiktok\\.com/web/report" + }, + { + "rule": "tiktok\\.com/oembed" + }, + { + "rule": "tiktok\\.com/i18n/pixel/enable_cookie" + }, + { + "rule": "tiktok\\.com/i18n/pixel/static/identify_a7248\\.js" + }, + { + "rule": "tiktok\\.com/i18n/pixel/static/main\\..*\\.js" + } + ] }, "tinypass.com": { "domain": "tinypass.com", @@ -27427,7 +27619,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -28284,7 +28476,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -28414,7 +28606,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -28425,7 +28617,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -28558,7 +28750,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -28641,7 +28833,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -28652,7 +28844,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -29012,7 +29204,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -29123,7 +29315,18 @@ "categories": [ "Advertising" ], - "default": "block" + "default": "block", + "rules": [ + { + "rule": "cdn\\.viglink\\.com/api/vglnk\\.js", + "exceptions": { + "domains": [ + "9to5mac.com", + "electrek.co" + ] + } + } + ] }, "visualwebsiteoptimizer.com": { "domain": "visualwebsiteoptimizer.com", @@ -29308,7 +29511,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -29319,7 +29522,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -29445,7 +29648,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -29456,7 +29659,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -29856,6 +30059,14 @@ "categories": [], "default": "ignore", "rules": [ + { + "rule": "universal\\.wgplayer\\.com/tag/", + "exceptions": { + "domains": [ + "yoho.games" + ] + } + }, { "rule": "wgplayer\\.com\\/tag\\/", "fingerprinting": 1, @@ -31190,7 +31401,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -31264,21 +31475,6 @@ "categories": [], "default": "block" }, - "zopim.com": { - "domain": "zopim.com", - "owner": { - "name": "Zendesk, Inc.", - "displayName": "Zendesk", - "privacyPolicy": "https://www.zendesk.com/company/customers-partners/privacy-policy/" - }, - "prevalence": 0.0078, - "fingerprinting": 1, - "cookies": 0.00153, - "categories": [ - "Embedded Content" - ], - "default": "block" - }, "zprk.io": { "domain": "zprk.io", "owner": { @@ -31494,7 +31690,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32654,7 +32850,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32665,7 +32861,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32676,7 +32872,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32687,7 +32883,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32698,7 +32894,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32709,7 +32905,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32720,7 +32916,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32731,7 +32927,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "adamantsnail.com": { + "domain": "adamantsnail.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32742,7 +32949,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32753,7 +32960,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32764,7 +32971,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32775,7 +32982,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32786,7 +32993,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32797,7 +33004,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32808,7 +33015,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32819,7 +33026,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32830,7 +33037,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32841,7 +33048,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32852,7 +33059,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32863,7 +33070,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32874,7 +33081,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32885,7 +33092,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32896,7 +33103,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32907,7 +33114,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32918,7 +33125,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32929,7 +33136,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32940,7 +33147,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32951,7 +33158,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32962,7 +33169,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32973,7 +33180,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32984,7 +33191,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -32995,7 +33202,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33006,7 +33213,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33017,7 +33224,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33028,7 +33235,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33039,7 +33246,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33050,7 +33257,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33061,7 +33268,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33072,7 +33279,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33083,7 +33290,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33094,7 +33301,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33105,7 +33312,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33116,7 +33323,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33127,7 +33334,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33138,7 +33345,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33149,7 +33356,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33160,7 +33367,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33171,7 +33378,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33182,7 +33389,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33193,7 +33400,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33204,7 +33411,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33215,7 +33422,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33226,7 +33433,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33237,7 +33444,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33248,7 +33455,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33259,7 +33466,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33270,7 +33477,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33281,7 +33488,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33292,7 +33499,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33303,7 +33510,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33314,7 +33521,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33325,7 +33532,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33336,7 +33543,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33347,7 +33554,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33358,7 +33565,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "chalkoil.com": { + "domain": "chalkoil.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33369,7 +33587,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33380,7 +33598,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33391,7 +33609,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33402,7 +33620,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33413,7 +33631,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33424,7 +33642,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33435,7 +33653,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33446,7 +33664,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33457,7 +33675,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33468,7 +33686,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33479,7 +33697,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33490,7 +33708,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33501,7 +33719,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33512,7 +33730,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33523,7 +33741,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33534,7 +33752,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33545,7 +33763,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33556,7 +33774,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33567,7 +33785,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33578,7 +33796,29 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "cozyhillside.com": { + "domain": "cozyhillside.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0144, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "crimsonmeadow.com": { + "domain": "crimsonmeadow.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33589,7 +33829,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33600,7 +33840,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "crystalboulevard.com": { + "domain": "crystalboulevard.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33611,7 +33862,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33622,7 +33873,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33633,7 +33884,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33644,7 +33895,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33655,7 +33906,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33666,7 +33917,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33677,7 +33928,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33688,7 +33939,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33699,7 +33950,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33710,7 +33961,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33721,7 +33972,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33732,7 +33983,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "delicatecascade.com": { + "domain": "delicatecascade.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33743,7 +34005,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33754,7 +34016,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33765,7 +34027,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33776,7 +34038,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33787,7 +34049,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33798,7 +34060,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33809,7 +34071,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33820,7 +34082,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33831,7 +34093,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33842,7 +34104,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33853,7 +34115,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33864,7 +34126,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33875,7 +34137,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33886,7 +34148,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "ethereallagoon.com": { + "domain": "ethereallagoon.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33897,7 +34170,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33908,7 +34181,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33919,7 +34192,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33930,7 +34203,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33941,7 +34214,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33952,7 +34225,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33963,7 +34236,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33974,7 +34247,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33985,7 +34258,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -33996,7 +34269,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34007,7 +34280,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34018,7 +34291,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34029,7 +34302,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34040,7 +34313,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34051,7 +34324,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34062,7 +34335,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34073,7 +34346,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34084,7 +34357,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34095,7 +34368,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34106,7 +34379,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34117,7 +34390,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34128,7 +34401,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34139,7 +34412,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "forgetfulsnail.com": { + "domain": "forgetfulsnail.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34150,7 +34434,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34161,7 +34445,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34172,7 +34456,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34183,7 +34467,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34194,7 +34478,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34205,7 +34489,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34216,7 +34500,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34227,7 +34511,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34238,7 +34522,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34249,7 +34533,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34260,7 +34544,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34271,7 +34555,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34282,7 +34566,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34293,7 +34577,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34304,7 +34588,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34315,7 +34599,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34326,7 +34610,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34337,7 +34621,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34348,7 +34632,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34359,7 +34643,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34370,7 +34654,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34381,7 +34665,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34392,7 +34676,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34403,7 +34687,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34414,7 +34698,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34425,7 +34709,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34436,7 +34720,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34447,7 +34731,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34458,7 +34742,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "headydegree.com": { + "domain": "headydegree.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34469,7 +34764,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34480,7 +34775,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34491,7 +34786,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34502,7 +34797,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34513,7 +34808,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34524,7 +34819,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34535,7 +34830,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34546,7 +34841,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34557,7 +34852,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34568,7 +34863,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34579,7 +34874,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34590,7 +34885,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34601,7 +34896,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34612,7 +34907,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34623,7 +34918,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34634,7 +34929,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "jubilantcanyon.com": { + "domain": "jubilantcanyon.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34645,7 +34951,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34656,7 +34962,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34667,7 +34973,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34678,7 +34984,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34689,7 +34995,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34700,7 +35006,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34711,7 +35017,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34722,7 +35028,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34733,7 +35039,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34744,7 +35050,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34755,7 +35061,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34766,7 +35072,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34777,7 +35083,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34788,7 +35094,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34799,7 +35105,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34810,7 +35116,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34821,7 +35127,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34832,7 +35138,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34843,7 +35149,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34854,7 +35160,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34865,7 +35171,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34876,7 +35182,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34887,7 +35193,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34898,7 +35204,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34909,7 +35215,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34920,7 +35226,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34931,7 +35237,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34942,7 +35248,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34953,7 +35259,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34964,7 +35270,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34975,7 +35281,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34986,7 +35292,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -34997,7 +35303,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35008,7 +35314,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35019,7 +35325,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35030,7 +35336,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35041,7 +35347,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35052,7 +35358,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35063,7 +35369,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35074,7 +35380,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35085,7 +35391,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35096,7 +35402,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35107,7 +35413,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35118,7 +35424,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35129,7 +35435,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35140,7 +35446,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35151,7 +35457,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35162,7 +35468,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35173,7 +35479,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35184,7 +35490,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35195,7 +35501,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35206,7 +35512,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35217,7 +35523,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35228,7 +35534,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35239,7 +35545,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35250,7 +35556,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35261,7 +35567,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35272,7 +35578,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35283,7 +35589,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35294,7 +35600,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35305,7 +35611,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35316,7 +35622,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35327,7 +35633,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35338,7 +35644,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35349,7 +35655,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35360,7 +35666,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35371,7 +35677,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35382,7 +35688,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35393,7 +35699,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35404,7 +35710,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35415,7 +35721,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35426,7 +35732,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35437,7 +35743,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35448,7 +35754,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35459,7 +35765,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35470,7 +35776,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35481,7 +35787,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35492,7 +35798,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35503,7 +35809,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35514,7 +35820,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35525,7 +35831,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35536,7 +35842,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35547,7 +35853,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35558,7 +35864,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35569,7 +35875,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35580,7 +35886,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35591,7 +35897,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35602,7 +35908,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35613,7 +35919,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35624,7 +35930,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35635,7 +35941,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35646,7 +35952,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35657,7 +35963,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35668,7 +35974,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35679,7 +35985,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35690,7 +35996,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35701,7 +36007,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35712,7 +36018,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35723,7 +36029,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35734,7 +36040,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35745,7 +36051,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35756,7 +36062,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35767,7 +36073,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35778,7 +36084,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35789,7 +36095,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35800,7 +36106,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35811,7 +36117,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35822,7 +36128,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35833,7 +36139,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35844,7 +36150,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35855,7 +36161,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35866,7 +36172,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35877,7 +36183,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35888,7 +36194,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35899,7 +36205,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35910,7 +36216,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35921,7 +36227,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35932,7 +36238,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35943,7 +36249,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35954,7 +36260,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35965,7 +36271,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35976,7 +36282,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35987,7 +36293,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -35998,7 +36304,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "smilingswim.com": { + "domain": "smilingswim.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36009,7 +36326,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36020,7 +36337,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36031,7 +36348,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36042,7 +36359,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36053,7 +36370,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36064,7 +36381,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36075,7 +36392,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36086,7 +36403,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36097,7 +36414,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36108,7 +36425,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36119,7 +36436,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36130,7 +36447,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36141,7 +36458,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36152,7 +36469,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36163,7 +36480,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36174,7 +36491,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36185,7 +36502,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36196,7 +36513,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36207,7 +36524,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36218,7 +36535,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36229,7 +36546,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36240,7 +36557,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36251,7 +36568,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36262,7 +36579,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36273,7 +36590,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36284,7 +36601,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36295,7 +36612,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36306,7 +36623,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36317,7 +36634,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "succeedscene.com": { + "domain": "succeedscene.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36328,7 +36656,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36339,7 +36667,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36350,7 +36678,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36361,7 +36689,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36372,7 +36700,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36383,7 +36711,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36394,7 +36722,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36405,7 +36733,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36416,7 +36744,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36427,7 +36755,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36438,7 +36766,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36449,7 +36777,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36460,7 +36788,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36471,7 +36799,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36482,7 +36810,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36493,7 +36821,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36504,7 +36832,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36515,7 +36843,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36526,7 +36854,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36537,7 +36865,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36548,7 +36876,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36559,7 +36887,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36570,7 +36898,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36581,7 +36909,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36592,7 +36920,18 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, + "fingerprinting": 1, + "cookies": 0.01, + "default": "block" + }, + "tranquilcanyon.com": { + "domain": "tranquilcanyon.com", + "owner": { + "name": "Leven Labs, Inc. DBA Admiral", + "displayName": "Admiral" + }, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36603,7 +36942,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36614,7 +36953,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36625,7 +36964,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36636,7 +36975,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36647,7 +36986,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36658,7 +36997,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36669,7 +37008,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36680,7 +37019,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36691,7 +37030,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36702,7 +37041,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36713,7 +37052,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36724,7 +37063,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36735,7 +37074,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36746,7 +37085,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36757,7 +37096,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36768,7 +37107,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36779,7 +37118,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36790,7 +37129,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36801,7 +37140,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36812,7 +37151,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36823,7 +37162,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36834,7 +37173,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36845,7 +37184,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36856,7 +37195,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36867,7 +37206,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36878,7 +37217,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36889,7 +37228,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36900,7 +37239,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36911,7 +37250,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -36922,7 +37261,7 @@ "name": "Leven Labs, Inc. DBA Admiral", "displayName": "Admiral" }, - "prevalence": 0.0138, + "prevalence": 0.0144, "fingerprinting": 1, "cookies": 0.01, "default": "block" @@ -42132,13 +42471,6 @@ "prevalence": 0.0926, "displayName": "iPerceptions" }, - "ipify.org": { - "domains": [ - "ipify.org" - ], - "prevalence": 0.0163, - "displayName": "ipify.org" - }, "Grupa Interia.pl Sp. z o.o. sp. k.": { "domains": [ "adretail.pl", @@ -46961,6 +47293,7 @@ "actoramusement.com", "actuallysnake.com", "actuallything.com", + "adamantsnail.com", "adorableanger.com", "adventurousamount.com", "agreeablearch.com", @@ -47045,6 +47378,7 @@ "cautiouscherries.com", "cautiouscredit.com", "ceciliavenus.com", + "chalkoil.com", "chargecracker.com", "charmingplate.com", "cherriescare.com", @@ -47074,11 +47408,14 @@ "consciousdirt.com", "courageousbaby.com", "coverapparatus.com", + "cozyhillside.com", "crabbychin.com", "cratecamera.com", + "crimsonmeadow.com", "critictruck.com", "crookedcreature.com", "crowdedmass.com", + "crystalboulevard.com", "cubchannel.com", "cumbersomecarpenter.com", "currentcollar.com", @@ -47099,6 +47436,7 @@ "decisivedrawer.com", "decisiveducks.com", "deerbeginner.com", + "delicatecascade.com", "detailedkitten.com", "detectdiscovery.com", "devilishdinner.com", @@ -47123,6 +47461,7 @@ "entertainskin.com", "enviousshape.com", "equablekettle.com", + "ethereallagoon.com", "evanescentedge.com", "eventexistence.com", "exampleshake.com", @@ -47156,6 +47495,7 @@ "floweryflavor.com", "flutteringfireman.com", "followborder.com", + "forgetfulsnail.com", "fortunatemark.com", "frailfruit.com", "franticroof.com", @@ -47202,6 +47542,7 @@ "haplessland.com", "harborcaption.com", "hatefulrequest.com", + "headydegree.com", "heartbreakingmind.com", "hearthorn.com", "heavyplayground.com", @@ -47222,6 +47563,7 @@ "inquisitiveice.com", "internalsink.com", "j93557g.com", + "jubilantcanyon.com", "kaputquill.com", "knitstamp.com", "knottyswing.com", @@ -47429,6 +47771,7 @@ "slopesoap.com", "smashquartz.com", "smashsurprise.com", + "smilingswim.com", "smoggysnakes.com", "smoggysongs.com", "soggysponge.com", @@ -47479,6 +47822,7 @@ "stupendoussleet.com", "stupendoussnow.com", "stupidscene.com", + "succeedscene.com", "sugarfriction.com", "suggestionbridge.com", "sulkycook.com", @@ -47515,6 +47859,7 @@ "tidymitten.com", "tiredthroat.com", "tiresomethunder.com", + "tranquilcanyon.com", "tremendousearthquake.com", "tremendousplastic.com", "tritebadge.com", @@ -47558,7 +47903,7 @@ "zipperxray.com", "zlp6s.pw" ], - "prevalence": 0.0138, + "prevalence": 0.0144, "displayName": "Admiral" } }, @@ -48288,6 +48633,7 @@ "actoramusement.com": "Leven Labs, Inc. DBA Admiral", "actuallysnake.com": "Leven Labs, Inc. DBA Admiral", "actuallything.com": "Leven Labs, Inc. DBA Admiral", + "adamantsnail.com": "Leven Labs, Inc. DBA Admiral", "adorableanger.com": "Leven Labs, Inc. DBA Admiral", "adventurousamount.com": "Leven Labs, Inc. DBA Admiral", "agreeablearch.com": "Leven Labs, Inc. DBA Admiral", @@ -48372,6 +48718,7 @@ "cautiouscherries.com": "Leven Labs, Inc. DBA Admiral", "cautiouscredit.com": "Leven Labs, Inc. DBA Admiral", "ceciliavenus.com": "Leven Labs, Inc. DBA Admiral", + "chalkoil.com": "Leven Labs, Inc. DBA Admiral", "chargecracker.com": "Leven Labs, Inc. DBA Admiral", "charmingplate.com": "Leven Labs, Inc. DBA Admiral", "cherriescare.com": "Leven Labs, Inc. DBA Admiral", @@ -48401,11 +48748,14 @@ "consciousdirt.com": "Leven Labs, Inc. DBA Admiral", "courageousbaby.com": "Leven Labs, Inc. DBA Admiral", "coverapparatus.com": "Leven Labs, Inc. DBA Admiral", + "cozyhillside.com": "Leven Labs, Inc. DBA Admiral", "crabbychin.com": "Leven Labs, Inc. DBA Admiral", "cratecamera.com": "Leven Labs, Inc. DBA Admiral", + "crimsonmeadow.com": "Leven Labs, Inc. DBA Admiral", "critictruck.com": "Leven Labs, Inc. DBA Admiral", "crookedcreature.com": "Leven Labs, Inc. DBA Admiral", "crowdedmass.com": "Leven Labs, Inc. DBA Admiral", + "crystalboulevard.com": "Leven Labs, Inc. DBA Admiral", "cubchannel.com": "Leven Labs, Inc. DBA Admiral", "cumbersomecarpenter.com": "Leven Labs, Inc. DBA Admiral", "currentcollar.com": "Leven Labs, Inc. DBA Admiral", @@ -48426,6 +48776,7 @@ "decisivedrawer.com": "Leven Labs, Inc. DBA Admiral", "decisiveducks.com": "Leven Labs, Inc. DBA Admiral", "deerbeginner.com": "Leven Labs, Inc. DBA Admiral", + "delicatecascade.com": "Leven Labs, Inc. DBA Admiral", "detailedkitten.com": "Leven Labs, Inc. DBA Admiral", "detectdiscovery.com": "Leven Labs, Inc. DBA Admiral", "devilishdinner.com": "Leven Labs, Inc. DBA Admiral", @@ -48450,6 +48801,7 @@ "entertainskin.com": "Leven Labs, Inc. DBA Admiral", "enviousshape.com": "Leven Labs, Inc. DBA Admiral", "equablekettle.com": "Leven Labs, Inc. DBA Admiral", + "ethereallagoon.com": "Leven Labs, Inc. DBA Admiral", "evanescentedge.com": "Leven Labs, Inc. DBA Admiral", "eventexistence.com": "Leven Labs, Inc. DBA Admiral", "exampleshake.com": "Leven Labs, Inc. DBA Admiral", @@ -48483,6 +48835,7 @@ "floweryflavor.com": "Leven Labs, Inc. DBA Admiral", "flutteringfireman.com": "Leven Labs, Inc. DBA Admiral", "followborder.com": "Leven Labs, Inc. DBA Admiral", + "forgetfulsnail.com": "Leven Labs, Inc. DBA Admiral", "fortunatemark.com": "Leven Labs, Inc. DBA Admiral", "frailfruit.com": "Leven Labs, Inc. DBA Admiral", "franticroof.com": "Leven Labs, Inc. DBA Admiral", @@ -48529,6 +48882,7 @@ "haplessland.com": "Leven Labs, Inc. DBA Admiral", "harborcaption.com": "Leven Labs, Inc. DBA Admiral", "hatefulrequest.com": "Leven Labs, Inc. DBA Admiral", + "headydegree.com": "Leven Labs, Inc. DBA Admiral", "heartbreakingmind.com": "Leven Labs, Inc. DBA Admiral", "hearthorn.com": "Leven Labs, Inc. DBA Admiral", "heavyplayground.com": "Leven Labs, Inc. DBA Admiral", @@ -48549,6 +48903,7 @@ "inquisitiveice.com": "Leven Labs, Inc. DBA Admiral", "internalsink.com": "Leven Labs, Inc. DBA Admiral", "j93557g.com": "Leven Labs, Inc. DBA Admiral", + "jubilantcanyon.com": "Leven Labs, Inc. DBA Admiral", "kaputquill.com": "Leven Labs, Inc. DBA Admiral", "knitstamp.com": "Leven Labs, Inc. DBA Admiral", "knottyswing.com": "Leven Labs, Inc. DBA Admiral", @@ -48756,6 +49111,7 @@ "slopesoap.com": "Leven Labs, Inc. DBA Admiral", "smashquartz.com": "Leven Labs, Inc. DBA Admiral", "smashsurprise.com": "Leven Labs, Inc. DBA Admiral", + "smilingswim.com": "Leven Labs, Inc. DBA Admiral", "smoggysnakes.com": "Leven Labs, Inc. DBA Admiral", "smoggysongs.com": "Leven Labs, Inc. DBA Admiral", "soggysponge.com": "Leven Labs, Inc. DBA Admiral", @@ -48806,6 +49162,7 @@ "stupendoussleet.com": "Leven Labs, Inc. DBA Admiral", "stupendoussnow.com": "Leven Labs, Inc. DBA Admiral", "stupidscene.com": "Leven Labs, Inc. DBA Admiral", + "succeedscene.com": "Leven Labs, Inc. DBA Admiral", "sugarfriction.com": "Leven Labs, Inc. DBA Admiral", "suggestionbridge.com": "Leven Labs, Inc. DBA Admiral", "sulkycook.com": "Leven Labs, Inc. DBA Admiral", @@ -48842,6 +49199,7 @@ "tidymitten.com": "Leven Labs, Inc. DBA Admiral", "tiredthroat.com": "Leven Labs, Inc. DBA Admiral", "tiresomethunder.com": "Leven Labs, Inc. DBA Admiral", + "tranquilcanyon.com": "Leven Labs, Inc. DBA Admiral", "tremendousearthquake.com": "Leven Labs, Inc. DBA Admiral", "tremendousplastic.com": "Leven Labs, Inc. DBA Admiral", "tritebadge.com": "Leven Labs, Inc. DBA Admiral", @@ -51150,7 +51508,6 @@ "iocnt.net": "INFOnline GmbH", "iper2.com": "iPerceptions Inc.", "iperceptions.com": "iPerceptions Inc.", - "ipify.org": "ipify.org", "adretail.pl": "Grupa Interia.pl Sp. z o.o. sp. k.", "adsearch.pl": "Grupa Interia.pl Sp. z o.o. sp. k.", "bryk.pl": "Grupa Interia.pl Sp. z o.o. sp. k.", diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 149f3fd89c..24454f9b80 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -246,8 +246,12 @@ 31DD208427395A5A008FB313 /* VoiceSearchHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31DD208327395A5A008FB313 /* VoiceSearchHelper.swift */; }; 31E69A63280F4CB600478327 /* DuckUI in Frameworks */ = {isa = PBXBuildFile; productRef = 31E69A62280F4CB600478327 /* DuckUI */; }; 31EF52E1281B3BDC0034796E /* AutofillLoginListItemViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31EF52E0281B3BDC0034796E /* AutofillLoginListItemViewModel.swift */; }; + 373608902ABB1E6C00629E7F /* FavoritesDisplayModeStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3736088F2ABB1E6C00629E7F /* FavoritesDisplayModeStorage.swift */; }; + 373608922ABB430D00629E7F /* FavoritesDisplayMode+UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373608912ABB430D00629E7F /* FavoritesDisplayMode+UserDefaults.swift */; }; + 373608932ABB432600629E7F /* FavoritesDisplayMode+UserDefaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373608912ABB430D00629E7F /* FavoritesDisplayMode+UserDefaults.swift */; }; 37445F972A155F7C0029F789 /* SyncDataProviders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37445F962A155F7C0029F789 /* SyncDataProviders.swift */; }; 3760DFED299315EF0045A446 /* Waitlist in Frameworks */ = {isa = PBXBuildFile; productRef = 3760DFEC299315EF0045A446 /* Waitlist */; }; + 377D80222AB48554002AF251 /* FavoritesDisplayModeSyncHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 377D80212AB48554002AF251 /* FavoritesDisplayModeSyncHandler.swift */; }; 379E877429E97C8D001C8BB0 /* BookmarksCleanupErrorHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 379E877329E97C8D001C8BB0 /* BookmarksCleanupErrorHandling.swift */; }; 37CBCA9E2A8A659C0050218F /* SyncSettingsAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CBCA9D2A8A659C0050218F /* SyncSettingsAdapter.swift */; }; 37CEFCAC2A673B90001EF741 /* CredentialsCleanupErrorHandling.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CEFCAB2A673B90001EF741 /* CredentialsCleanupErrorHandling.swift */; }; @@ -263,6 +267,7 @@ 37FCAAC029930E26000E420A /* FailedAssertionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37FCAABF29930E26000E420A /* FailedAssertionView.swift */; }; 37FD780F2A29E28B00B36DB1 /* SyncErrorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37FD780E2A29E28B00B36DB1 /* SyncErrorHandler.swift */; }; 4B0295192537BC6700E00CEF /* ConfigurationDebugViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B0295182537BC6700E00CEF /* ConfigurationDebugViewController.swift */; }; + 4B274F602AFEAECC003F0745 /* NetworkProtectionWidgetRefreshModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B274F5F2AFEAECC003F0745 /* NetworkProtectionWidgetRefreshModel.swift */; }; 4B2754EC29E8C7DF00394032 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = 4B2754EB29E8C7DF00394032 /* Lottie */; }; 4B470ED6299C49800086EBDC /* AppTrackingProtectionDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B470ED5299C49800086EBDC /* AppTrackingProtectionDatabase.swift */; }; 4B470ED9299C4AED0086EBDC /* AppTrackingProtectionModel.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 4B470ED7299C4AED0086EBDC /* AppTrackingProtectionModel.xcdatamodeld */; }; @@ -271,6 +276,8 @@ 4B470EE4299C6DFB0086EBDC /* Core.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F143C2E41E4A4CD400CFDE3A /* Core.framework */; }; 4B52648B25F9613B00CB4C24 /* trackerData.json in Resources */ = {isa = PBXBuildFile; fileRef = 4B52648A25F9613B00CB4C24 /* trackerData.json */; }; 4B53648A26718D0E001AA041 /* EmailWaitlist.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B53648926718D0E001AA041 /* EmailWaitlist.swift */; }; + 4B5C462A2AF2A6E6002A4432 /* VPNIntents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B5C46292AF2A6E6002A4432 /* VPNIntents.swift */; }; + 4B5C462B2AF2BDC4002A4432 /* VPNIntents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B5C46292AF2A6E6002A4432 /* VPNIntents.swift */; }; 4B60AC97252EC07B00E8D219 /* fullscreenvideo.js in Resources */ = {isa = PBXBuildFile; fileRef = 4B60AC96252EC07B00E8D219 /* fullscreenvideo.js */; }; 4B60ACA1252EC0B100E8D219 /* FullScreenVideoUserScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B60ACA0252EC0B100E8D219 /* FullScreenVideoUserScript.swift */; }; 4B62C4BA25B930DD008912C6 /* AppConfigurationFetchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B62C4B925B930DD008912C6 /* AppConfigurationFetchTests.swift */; }; @@ -287,8 +294,20 @@ 4B83397329AFB8D2003F7EA9 /* AppTrackingProtectionFeedbackModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B83397229AFB8D2003F7EA9 /* AppTrackingProtectionFeedbackModel.swift */; }; 4B83397529AFBCE6003F7EA9 /* AppTrackingProtectionFeedbackModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B83397429AFBCE6003F7EA9 /* AppTrackingProtectionFeedbackModelTests.swift */; }; 4B948E2629DCCDB9002531FA /* Persistence in Frameworks */ = {isa = PBXBuildFile; productRef = 4B948E2529DCCDB9002531FA /* Persistence */; }; + 4BB7CBB02AF59C310014A35F /* VPNWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB7CBAF2AF59C310014A35F /* VPNWidget.swift */; }; + 4BBBBA872B02E85400D965DA /* DesignResourcesKit in Frameworks */ = {isa = PBXBuildFile; productRef = 4BBBBA862B02E85400D965DA /* DesignResourcesKit */; }; + 4BBBBA8D2B031B4200D965DA /* VPNWaitlistDebugViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BBBBA892B031B4200D965DA /* VPNWaitlistDebugViewController.swift */; }; + 4BBBBA8E2B031B4200D965DA /* VPNWaitlistViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BBBBA8A2B031B4200D965DA /* VPNWaitlistViewController.swift */; }; + 4BBBBA8F2B031B4200D965DA /* VPNWaitlistView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BBBBA8B2B031B4200D965DA /* VPNWaitlistView.swift */; }; + 4BBBBA902B031B4200D965DA /* VPNWaitlist.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BBBBA8C2B031B4200D965DA /* VPNWaitlist.swift */; }; + 4BBBBA922B03291700D965DA /* VPNWaitlistUserText.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BBBBA912B03291700D965DA /* VPNWaitlistUserText.swift */; }; 4BC21A2F27238B7500229F0E /* RunLoopExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC21A2C272388BD00229F0E /* RunLoopExtensionTests.swift */; }; 4BC6DD1C2A60E6AD001EC129 /* ReportBrokenSiteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC6DD1B2A60E6AD001EC129 /* ReportBrokenSiteView.swift */; }; + 4BCD14632B05AF2B000B1E4C /* NetworkProtectionAccessController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BCD14622B05AF2B000B1E4C /* NetworkProtectionAccessController.swift */; }; + 4BCD14672B05B682000B1E4C /* NetworkProtectionTermsAndConditionsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BCD14662B05B682000B1E4C /* NetworkProtectionTermsAndConditionsStore.swift */; }; + 4BCD14692B05BDD5000B1E4C /* AppDelegate+Waitlists.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BCD14682B05BDD5000B1E4C /* AppDelegate+Waitlists.swift */; }; + 4BCD146B2B05C4B5000B1E4C /* VPNWaitlistTermsAndConditionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BCD146A2B05C4B5000B1E4C /* VPNWaitlistTermsAndConditionsViewController.swift */; }; + 4BCD146D2B05DB09000B1E4C /* NetworkProtectionAccessControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BCD146C2B05DB09000B1E4C /* NetworkProtectionAccessControllerTests.swift */; }; 4BE2756827304F57006B20B0 /* URLRequestExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BE27566272F878F006B20B0 /* URLRequestExtension.swift */; }; 4BEF65692989C2FC00B650CB /* AdapterSocketEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 021D307A2989C0C400918636 /* AdapterSocketEvent.swift */; }; 4BEF656A2989C2FC00B650CB /* ProxyServerEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 021D307C2989C0C600918636 /* ProxyServerEvent.swift */; }; @@ -374,6 +393,7 @@ 853273B624FFE0BB00E3C778 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8512EA4E24ED30D20073EE19 /* WidgetKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; 8536A1C8209AF2410050739E /* MockVariantManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8536A1C7209AF2410050739E /* MockVariantManager.swift */; }; 8536A1CA209AF6490050739E /* HomeRowReminderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8536A1C9209AF6480050739E /* HomeRowReminderTests.swift */; }; + 8536A1FD2ACF114B003AC5BA /* Theme+DesignSystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8536A1FC2ACF114B003AC5BA /* Theme+DesignSystem.swift */; }; 85371D242121B9D500920548 /* new_tab.json in Resources */ = {isa = PBXBuildFile; fileRef = 85371D232121B9D400920548 /* new_tab.json */; }; 85372447220DD103009D09CD /* UIKeyCommandExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85372446220DD103009D09CD /* UIKeyCommandExtension.swift */; }; 85374D3821AC419800FF5A1E /* NavigationSearchHomeViewSectionRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85374D3721AC419800FF5A1E /* NavigationSearchHomeViewSectionRenderer.swift */; }; @@ -405,6 +425,7 @@ 85514FFD2372DA0100DBC528 /* ios13-home-row.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 85514FFC2372DA0000DBC528 /* ios13-home-row.mp4 */; }; 8551912724746EDC0010FDD0 /* SnapshotHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8551912624746EDC0010FDD0 /* SnapshotHelper.swift */; }; 85582E0029D7409700E9AE35 /* SyncSettingsViewController+PDFRendering.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85582DFF29D7409700E9AE35 /* SyncSettingsViewController+PDFRendering.swift */; }; + 855D45D32ACD7DD1008F7AC6 /* AddressBarPositionSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 855D45D22ACD7DD1008F7AC6 /* AddressBarPositionSettingsViewController.swift */; }; 855D914D2063EF6A00C4B448 /* TabSwitcherTransition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 855D914C2063EF6A00C4B448 /* TabSwitcherTransition.swift */; }; 8563A03C1F9288D600F04442 /* BrowserChromeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8563A03B1F9288D600F04442 /* BrowserChromeManager.swift */; }; 8565A34B1FC8D96B00239327 /* LaunchTabNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8565A34A1FC8D96B00239327 /* LaunchTabNotification.swift */; }; @@ -501,7 +522,6 @@ 981CA7EA2617797500E119D5 /* MainViewController+AddFavoriteFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 981CA7E92617797500E119D5 /* MainViewController+AddFavoriteFlow.swift */; }; 981FED692201FE69008488D7 /* AutoClearSettingsScreenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 981FED682201FE69008488D7 /* AutoClearSettingsScreenTests.swift */; }; 981FED6E22025151008488D7 /* BlankSnapshotViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 981FED6C22025151008488D7 /* BlankSnapshotViewController.swift */; }; - 981FED712202519C008488D7 /* BlankSnapshot.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 981FED702202519C008488D7 /* BlankSnapshot.storyboard */; }; 981FED7422046017008488D7 /* AutoClearTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 981FED7322046017008488D7 /* AutoClearTests.swift */; }; 981FED76220464EF008488D7 /* AutoClearSettingsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 981FED75220464EF008488D7 /* AutoClearSettingsModel.swift */; }; 9820EAF522613CD30089094D /* WebProgressWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9820EAF422613CD30089094D /* WebProgressWorker.swift */; }; @@ -705,14 +725,18 @@ C1B7B52D2894469D0098FD6A /* DefaultVariantManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1B7B52C2894469D0098FD6A /* DefaultVariantManager.swift */; }; C1B7B53028944E390098FD6A /* RemoteMessagingStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1B7B52F28944E390098FD6A /* RemoteMessagingStoreTests.swift */; }; C1B7B53428944EFA0098FD6A /* CoreDataTestUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1B7B53328944EFA0098FD6A /* CoreDataTestUtilities.swift */; }; + C1B924B72ACD6E6800EE7B06 /* AutofillNeverSavedTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1B924B62ACD6E6800EE7B06 /* AutofillNeverSavedTableViewCell.swift */; }; C1BF0BA529B63D7200482B73 /* AutofillLoginPromptHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1BF0BA429B63D7200482B73 /* AutofillLoginPromptHelper.swift */; }; C1BF0BA929B63E2200482B73 /* AutofillLoginPromptViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1BF0BA729B63E1A00482B73 /* AutofillLoginPromptViewModelTests.swift */; }; C1CCCBA7283E101500CF3791 /* FaviconsHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1CCCBA6283E101500CF3791 /* FaviconsHelper.swift */; }; + C1CDA3162AFB9C7F006D1476 /* AutofillNeverPromptWebsitesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1CDA3152AFB9C7F006D1476 /* AutofillNeverPromptWebsitesManager.swift */; }; + C1CDA31E2AFBF811006D1476 /* AutofillNeverPromptWebsitesManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1CDA31D2AFBF811006D1476 /* AutofillNeverPromptWebsitesManagerTests.swift */; }; C1D21E2D293A5965006E5A05 /* AutofillLoginSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1D21E2C293A5965006E5A05 /* AutofillLoginSession.swift */; }; C1D21E2F293A599C006E5A05 /* AutofillLoginSessionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1D21E2E293A599C006E5A05 /* AutofillLoginSessionTests.swift */; }; C1F341C52A6924000032057B /* EmailAddressPromptView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1F341C42A6924000032057B /* EmailAddressPromptView.swift */; }; C1F341C72A6924100032057B /* EmailAddressPromptViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1F341C62A6924100032057B /* EmailAddressPromptViewModel.swift */; }; C1F341C92A6926920032057B /* EmailAddressPromptViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1F341C82A6926920032057B /* EmailAddressPromptViewController.swift */; }; + CB1143DE2AF6D4B600C1CCD3 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = CB1143DC2AF6D4B600C1CCD3 /* InfoPlist.strings */; }; CB258D1229A4F24900DEBA24 /* ConfigurationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB258D0F29A4D0FD00DEBA24 /* ConfigurationManager.swift */; }; CB258D1329A4F24E00DEBA24 /* ConfigurationStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB84C7C029A3F0280088A5B8 /* ConfigurationStore.swift */; }; CB258D1D29A52AF900DEBA24 /* EtagStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9896632322C56716007BE4FE /* EtagStorage.swift */; }; @@ -738,6 +762,7 @@ CBD4F140279EBFB300B20FD7 /* SwiftUICollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB1AEFB02799AA940031AE3D /* SwiftUICollectionViewCell.swift */; }; CBDD5DDF29A6736A00832877 /* APIHeadersTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBDD5DDE29A6736A00832877 /* APIHeadersTests.swift */; }; CBDD5DE129A6741300832877 /* MockBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBDD5DE029A6741300832877 /* MockBundle.swift */; }; + CBEFB9142AE0844700DEDE7B /* CriticalAlerts.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBEFB9102ADFFE7900DEDE7B /* CriticalAlerts.swift */; }; D63657192A7BAE7C001AF19D /* EmailManagerRequestDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63657182A7BAE7C001AF19D /* EmailManagerRequestDelegate.swift */; }; EA39B7E2268A1A35000C62CD /* privacy-reference-tests in Resources */ = {isa = PBXBuildFile; fileRef = EA39B7E1268A1A35000C62CD /* privacy-reference-tests */; }; EAB19EDA268963510015D3EA /* DomainMatchingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB19ED9268963510015D3EA /* DomainMatchingTests.swift */; }; @@ -746,6 +771,8 @@ EE0153EB2A6FF970002A8B26 /* NetworkProtectionRootViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE0153EA2A6FF970002A8B26 /* NetworkProtectionRootViewModelTests.swift */; }; EE0153ED2A6FF9E6002A8B26 /* NetworkProtectionRootView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE0153EC2A6FF9E6002A8B26 /* NetworkProtectionRootView.swift */; }; EE0153EF2A70021E002A8B26 /* NetworkProtectionInviteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE0153EE2A70021E002A8B26 /* NetworkProtectionInviteView.swift */; }; + EE01EB402AFBD0000096AAC9 /* NetworkProtectionVPNSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE01EB3F2AFBD0000096AAC9 /* NetworkProtectionVPNSettingsViewModel.swift */; }; + EE01EB432AFC1E0A0096AAC9 /* NetworkProtectionVPNLocationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE01EB422AFC1E0A0096AAC9 /* NetworkProtectionVPNLocationView.swift */; }; EE276BEA2A77F823009167B6 /* NetworkProtectionRootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE276BE92A77F823009167B6 /* NetworkProtectionRootViewController.swift */; }; EE3766DE2AC5945500AAB575 /* NetworkProtectionUNNotificationPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE3766DD2AC5945500AAB575 /* NetworkProtectionUNNotificationPresenter.swift */; }; EE3B226B29DE0F110082298A /* MockInternalUserStoring.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE3B226A29DE0F110082298A /* MockInternalUserStoring.swift */; }; @@ -763,6 +790,14 @@ EE7A92872AC6DE4700832A36 /* NetworkProtectionNotificationIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE7A92862AC6DE4700832A36 /* NetworkProtectionNotificationIdentifier.swift */; }; EE8594992A44791C008A6D06 /* NetworkProtectionTunnelController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE8594982A44791C008A6D06 /* NetworkProtectionTunnelController.swift */; }; EE8E568A2A56BCE400F11DCA /* NetworkProtection in Frameworks */ = {isa = PBXBuildFile; productRef = EE8E56892A56BCE400F11DCA /* NetworkProtection */; }; + EE9D68D12AE00CF300B55EF4 /* NetworkProtectionVPNSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE9D68D02AE00CF300B55EF4 /* NetworkProtectionVPNSettingsView.swift */; }; + EE9D68D52AE1526600B55EF4 /* NetworkProtectionVPNNotificationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE9D68D42AE1526600B55EF4 /* NetworkProtectionVPNNotificationsView.swift */; }; + EE9D68D82AE15AD600B55EF4 /* UIApplicationExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE9D68D72AE15AD600B55EF4 /* UIApplicationExtension.swift */; }; + EE9D68DA2AE1659F00B55EF4 /* NetworkProtectionVPNNotificationsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE9D68D92AE1659F00B55EF4 /* NetworkProtectionVPNNotificationsViewModel.swift */; }; + EE9D68DC2AE16AE100B55EF4 /* NotificationsAuthorizationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE9D68DB2AE16AE100B55EF4 /* NotificationsAuthorizationController.swift */; }; + EE9D68DE2AE2A65600B55EF4 /* UserDefaults+NetworkProtection.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE9D68DD2AE2A65600B55EF4 /* UserDefaults+NetworkProtection.swift */; }; + EEC02C142B0519DE0045CE11 /* NetworkProtectionVPNLocationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEC02C132B0519DE0045CE11 /* NetworkProtectionVPNLocationViewModel.swift */; }; + EEC02C162B065BE00045CE11 /* NetworkProtectionVPNLocationViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEC02C152B065BE00045CE11 /* NetworkProtectionVPNLocationViewModelTests.swift */; }; EEDFE2DA2AC6ED4F00F0E19C /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = EEDFE2DC2AC6ED4F00F0E19C /* Localizable.strings */; }; EEEB80A32A421CE600386378 /* NetworkProtectionPacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEEB80A22A421CE600386378 /* NetworkProtectionPacketTunnelProvider.swift */; }; EEF0F8CC2ABC832300630031 /* NetworkProtectionDebugFeatures.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEF0F8CB2ABC832200630031 /* NetworkProtectionDebugFeatures.swift */; }; @@ -1250,7 +1285,10 @@ 31CC224828369B38001654A4 /* AutofillLoginSettingsListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillLoginSettingsListViewController.swift; sourceTree = ""; }; 31DD208327395A5A008FB313 /* VoiceSearchHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceSearchHelper.swift; sourceTree = ""; }; 31EF52E0281B3BDC0034796E /* AutofillLoginListItemViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillLoginListItemViewModel.swift; sourceTree = ""; }; + 3736088F2ABB1E6C00629E7F /* FavoritesDisplayModeStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoritesDisplayModeStorage.swift; sourceTree = ""; }; + 373608912ABB430D00629E7F /* FavoritesDisplayMode+UserDefaults.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FavoritesDisplayMode+UserDefaults.swift"; sourceTree = ""; }; 37445F962A155F7C0029F789 /* SyncDataProviders.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncDataProviders.swift; sourceTree = ""; }; + 377D80212AB48554002AF251 /* FavoritesDisplayModeSyncHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoritesDisplayModeSyncHandler.swift; sourceTree = ""; }; 379E877329E97C8D001C8BB0 /* BookmarksCleanupErrorHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksCleanupErrorHandling.swift; sourceTree = ""; }; 37CBCA9D2A8A659C0050218F /* SyncSettingsAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncSettingsAdapter.swift; sourceTree = ""; }; 37CEFCAB2A673B90001EF741 /* CredentialsCleanupErrorHandling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialsCleanupErrorHandling.swift; sourceTree = ""; }; @@ -1266,6 +1304,7 @@ 37FCAACB2993149A000E420A /* Waitlist */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Waitlist; sourceTree = ""; }; 37FD780E2A29E28B00B36DB1 /* SyncErrorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncErrorHandler.swift; sourceTree = ""; }; 4B0295182537BC6700E00CEF /* ConfigurationDebugViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationDebugViewController.swift; sourceTree = ""; }; + 4B274F5F2AFEAECC003F0745 /* NetworkProtectionWidgetRefreshModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionWidgetRefreshModel.swift; sourceTree = ""; }; 4B470ED5299C49800086EBDC /* AppTrackingProtectionDatabase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTrackingProtectionDatabase.swift; sourceTree = ""; }; 4B470ED8299C4AED0086EBDC /* AppTrackingProtectionModel.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = AppTrackingProtectionModel.xcdatamodel; sourceTree = ""; }; 4B470EDA299C4FB20086EBDC /* AppTrackingProtectionListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTrackingProtectionListViewModel.swift; sourceTree = ""; }; @@ -1273,6 +1312,7 @@ 4B470EE2299C6DD10086EBDC /* AppTrackingProtectionStoringModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTrackingProtectionStoringModel.swift; sourceTree = ""; }; 4B52648A25F9613B00CB4C24 /* trackerData.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = trackerData.json; sourceTree = ""; }; 4B53648926718D0E001AA041 /* EmailWaitlist.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailWaitlist.swift; sourceTree = ""; }; + 4B5C46292AF2A6E6002A4432 /* VPNIntents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNIntents.swift; sourceTree = ""; }; 4B60AC96252EC07B00E8D219 /* fullscreenvideo.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = fullscreenvideo.js; sourceTree = ""; }; 4B60ACA0252EC0B100E8D219 /* FullScreenVideoUserScript.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullScreenVideoUserScript.swift; sourceTree = ""; }; 4B62C4B925B930DD008912C6 /* AppConfigurationFetchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppConfigurationFetchTests.swift; sourceTree = ""; }; @@ -1287,15 +1327,25 @@ 4B83397029AC18C9003F7EA9 /* AppTrackingProtectionStoringModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTrackingProtectionStoringModelTests.swift; sourceTree = ""; }; 4B83397229AFB8D2003F7EA9 /* AppTrackingProtectionFeedbackModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTrackingProtectionFeedbackModel.swift; sourceTree = ""; }; 4B83397429AFBCE6003F7EA9 /* AppTrackingProtectionFeedbackModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTrackingProtectionFeedbackModelTests.swift; sourceTree = ""; }; + 4BB7CBAF2AF59C310014A35F /* VPNWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNWidget.swift; sourceTree = ""; }; + 4BBBBA892B031B4200D965DA /* VPNWaitlistDebugViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VPNWaitlistDebugViewController.swift; sourceTree = ""; }; + 4BBBBA8A2B031B4200D965DA /* VPNWaitlistViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VPNWaitlistViewController.swift; sourceTree = ""; }; + 4BBBBA8B2B031B4200D965DA /* VPNWaitlistView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VPNWaitlistView.swift; sourceTree = ""; }; + 4BBBBA8C2B031B4200D965DA /* VPNWaitlist.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VPNWaitlist.swift; sourceTree = ""; }; + 4BBBBA912B03291700D965DA /* VPNWaitlistUserText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNWaitlistUserText.swift; sourceTree = ""; }; 4BC21A2C272388BD00229F0E /* RunLoopExtensionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunLoopExtensionTests.swift; sourceTree = ""; }; 4BC6DD1B2A60E6AD001EC129 /* ReportBrokenSiteView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReportBrokenSiteView.swift; sourceTree = ""; }; + 4BCD14622B05AF2B000B1E4C /* NetworkProtectionAccessController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionAccessController.swift; sourceTree = ""; }; + 4BCD14662B05B682000B1E4C /* NetworkProtectionTermsAndConditionsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionTermsAndConditionsStore.swift; sourceTree = ""; }; + 4BCD14682B05BDD5000B1E4C /* AppDelegate+Waitlists.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AppDelegate+Waitlists.swift"; sourceTree = ""; }; + 4BCD146A2B05C4B5000B1E4C /* VPNWaitlistTermsAndConditionsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNWaitlistTermsAndConditionsViewController.swift; sourceTree = ""; }; + 4BCD146C2B05DB09000B1E4C /* NetworkProtectionAccessControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionAccessControllerTests.swift; sourceTree = ""; }; 4BE27566272F878F006B20B0 /* URLRequestExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = URLRequestExtension.swift; path = ../DuckDuckGo/URLRequestExtension.swift; sourceTree = ""; }; 4BFB911A29B7D9530014D4B7 /* AppTrackingProtectionStoringModelPerformanceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTrackingProtectionStoringModelPerformanceTests.swift; sourceTree = ""; }; 56244C1C2A137B1900EDF259 /* WaitlistViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaitlistViews.swift; sourceTree = ""; }; 6AC6DAB228804F97002723C0 /* BarsAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarsAnimator.swift; sourceTree = ""; }; 6AC98418288055C1005FA9CA /* BarsAnimatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarsAnimatorTests.swift; sourceTree = ""; }; 6FB030C7234331B400A10DB9 /* Configuration.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Configuration.xcconfig; path = Configuration/Configuration.xcconfig; sourceTree = ""; }; - 7B5E1F9D2AB9E1E900DA1172 /* NetworkProtectionDebugFeatures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionDebugFeatures.swift; sourceTree = ""; }; 83004E7F2193BB8200DA013C /* WKNavigationExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WKNavigationExtension.swift; sourceTree = ""; }; 83004E832193E14C00DA013C /* UIAlertControllerExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = UIAlertControllerExtension.swift; path = ../Core/UIAlertControllerExtension.swift; sourceTree = ""; }; 83004E852193E5ED00DA013C /* TabViewControllerBrowsingMenuExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabViewControllerBrowsingMenuExtension.swift; sourceTree = ""; }; @@ -1376,6 +1426,7 @@ 853273B124FF114700E3C778 /* DeepLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeepLinks.swift; sourceTree = ""; }; 8536A1C7209AF2410050739E /* MockVariantManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockVariantManager.swift; sourceTree = ""; }; 8536A1C9209AF6480050739E /* HomeRowReminderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeRowReminderTests.swift; sourceTree = ""; }; + 8536A1FC2ACF114B003AC5BA /* Theme+DesignSystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Theme+DesignSystem.swift"; sourceTree = ""; }; 85371D232121B9D400920548 /* new_tab.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = new_tab.json; sourceTree = ""; }; 85372446220DD103009D09CD /* UIKeyCommandExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKeyCommandExtension.swift; sourceTree = ""; }; 85374D3721AC419800FF5A1E /* NavigationSearchHomeViewSectionRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationSearchHomeViewSectionRenderer.swift; sourceTree = ""; }; @@ -1409,6 +1460,7 @@ 85519124247468580010FDD0 /* TrackerRadarIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackerRadarIntegrationTests.swift; sourceTree = ""; }; 8551912624746EDC0010FDD0 /* SnapshotHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SnapshotHelper.swift; path = fastlane/SnapshotHelper.swift; sourceTree = SOURCE_ROOT; }; 85582DFF29D7409700E9AE35 /* SyncSettingsViewController+PDFRendering.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SyncSettingsViewController+PDFRendering.swift"; sourceTree = ""; }; + 855D45D22ACD7DD1008F7AC6 /* AddressBarPositionSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressBarPositionSettingsViewController.swift; sourceTree = ""; }; 855D914C2063EF6A00C4B448 /* TabSwitcherTransition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabSwitcherTransition.swift; sourceTree = ""; }; 8563A03B1F9288D600F04442 /* BrowserChromeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowserChromeManager.swift; sourceTree = ""; }; 8565A34A1FC8D96B00239327 /* LaunchTabNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchTabNotification.swift; sourceTree = ""; }; @@ -1544,7 +1596,6 @@ 981DCA992521EFAB00CD4C18 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/Localizable.strings; sourceTree = ""; }; 981FED682201FE69008488D7 /* AutoClearSettingsScreenTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoClearSettingsScreenTests.swift; sourceTree = ""; }; 981FED6C22025151008488D7 /* BlankSnapshotViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlankSnapshotViewController.swift; sourceTree = ""; }; - 981FED702202519C008488D7 /* BlankSnapshot.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = BlankSnapshot.storyboard; sourceTree = ""; }; 981FED7322046017008488D7 /* AutoClearTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoClearTests.swift; sourceTree = ""; }; 981FED75220464EF008488D7 /* AutoClearSettingsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutoClearSettingsModel.swift; sourceTree = ""; }; 9820A5D522B1C0B20024E37C /* DDG Trace.tracetemplate */ = {isa = PBXFileReference; lastKnownFileType = file.bplist; path = "DDG Trace.tracetemplate"; sourceTree = ""; }; @@ -2277,30 +2328,58 @@ C1B7B52C2894469D0098FD6A /* DefaultVariantManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultVariantManager.swift; sourceTree = ""; }; C1B7B52F28944E390098FD6A /* RemoteMessagingStoreTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RemoteMessagingStoreTests.swift; sourceTree = ""; }; C1B7B53328944EFA0098FD6A /* CoreDataTestUtilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreDataTestUtilities.swift; sourceTree = ""; }; + C1B924B62ACD6E6800EE7B06 /* AutofillNeverSavedTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillNeverSavedTableViewCell.swift; sourceTree = ""; }; C1BF0BA429B63D7200482B73 /* AutofillLoginPromptHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutofillLoginPromptHelper.swift; sourceTree = ""; }; C1BF0BA729B63E1A00482B73 /* AutofillLoginPromptViewModelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutofillLoginPromptViewModelTests.swift; sourceTree = ""; }; C1CCCBA6283E101500CF3791 /* FaviconsHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FaviconsHelper.swift; sourceTree = ""; }; + C1CDA3152AFB9C7F006D1476 /* AutofillNeverPromptWebsitesManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutofillNeverPromptWebsitesManager.swift; sourceTree = ""; }; + C1CDA31D2AFBF811006D1476 /* AutofillNeverPromptWebsitesManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillNeverPromptWebsitesManagerTests.swift; sourceTree = ""; }; C1D21E2C293A5965006E5A05 /* AutofillLoginSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillLoginSession.swift; sourceTree = ""; }; C1D21E2E293A599C006E5A05 /* AutofillLoginSessionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillLoginSessionTests.swift; sourceTree = ""; }; C1F341C42A6924000032057B /* EmailAddressPromptView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailAddressPromptView.swift; sourceTree = ""; }; C1F341C62A6924100032057B /* EmailAddressPromptViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailAddressPromptViewModel.swift; sourceTree = ""; }; C1F341C82A6926920032057B /* EmailAddressPromptViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailAddressPromptViewController.swift; sourceTree = ""; }; + CB1143DD2AF6D4B600C1CCD3 /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/InfoPlist.strings; sourceTree = ""; }; + CB15F4762AF6D5100062A994 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/InfoPlist.strings; sourceTree = ""; }; + CB18F2712AF6D4E400A0F8FE /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/InfoPlist.strings; sourceTree = ""; }; CB1AEFB02799AA940031AE3D /* SwiftUICollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUICollectionViewCell.swift; sourceTree = ""; }; + CB1FAE472AF6D59B003F452F /* sk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sk; path = sk.lproj/InfoPlist.strings; sourceTree = ""; }; CB24F70E29A3EB15006DCC58 /* AppConfigurationURLProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppConfigurationURLProvider.swift; path = ../Core/AppConfigurationURLProvider.swift; sourceTree = ""; }; CB258D0C29A4CD0500DEBA24 /* Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Configuration.swift; sourceTree = ""; }; CB258D0F29A4D0FD00DEBA24 /* ConfigurationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationManager.swift; sourceTree = ""; }; + CB29792D2AF6D5C1006C461D /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = ""; }; CB2A7EEE283D185100885F67 /* RulesCompilationMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RulesCompilationMonitor.swift; sourceTree = ""; }; CB2A7EF028410DF700885F67 /* PixelEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PixelEvent.swift; sourceTree = ""; }; CB2A7EF3285383B300885F67 /* AppLastCompiledRulesStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLastCompiledRulesStore.swift; sourceTree = ""; }; + CB2C47822AF6D55800AEDCD9 /* nb */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nb; path = nb.lproj/InfoPlist.strings; sourceTree = ""; }; + CB4448752AF6D51D001F93F7 /* hr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hr; path = hr.lproj/InfoPlist.strings; sourceTree = ""; }; + CB5038622AF6D563007FD69F /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/InfoPlist.strings; sourceTree = ""; }; + CB6ABD002AF6D52B004A8224 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/InfoPlist.strings; sourceTree = ""; }; + CB6CE65B2AF6D4EE00119848 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = ""; }; + CB7407BC2AF6D56D0090A41C /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = ""; }; + CB75AA132AF6D5AA00AED266 /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/InfoPlist.strings; sourceTree = ""; }; CB84C7C029A3F0280088A5B8 /* ConfigurationStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationStore.swift; sourceTree = ""; }; + CB8EF4A32AF6D4C200EF158D /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/InfoPlist.strings; sourceTree = ""; }; + CB8F1F7D2AF6D5370024BF0E /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/InfoPlist.strings; sourceTree = ""; }; CB9B8738278C8E72001F4906 /* WidgetEducationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetEducationViewController.swift; sourceTree = ""; }; CB9B873B278C8FEA001F4906 /* WidgetEducationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetEducationView.swift; sourceTree = ""; }; CB9B873D278C93C2001F4906 /* HomeMessage.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = HomeMessage.xcassets; sourceTree = ""; }; + CB9F2A4B2AF6D4FB00F924BB /* et */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = et; path = et.lproj/InfoPlist.strings; sourceTree = ""; }; + CBA1DE942AF6D579007C9457 /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/InfoPlist.strings; sourceTree = ""; }; CBAA195927BFE15600A4BD49 /* NSManagedObjectContextExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSManagedObjectContextExtension.swift; sourceTree = ""; }; CBAA195B27C3982A00A4BD49 /* PrivacyFeatures.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyFeatures.swift; sourceTree = ""; }; + CBB6B2542AF6D543006B777C /* lt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lt; path = lt.lproj/InfoPlist.strings; sourceTree = ""; }; + CBC7AB542AF6D583008CB798 /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = ro.lproj/InfoPlist.strings; sourceTree = ""; }; + CBC8DC252AF6D4CD00BA681A /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/InfoPlist.strings; sourceTree = ""; }; CBD4F13B279EBF4A00B20FD7 /* HomeMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeMessage.swift; sourceTree = ""; }; + CBD7AE812AF6D5B6009052FD /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/InfoPlist.strings; sourceTree = ""; }; CBDD5DDE29A6736A00832877 /* APIHeadersTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = APIHeadersTests.swift; sourceTree = ""; }; CBDD5DE029A6741300832877 /* MockBundle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockBundle.swift; sourceTree = ""; }; + CBE099292AF6D54D000EFC47 /* lv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lv; path = lv.lproj/InfoPlist.strings; sourceTree = ""; }; + CBECB27B2AF6D58D006960FA /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/InfoPlist.strings; sourceTree = ""; }; + CBEF49902AF6D50600BFBD7D /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/InfoPlist.strings; sourceTree = ""; }; + CBEFB9102ADFFE7900DEDE7B /* CriticalAlerts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CriticalAlerts.swift; sourceTree = ""; }; + CBF0FA762AF6D4D800FB1C5B /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = ""; }; CBF14FC227970072001D94D0 /* HomeMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeMessageView.swift; sourceTree = ""; }; CBF14FC427970AB0001D94D0 /* HomeMessageViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeMessageViewModel.swift; sourceTree = ""; }; CBF14FC627970C8A001D94D0 /* HomeMessageCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeMessageCollectionViewCell.swift; sourceTree = ""; }; @@ -2312,6 +2391,8 @@ EE0153EA2A6FF970002A8B26 /* NetworkProtectionRootViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionRootViewModelTests.swift; sourceTree = ""; }; EE0153EC2A6FF9E6002A8B26 /* NetworkProtectionRootView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionRootView.swift; sourceTree = ""; }; EE0153EE2A70021E002A8B26 /* NetworkProtectionInviteView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionInviteView.swift; sourceTree = ""; }; + EE01EB3F2AFBD0000096AAC9 /* NetworkProtectionVPNSettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionVPNSettingsViewModel.swift; sourceTree = ""; }; + EE01EB422AFC1E0A0096AAC9 /* NetworkProtectionVPNLocationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionVPNLocationView.swift; sourceTree = ""; }; EE276BE92A77F823009167B6 /* NetworkProtectionRootViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionRootViewController.swift; sourceTree = ""; }; EE3766DD2AC5945500AAB575 /* NetworkProtectionUNNotificationPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionUNNotificationPresenter.swift; sourceTree = ""; }; EE3B226A29DE0F110082298A /* MockInternalUserStoring.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockInternalUserStoring.swift; sourceTree = ""; }; @@ -2330,7 +2411,15 @@ EE7917902A83DE93008DFF28 /* CombineTestUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CombineTestUtilities.swift; sourceTree = ""; }; EE7A92862AC6DE4700832A36 /* NetworkProtectionNotificationIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionNotificationIdentifier.swift; sourceTree = ""; }; EE8594982A44791C008A6D06 /* NetworkProtectionTunnelController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionTunnelController.swift; sourceTree = ""; }; + EE9D68D02AE00CF300B55EF4 /* NetworkProtectionVPNSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionVPNSettingsView.swift; sourceTree = ""; }; + EE9D68D42AE1526600B55EF4 /* NetworkProtectionVPNNotificationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionVPNNotificationsView.swift; sourceTree = ""; }; + EE9D68D72AE15AD600B55EF4 /* UIApplicationExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIApplicationExtension.swift; sourceTree = ""; }; + EE9D68D92AE1659F00B55EF4 /* NetworkProtectionVPNNotificationsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionVPNNotificationsViewModel.swift; sourceTree = ""; }; + EE9D68DB2AE16AE100B55EF4 /* NotificationsAuthorizationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsAuthorizationController.swift; sourceTree = ""; }; + EE9D68DD2AE2A65600B55EF4 /* UserDefaults+NetworkProtection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults+NetworkProtection.swift"; sourceTree = ""; }; EEB8FDB92A990AEE00EBEDCF /* Configuration-Alpha.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Configuration-Alpha.xcconfig"; path = "Configuration/Configuration-Alpha.xcconfig"; sourceTree = ""; }; + EEC02C132B0519DE0045CE11 /* NetworkProtectionVPNLocationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionVPNLocationViewModel.swift; sourceTree = ""; }; + EEC02C152B065BE00045CE11 /* NetworkProtectionVPNLocationViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionVPNLocationViewModelTests.swift; sourceTree = ""; }; EEDFE2DB2AC6ED4F00F0E19C /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; EEDFE2DD2AC6ED5B00F0E19C /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/Localizable.strings; sourceTree = ""; }; EEDFE2DE2AC6ED5F00F0E19C /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/Localizable.strings; sourceTree = ""; }; @@ -2539,6 +2628,7 @@ 8512EA5124ED30D20073EE19 /* SwiftUI.framework in Frameworks */, 85DF714624F7FE6100C89288 /* Core.framework in Frameworks */, 8512EA4F24ED30D20073EE19 /* WidgetKit.framework in Frameworks */, + 4BBBBA872B02E85400D965DA /* DesignResourcesKit in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2619,6 +2709,7 @@ 02025666298818B200E694E7 /* AppTrackingProtectionPacketTunnelProvider.swift */, 02025B1429884EA500E694E7 /* DDGObserverFactory.swift */, 02025668298818B200E694E7 /* Info.plist */, + CB1143DC2AF6D4B600C1CCD3 /* InfoPlist.strings */, 02025669298818B200E694E7 /* PacketTunnelProvider.entitlements */, EEFC6A5F2AC0F2F80065027D /* UserText.swift */, EEDFE2DC2AC6ED4F00F0E19C /* Localizable.strings */, @@ -3247,6 +3338,7 @@ 311BD1AE2836BB4200AEF6C1 /* AutofillItemsLockedView.swift */, 2DC3FBD62FBAF21E87610FA8 /* AutofillNoAuthAvailableView.swift */, C18ED4392AB6F77600BF3805 /* AutofillSettingsEnableFooterView.swift */, + C1B924B62ACD6E6800EE7B06 /* AutofillNeverSavedTableViewCell.swift */, ); name = Table; sourceTree = ""; @@ -3288,6 +3380,14 @@ path = LocalPackages; sourceTree = ""; }; + 377D80202AB4853A002AF251 /* SettingSyncHandlers */ = { + isa = PBXGroup; + children = ( + 377D80212AB48554002AF251 /* FavoritesDisplayModeSyncHandler.swift */, + ); + name = SettingSyncHandlers; + sourceTree = ""; + }; 37DF000829F9C3F0002B7D3E /* Sync */ = { isa = PBXGroup; children = ( @@ -3323,6 +3423,14 @@ name = WindowsBrowser; sourceTree = ""; }; + 4B274F5E2AFEAEB3003F0745 /* Widget */ = { + isa = PBXGroup; + children = ( + 4B274F5F2AFEAECC003F0745 /* NetworkProtectionWidgetRefreshModel.swift */, + ); + name = Widget; + sourceTree = ""; + }; 4B470ED4299C484B0086EBDC /* AppTrackingProtection */ = { isa = PBXGroup; children = ( @@ -3338,6 +3446,14 @@ name = AppTrackingProtection; sourceTree = ""; }; + 4B5C46282AF2A6DB002A4432 /* Intents */ = { + isa = PBXGroup; + children = ( + 4B5C46292AF2A6E6002A4432 /* VPNIntents.swift */, + ); + name = Intents; + sourceTree = ""; + }; 4B6484F427FD1E390050A7A1 /* Waitlist */ = { isa = PBXGroup; children = ( @@ -3345,6 +3461,7 @@ 56244C1C2A137B1900EDF259 /* WaitlistViews.swift */, 37FCAAA0299117F9000E420A /* MacBrowser */, 37FCAAA129911801000E420A /* WindowsBrowser */, + 4BBBBA882B031B3300D965DA /* VPN */, 8524AAAB2A3888FE00EEC6D2 /* Waitlist.xcassets */, ); name = Waitlist; @@ -3370,6 +3487,18 @@ name = AppTrackingProtection; sourceTree = ""; }; + 4BBBBA882B031B3300D965DA /* VPN */ = { + isa = PBXGroup; + children = ( + 4BBBBA8C2B031B4200D965DA /* VPNWaitlist.swift */, + 4BBBBA892B031B4200D965DA /* VPNWaitlistDebugViewController.swift */, + 4BBBBA8B2B031B4200D965DA /* VPNWaitlistView.swift */, + 4BBBBA8A2B031B4200D965DA /* VPNWaitlistViewController.swift */, + 4BCD146A2B05C4B5000B1E4C /* VPNWaitlistTermsAndConditionsViewController.swift */, + ); + name = VPN; + sourceTree = ""; + }; 830FA79B1F8E81FB00FCE105 /* ContentBlocker */ = { isa = PBXGroup; children = ( @@ -3531,7 +3660,6 @@ 83ED3B8D1FA8E63700B47556 /* README.md */, 83ED3B8C1FA8E61D00B47556 /* ManualTestsScript.md */, 85A313962028E78A00327D00 /* release_notes.txt */, - EEF0F8CA2ABC82E100630031 /* Recovered References */, ); sourceTree = ""; }; @@ -3646,6 +3774,7 @@ 8512EA5324ED30D20073EE19 /* Widgets.swift */, 853273AF24FEFE4600E3C778 /* WidgetsExtension.entitlements */, 853273A924FEF24300E3C778 /* WidgetViews.swift */, + 4BB7CBAF2AF59C310014A35F /* VPNWidget.swift */, ); path = Widgets; sourceTree = ""; @@ -3684,19 +3813,20 @@ isa = PBXGroup; children = ( F1CDD3F11F16911700BE0581 /* AboutViewController.swift */, - 1E865AEF272042DB001C74F3 /* TextSizeSettingsViewController.swift */, + 855D45D22ACD7DD1008F7AC6 /* AddressBarPositionSettingsViewController.swift */, AA3D854623D9E88E00788410 /* AppIconSettingsCell.swift */, AA3D854423D9942200788410 /* AppIconSettingsViewController.swift */, 98F0FC1F21FF18E700CE77AB /* AutoClearSettingsViewController.swift */, + 1EE7C298294227EC0026C8CB /* AutoconsentSettingsViewController.swift */, + 02C57C4A2514FEFB009E5129 /* DoNotSellSettingsViewController.swift */, F456B3B425810BB900B79B90 /* FireButtonAnimationSettingsViewController.swift */, 85449EF423FDA02800512AAF /* KeyboardSettingsViewController.swift */, 8540BD5523D9E9C20057FDD2 /* PreserveLoginsSettingsViewController.swift */, F176699D1E40BC86003D3222 /* Settings.storyboard */, F1AB2B411E3F7D5C00868554 /* SettingsViewController.swift */, + 1E865AEF272042DB001C74F3 /* TextSizeSettingsViewController.swift */, 9881439B23326DC200573F7C /* ThemeSettingsViewController.swift */, 8531A08D1F9950E6000484F0 /* UnprotectedSitesViewController.swift */, - 02C57C4A2514FEFB009E5129 /* DoNotSellSettingsViewController.swift */, - 1EE7C298294227EC0026C8CB /* AutoconsentSettingsViewController.swift */, ); name = UI; sourceTree = ""; @@ -3944,6 +4074,7 @@ 85F98F8C296F0ED100742F4A /* Sync */ = { isa = PBXGroup; children = ( + 377D80202AB4853A002AF251 /* SettingSyncHandlers */, 85F98F97296F4CB100742F4A /* SyncAssets.xcassets */, 85F0E97229952D7A003D5181 /* DuckDuckGo Recovery Document.pdf */, 85DD44232976C7A8005CC388 /* Controllers */, @@ -4067,6 +4198,7 @@ 98DA6EC92181E41F00E65433 /* ThemeManager.swift */, 98F3A1D9217B37200011A0D4 /* LightTheme.swift */, 98F3A1DB217B373E0011A0D4 /* DarkTheme.swift */, + 8536A1FC2ACF114B003AC5BA /* Theme+DesignSystem.swift */, ); name = Themes; sourceTree = ""; @@ -4342,6 +4474,9 @@ children = ( EE0153E02A6EABE0002A8B26 /* NetworkProtectionConvenienceInitialisers.swift */, EE458D0C2AB1DA4600FC651A /* EventMapping+NetworkProtectionError.swift */, + EE9D68DB2AE16AE100B55EF4 /* NotificationsAuthorizationController.swift */, + 4BCD14622B05AF2B000B1E4C /* NetworkProtectionAccessController.swift */, + 4BCD14662B05B682000B1E4C /* NetworkProtectionTermsAndConditionsStore.swift */, ); name = Helpers; sourceTree = ""; @@ -4356,6 +4491,15 @@ name = Root; sourceTree = ""; }; + EE01EB412AFC1DE10096AAC9 /* PreferredLocation */ = { + isa = PBXGroup; + children = ( + EE01EB422AFC1E0A0096AAC9 /* NetworkProtectionVPNLocationView.swift */, + EEC02C132B0519DE0045CE11 /* NetworkProtectionVPNLocationViewModel.swift */, + ); + name = PreferredLocation; + sourceTree = ""; + }; EE3766DC2AC5940A00AAB575 /* NetworkProtection */ = { isa = PBXGroup; children = ( @@ -4397,6 +4541,8 @@ EEFE9C722A603CE9005B0A26 /* NetworkProtectionStatusViewModelTests.swift */, EE0153EA2A6FF970002A8B26 /* NetworkProtectionRootViewModelTests.swift */, EE41BD182A729E9C00546C57 /* NetworkProtectionInviteViewModelTests.swift */, + 4BCD146C2B05DB09000B1E4C /* NetworkProtectionAccessControllerTests.swift */, + EEC02C152B065BE00045CE11 /* NetworkProtectionVPNLocationViewModelTests.swift */, ); name = NetworkProtection; sourceTree = ""; @@ -4405,18 +4551,42 @@ isa = PBXGroup; children = ( EE7A92862AC6DE4700832A36 /* NetworkProtectionNotificationIdentifier.swift */, + EE9D68DD2AE2A65600B55EF4 /* UserDefaults+NetworkProtection.swift */, ); name = NetworkProtection; sourceTree = ""; }; + EE9D68CF2AE00CE000B55EF4 /* VPNSettings */ = { + isa = PBXGroup; + children = ( + EE9D68D02AE00CF300B55EF4 /* NetworkProtectionVPNSettingsView.swift */, + EE01EB3F2AFBD0000096AAC9 /* NetworkProtectionVPNSettingsViewModel.swift */, + ); + name = VPNSettings; + sourceTree = ""; + }; + EE9D68D62AE1527F00B55EF4 /* VPNNotifications */ = { + isa = PBXGroup; + children = ( + EE9D68D42AE1526600B55EF4 /* NetworkProtectionVPNNotificationsView.swift */, + EE9D68D92AE1659F00B55EF4 /* NetworkProtectionVPNNotificationsViewModel.swift */, + ); + name = VPNNotifications; + sourceTree = ""; + }; EECD94B22A28B8580085C66E /* NetworkProtection */ = { isa = PBXGroup; children = ( + EE01EB412AFC1DE10096AAC9 /* PreferredLocation */, + EE9D68D62AE1527F00B55EF4 /* VPNNotifications */, + EE9D68CF2AE00CE000B55EF4 /* VPNSettings */, EE458D122ABB651500FC651A /* Debug */, EE0153E22A6FE031002A8B26 /* Root */, EE0153DF2A6EABAF002A8B26 /* Helpers */, EEFD562D2A65B68B00DAEC48 /* Invite */, EECD94B32A28B96C0085C66E /* Status */, + 4B5C46282AF2A6DB002A4432 /* Intents */, + 4B274F5E2AFEAEB3003F0745 /* Widget */, EE8594982A44791C008A6D06 /* NetworkProtectionTunnelController.swift */, ); name = NetworkProtection; @@ -4431,14 +4601,6 @@ name = Status; sourceTree = ""; }; - EEF0F8CA2ABC82E100630031 /* Recovered References */ = { - isa = PBXGroup; - children = ( - 7B5E1F9D2AB9E1E900DA1172 /* NetworkProtectionDebugFeatures.swift */, - ); - name = "Recovered References"; - sourceTree = ""; - }; EEFD562D2A65B68B00DAEC48 /* Invite */ = { isa = PBXGroup; children = ( @@ -4789,6 +4951,7 @@ F1D796EF1E7B07610019D451 /* BookmarksViewControllerCells.swift */, 85E58C2B28FDA94F006A801A /* FavoritesViewController.swift */, F1D796EB1E7AB8930019D451 /* SaveBookmarkActivity.swift */, + 3736088F2ABB1E6C00629E7F /* FavoritesDisplayModeStorage.swift */, ); name = Bookmarks; sourceTree = ""; @@ -4933,7 +5096,6 @@ children = ( 984147B624F0264B00362052 /* Authentication.storyboard */, F1AE54E71F0425FC00D9A700 /* AuthenticationViewController.swift */, - 981FED702202519C008488D7 /* BlankSnapshot.storyboard */, 981FED6C22025151008488D7 /* BlankSnapshotViewController.swift */, F1CA3C3A1F045B65005FADB3 /* Authenticator.swift */, F1CA3C361F045878005FADB3 /* PrivacyStore.swift */, @@ -4981,9 +5143,11 @@ CB24F70E29A3EB15006DCC58 /* AppConfigurationURLProvider.swift */, 84E341951E2F7EFB00BDBA6F /* AppDelegate.swift */, 85DB12EC2A1FED0C000A4A72 /* AppDelegate+AppDeepLinks.swift */, + 4BCD14682B05BDD5000B1E4C /* AppDelegate+Waitlists.swift */, 98B31291218CCB8C00E54DE1 /* AppDependencyProvider.swift */, 85BA58591F3506AE00C6E8CA /* AppSettings.swift */, 85BA58541F34F49E00C6E8CA /* AppUserDefaults.swift */, + 373608912ABB430D00629E7F /* FavoritesDisplayMode+UserDefaults.swift */, 850250B220D803F4002199C7 /* AtbAndVariantCleanup.swift */, 983EABB7236198F6003948D1 /* DatabaseMigration.swift */, 853C5F6021C277C7001F7A05 /* global.swift */, @@ -5019,6 +5183,7 @@ 850ABD022AC4D46C00A733DF /* SuggestionTray.storyboard */, 85864FBB24D31EF300E756FF /* SuggestionTrayViewController.swift */, 851DFD86212C39D300D95F20 /* TabSwitcherButton.swift */, + CBEFB9102ADFFE7900DEDE7B /* CriticalAlerts.swift */, ); name = Main; sourceTree = ""; @@ -5093,7 +5258,9 @@ F143C32C1E4A9A4800CFDE3A /* UIViewControllerExtension.swift */, F1DE78591E5CD2A70058895A /* UIViewExtension.swift */, F1F5337B1F26A9EF00D80D4F /* UserText.swift */, + 4BBBBA912B03291700D965DA /* VPNWaitlistUserText.swift */, 986DA94924884B18004A7E39 /* WebViewTransition.swift */, + EE9D68D72AE15AD600B55EF4 /* UIApplicationExtension.swift */, ); name = UserInterface; sourceTree = ""; @@ -5144,6 +5311,7 @@ C1BF0BA629B63E0400482B73 /* AutofillLoginUI */, F40F843528C938370081AE75 /* AutofillLoginListViewModelTests.swift */, C1D21E2E293A599C006E5A05 /* AutofillLoginSessionTests.swift */, + C1CDA31D2AFBF811006D1476 /* AutofillNeverPromptWebsitesManagerTests.swift */, ); name = Autofill; sourceTree = ""; @@ -5154,6 +5322,7 @@ D63657182A7BAE7C001AF19D /* EmailManagerRequestDelegate.swift */, F4147353283BF834004AA7A5 /* AutofillContentScopeFeatureToggles.swift */, C1D21E2C293A5965006E5A05 /* AutofillLoginSession.swift */, + C1CDA3152AFB9C7F006D1476 /* AutofillNeverPromptWebsitesManager.swift */, C13B32D12A0E750700A59236 /* AutofillSettingStatus.swift */, 319A370F28299A850079FBCE /* PasswordHider.swift */, 31C70B5428045E3500FB6AD1 /* SecureVaultErrorReporter.swift */, @@ -5343,6 +5512,9 @@ 85DF714924F7FE6100C89288 /* PBXTargetDependency */, ); name = WidgetsExtension; + packageProductDependencies = ( + 4BBBBA862B02E85400D965DA /* DesignResourcesKit */, + ); productName = WidgetsExtension; productReference = 8512EA4D24ED30D20073EE19 /* WidgetsExtension.appex */; productType = "com.apple.product-type.app-extension"; @@ -5620,6 +5792,7 @@ buildActionMask = 2147483647; files = ( 0262085C2A37915D006CB755 /* ios_blocklist_075.json in Resources */, + CB1143DE2AF6D4B600C1CCD3 /* InfoPlist.strings in Resources */, EEDFE2DA2AC6ED4F00F0E19C /* Localizable.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -5709,7 +5882,6 @@ 1E16260B296845120004127F /* cookie-banner-illustration-animated.json in Resources */, AA4D6AD323DE4D27007E8790 /* AppIconPurple29x29@2x.png in Resources */, AA4D6AA123DE4CC4007E8790 /* AppIconBlue60x60@3x.png in Resources */, - 981FED712202519C008488D7 /* BlankSnapshot.storyboard in Resources */, 984147A824F0259000362052 /* Onboarding.storyboard in Resources */, AA4D6AF723DF0312007E8790 /* AppIconRed60x60@2x.png in Resources */, AA4D6AE923DE4D33007E8790 /* AppIconGreen29x29@3x.png in Resources */, @@ -6070,6 +6242,7 @@ 9820FF502244FECC008D4782 /* UIScrollViewExtension.swift in Sources */, 8540BD5423D8D5080057FDD2 /* PreserveLoginsAlert.swift in Sources */, 1E87615928A1517200C7C5CE /* PrivacyDashboardViewController.swift in Sources */, + EE9D68D12AE00CF300B55EF4 /* NetworkProtectionVPNSettingsView.swift in Sources */, 319A371028299A850079FBCE /* PasswordHider.swift in Sources */, 982C87C42255559A00919035 /* UITableViewCellExtension.swift in Sources */, B623C1C42862CD670043013E /* WKDownloadSession.swift in Sources */, @@ -6086,8 +6259,10 @@ F1668BCE1E798081008CBA04 /* BookmarksViewController.swift in Sources */, 1E162610296C5C630004127F /* CustomDaxDialogViewModel.swift in Sources */, 8590CB69268A4E190089F6BF /* DebugEtagStorage.swift in Sources */, + C1CDA3162AFB9C7F006D1476 /* AutofillNeverPromptWebsitesManager.swift in Sources */, F1CA3C371F045878005FADB3 /* PrivacyStore.swift in Sources */, 37FCAAC029930E26000E420A /* FailedAssertionView.swift in Sources */, + 4BBBBA922B03291700D965DA /* VPNWaitlistUserText.swift in Sources */, F4E1936625AF722F001D2666 /* HighlightCutOutView.swift in Sources */, 1E162605296840D80004127F /* Triangle.swift in Sources */, B609D5522862EAFF0088CAC2 /* InlineWKDownloadDelegate.swift in Sources */, @@ -6100,6 +6275,7 @@ F1DE78581E5CAE350058895A /* TabViewGridCell.swift in Sources */, 984D035824ACCC6F0066CFB8 /* TabViewListCell.swift in Sources */, B6BA95C328891E33004ABA20 /* BrowsingMenuAnimator.swift in Sources */, + EE9D68DC2AE16AE100B55EF4 /* NotificationsAuthorizationController.swift in Sources */, AA3D854923DA1DFB00788410 /* AppIcon.swift in Sources */, 8590CB612684D0600089F6BF /* CookieDebugViewController.swift in Sources */, 319A37152829A55F0079FBCE /* AutofillListItemTableViewCell.swift in Sources */, @@ -6110,6 +6286,7 @@ B652DF12287C336E00C12A9C /* ContentBlockingUpdating.swift in Sources */, 314C92BA27C3E7CB0042EC96 /* QuickLookContainerViewController.swift in Sources */, 855D914D2063EF6A00C4B448 /* TabSwitcherTransition.swift in Sources */, + 4BBBBA8F2B031B4200D965DA /* VPNWaitlistView.swift in Sources */, CB258D1229A4F24900DEBA24 /* ConfigurationManager.swift in Sources */, 8546A54A2A672959003929BF /* MainViewController+Email.swift in Sources */, F4F6DFB226E6AEC100ED7E12 /* AddOrEditBookmarkViewController.swift in Sources */, @@ -6121,6 +6298,7 @@ 1E8AD1C727BE9B2900ABA377 /* DownloadsListDataSource.swift in Sources */, 3157B43527F497F50042D3D7 /* SaveLoginViewController.swift in Sources */, 853C5F6121C277C7001F7A05 /* global.swift in Sources */, + EE9D68D82AE15AD600B55EF4 /* UIApplicationExtension.swift in Sources */, F13B4BD31F1822C700814661 /* Tab.swift in Sources */, F1BE54581E69DE1000FCF649 /* TutorialSettings.swift in Sources */, 1EE52ABB28FB1D6300B750C1 /* UIImageExtension.swift in Sources */, @@ -6131,17 +6309,20 @@ CB9B873C278C8FEA001F4906 /* WidgetEducationView.swift in Sources */, 85F200002215C17B006BB258 /* FindInPage.swift in Sources */, F1386BA41E6846C40062FC3C /* TabDelegate.swift in Sources */, + C1B924B72ACD6E6800EE7B06 /* AutofillNeverSavedTableViewCell.swift in Sources */, 020108A929A7C1CD00644F9D /* AppTrackerImageCache.swift in Sources */, 3132FA2A27A0788F00DD7A12 /* QuickLookPreviewHelper.swift in Sources */, C1D21E2D293A5965006E5A05 /* AutofillLoginSession.swift in Sources */, 4B53648A26718D0E001AA041 /* EmailWaitlist.swift in Sources */, 027F48762A4B5FBE001A1C6C /* AppTPLinkButton.swift in Sources */, 8524CC98246D66E100E59D45 /* String+Markdown.swift in Sources */, + CBEFB9142AE0844700DEDE7B /* CriticalAlerts.swift in Sources */, 020108A329A561C300644F9D /* AppTPActivityView.swift in Sources */, 02A54A9E2A097F0F000C8FED /* AppTPCollectionViewCell.swift in Sources */, C1B7B529289420830098FD6A /* RemoteMessaging.xcdatamodeld in Sources */, 986B16C425E92DF0007D23E8 /* BrowsingMenuViewController.swift in Sources */, 988AC355257E47C100793C64 /* RequeryLogic.swift in Sources */, + EE9D68D52AE1526600B55EF4 /* NetworkProtectionVPNNotificationsView.swift in Sources */, 1E4F4A5A297193DE00625985 /* MainViewController+CookiesManaged.swift in Sources */, 8586A10D24CBA7070049720E /* FindInPageActivity.swift in Sources */, 1E1626072968413B0004127F /* ViewExtension.swift in Sources */, @@ -6166,6 +6347,7 @@ F4147354283BF834004AA7A5 /* AutofillContentScopeFeatureToggles.swift in Sources */, 986DA94A24884B18004A7E39 /* WebViewTransition.swift in Sources */, 31B524572715BB23002225AB /* WebJSAlert.swift in Sources */, + 8536A1FD2ACF114B003AC5BA /* Theme+DesignSystem.swift in Sources */, F114C55B1E66EB020018F95F /* NibLoading.swift in Sources */, C10CB5F32A1A5BDF0048E503 /* AutofillViews.swift in Sources */, 982E5630222C3D5B008D861B /* FeedbackPickerViewController.swift in Sources */, @@ -6173,6 +6355,7 @@ 85DFEDED24C7CCA500973FE7 /* AppWidthObserver.swift in Sources */, 4B6484F327FD1E350050A7A1 /* MenuControllerView.swift in Sources */, 1EE7C299294227EC0026C8CB /* AutoconsentSettingsViewController.swift in Sources */, + 4BCD14632B05AF2B000B1E4C /* NetworkProtectionAccessController.swift in Sources */, 1E8AD1D527C2E22900ABA377 /* DownloadsListSectionViewModel.swift in Sources */, 4BC6DD1C2A60E6AD001EC129 /* ReportBrokenSiteView.swift in Sources */, 31584616281AFB46004ADB8B /* AutofillLoginDetailsViewController.swift in Sources */, @@ -6182,6 +6365,7 @@ 980891A52237D4F500313A70 /* FeedbackNavigator.swift in Sources */, C1B7B52328941F2A0098FD6A /* RemoteMessagingStore.swift in Sources */, 1E8AD1C927BFAD1500ABA377 /* DirectoryMonitor.swift in Sources */, + 377D80222AB48554002AF251 /* FavoritesDisplayModeSyncHandler.swift in Sources */, 1E8AD1D127C000AB00ABA377 /* OngoingDownloadRow.swift in Sources */, 85058366219AE9EA00ED4EDB /* HomePageConfiguration.swift in Sources */, EE0153E12A6EABE0002A8B26 /* NetworkProtectionConvenienceInitialisers.swift in Sources */, @@ -6236,6 +6420,8 @@ F46FEC5727987A5F0061D9DF /* KeychainItemsDebugViewController.swift in Sources */, 02341FA62A4379CC008A1531 /* OnboardingStepViewModel.swift in Sources */, 850365F323DE087800D0F787 /* UIImageViewExtension.swift in Sources */, + 373608922ABB430D00629E7F /* FavoritesDisplayMode+UserDefaults.swift in Sources */, + 4BBBBA8D2B031B4200D965DA /* VPNWaitlistDebugViewController.swift in Sources */, C160544129D6044D00B715A1 /* AutofillInterfaceUsernameTruncator.swift in Sources */, 02A54A9A2A094A17000C8FED /* AppTPHomeView.swift in Sources */, 31C70B5528045E3500FB6AD1 /* SecureVaultErrorReporter.swift in Sources */, @@ -6257,11 +6443,13 @@ 989B337522D7EF2100437824 /* EmptyCollectionReusableView.swift in Sources */, 8524CC94246C5C8900E59D45 /* DaxDialogViewController.swift in Sources */, F42EF9312614BABE00101FB9 /* ActionSheetDaxDialogViewController.swift in Sources */, + EEC02C142B0519DE0045CE11 /* NetworkProtectionVPNLocationViewModel.swift in Sources */, F13B4BC01F180D8A00814661 /* TabsModel.swift in Sources */, 02025B0C29884D2C00E694E7 /* AppTrackerData.swift in Sources */, 85AE6690209724120014CF04 /* NotificationView.swift in Sources */, 1EA51376286596A000493C6A /* PrivacyIconLogic.swift in Sources */, 980891A92238504B00313A70 /* UILabelExtension.swift in Sources */, + 4BCD146B2B05C4B5000B1E4C /* VPNWaitlistTermsAndConditionsViewController.swift in Sources */, 984D035A24ACCC7D0066CFB8 /* TabViewCell.swift in Sources */, 31951E8E2823003200CAF535 /* AutofillLoginDetailsHeaderView.swift in Sources */, F194FAED1F14E2B3009B4DF8 /* UIFontExtension.swift in Sources */, @@ -6293,10 +6481,12 @@ C17B595A2A03AAD30055F2D1 /* PasswordGenerationPromptViewController.swift in Sources */, 8531A08E1F9950E6000484F0 /* UnprotectedSitesViewController.swift in Sources */, CBD4F13C279EBF4A00B20FD7 /* HomeMessage.swift in Sources */, + 4BBBBA8E2B031B4200D965DA /* VPNWaitlistViewController.swift in Sources */, 3132FA2C27A07A1B00DD7A12 /* FilePreview.swift in Sources */, 85C861E628FF1B5F00189466 /* HomeViewSectionRenderersExtension.swift in Sources */, F1D477C61F2126CC0031ED49 /* OmniBarState.swift in Sources */, 85F2FFCD2211F615006BB258 /* MainViewController+KeyCommands.swift in Sources */, + 4B274F602AFEAECC003F0745 /* NetworkProtectionWidgetRefreshModel.swift in Sources */, 0268FC132A449F04000EE6A2 /* OnboardingContainerView.swift in Sources */, 858650D9246B0D3C00C36F8A /* DaxOnboardingViewController.swift in Sources */, 312E5746283BB04A00C18FA0 /* AutofillEmptySearchView.swift in Sources */, @@ -6309,6 +6499,7 @@ 31C70B5B2804C61000FB6AD1 /* SaveAutofillLoginManager.swift in Sources */, 85449EFD23FDA71F00512AAF /* KeyboardSettings.swift in Sources */, 980891A222369ADB00313A70 /* FeedbackUserText.swift in Sources */, + 4BCD14692B05BDD5000B1E4C /* AppDelegate+Waitlists.swift in Sources */, 988F3DD3237DE8D900AEE34C /* ForgetDataAlert.swift in Sources */, 850ABD012AC3961100A733DF /* MainViewController+Segues.swift in Sources */, 9817C9C321EF594700884F65 /* AutoClear.swift in Sources */, @@ -6316,11 +6507,13 @@ 85EE7F572246685B000FE757 /* WebContainerViewController.swift in Sources */, 1EC458462948932500CB2B13 /* UIHostingControllerExtension.swift in Sources */, 1E4DCF4E27B6A69600961E25 /* DownloadsListHostingController.swift in Sources */, + 4BCD14672B05B682000B1E4C /* NetworkProtectionTermsAndConditionsStore.swift in Sources */, 020108A129A5610C00644F9D /* AppTPActivityHostingViewController.swift in Sources */, C1F341C92A6926920032057B /* EmailAddressPromptViewController.swift in Sources */, 02025B0F29884DC500E694E7 /* AppTrackerDataParser.swift in Sources */, 027F48742A4B5904001A1C6C /* AppTPAboutView.swift in Sources */, 311BD1B12836C0CA00AEF6C1 /* AutofillLoginListAuthenticator.swift in Sources */, + 4BBBBA902B031B4200D965DA /* VPNWaitlist.swift in Sources */, B652DF13287C373A00C12A9C /* ScriptSourceProviding.swift in Sources */, 854A012B2A54412600FCC628 /* ActivityViewController.swift in Sources */, F1CA3C391F045885005FADB3 /* PrivacyUserDefaults.swift in Sources */, @@ -6330,6 +6523,7 @@ F1D796F01E7B07610019D451 /* BookmarksViewControllerCells.swift in Sources */, 85058369219F424500ED4EDB /* UIColorExtension.swift in Sources */, 85058368219C49E000ED4EDB /* HomeViewSectionRenderers.swift in Sources */, + EE01EB432AFC1E0A0096AAC9 /* NetworkProtectionVPNLocationView.swift in Sources */, F456B3B525810BB900B79B90 /* FireButtonAnimationSettingsViewController.swift in Sources */, 9820EAF522613CD30089094D /* WebProgressWorker.swift in Sources */, B6CB93E5286445AB0090FEB4 /* Base64DownloadSession.swift in Sources */, @@ -6338,6 +6532,7 @@ 85010502292FB1000033978F /* FireproofFaviconUpdater.swift in Sources */, F1C4A70E1E57725800A6CA1B /* OmniBar.swift in Sources */, 981CA7EA2617797500E119D5 /* MainViewController+AddFavoriteFlow.swift in Sources */, + 373608902ABB1E6C00629E7F /* FavoritesDisplayModeStorage.swift in Sources */, 9872D205247DCAC100CEF398 /* TabPreviewsSource.swift in Sources */, F130D73A1E5776C500C45811 /* OmniBarDelegate.swift in Sources */, 85DFEDEF24C7EA3B00973FE7 /* SmallOmniBarState.swift in Sources */, @@ -6354,6 +6549,7 @@ 020108A729A6ABF600644F9D /* AppTPToggleView.swift in Sources */, 02A54A982A093126000C8FED /* AppTPHomeViewModel.swift in Sources */, F1617C191E573EA800DEDCAF /* TabSwitcherDelegate.swift in Sources */, + 4B5C462A2AF2A6E6002A4432 /* VPNIntents.swift in Sources */, 310742A62848CD780012660B /* BackForwardMenuHistoryItem.swift in Sources */, 858566FB252E55D6007501B8 /* ImageCacheDebugViewController.swift in Sources */, 0290472E29E99A2F0008FE3C /* GenericIconView.swift in Sources */, @@ -6388,6 +6584,7 @@ 027F48782A4B663C001A1C6C /* AppTPFAQView.swift in Sources */, 02A4EACA29B0F464009BE006 /* AppTPToggleViewModel.swift in Sources */, 4B6484EE27FD1E350050A7A1 /* WindowsBrowserWaitlistDebugViewController.swift in Sources */, + 855D45D32ACD7DD1008F7AC6 /* AddressBarPositionSettingsViewController.swift in Sources */, F1D796EE1E7AF2EB0019D451 /* UIViewControllerExtension.swift in Sources */, 1EE411F12857C3640003FE64 /* TrackerAnimationImageProvider.swift in Sources */, 1E7A711C2934EEBC00B7EA19 /* OmniBarNotification.swift in Sources */, @@ -6397,6 +6594,7 @@ 31EF52E1281B3BDC0034796E /* AutofillLoginListItemViewModel.swift in Sources */, 1E4FAA6627D8DFC800ADC5B3 /* CompleteDownloadRowViewModel.swift in Sources */, 83004E862193E5ED00DA013C /* TabViewControllerBrowsingMenuExtension.swift in Sources */, + EE01EB402AFBD0000096AAC9 /* NetworkProtectionVPNSettingsViewModel.swift in Sources */, EE72CA852A862D000043B5B3 /* NetworkProtectionDebugViewController.swift in Sources */, C18ED43A2AB6F77600BF3805 /* AutofillSettingsEnableFooterView.swift in Sources */, CB84C7BD29A3EF530088A5B8 /* AppConfigurationURLProvider.swift in Sources */, @@ -6424,6 +6622,7 @@ 8586A11024CCCD040049720E /* TabsBarViewController.swift in Sources */, F1D796F41E7C2A410019D451 /* BookmarksDelegate.swift in Sources */, C1B7B52428941F2A0098FD6A /* RemoteMessageRequest.swift in Sources */, + EE9D68DA2AE1659F00B55EF4 /* NetworkProtectionVPNNotificationsViewModel.swift in Sources */, 1E8AD1D727C2E24E00ABA377 /* DownloadsListRowViewModel.swift in Sources */, C1B0F6422AB08BE9001EAF05 /* MockPrivacyConfiguration.swift in Sources */, 1E865AF0272042DB001C74F3 /* TextSizeSettingsViewController.swift in Sources */, @@ -6502,11 +6701,13 @@ 026F08B829B7DC480079B9DF /* EmbeddedAppTPDataTests.swift in Sources */, 851CD674244D7E6000331B98 /* UserDefaultsExtension.swift in Sources */, 850559D223CF710C0055C0D5 /* WebCacheManagerTests.swift in Sources */, + EEC02C162B065BE00045CE11 /* NetworkProtectionVPNLocationViewModelTests.swift in Sources */, 987130C5294AAB9F00AB05E0 /* BookmarkEditorViewModelTests.swift in Sources */, 8341D807212D5E8D000514C2 /* HashExtensionTest.swift in Sources */, C1D21E2F293A599C006E5A05 /* AutofillLoginSessionTests.swift in Sources */, 85D2187924BF6B8B004373D2 /* FaviconSourcesProviderTests.swift in Sources */, 1E8146AD28C8ABF000D1AF63 /* TrackerAnimationLogicTests.swift in Sources */, + C1CDA31E2AFBF811006D1476 /* AutofillNeverPromptWebsitesManagerTests.swift in Sources */, B6AD9E3A28D456820019CDE9 /* PrivacyConfigurationManagerMock.swift in Sources */, F189AED71F18F6DE001EBAE1 /* TabTests.swift in Sources */, F13B4BFB1F18E3D900814661 /* TabsModelPersistenceExtensionTests.swift in Sources */, @@ -6528,6 +6729,7 @@ F1D477C91F2139410031ED49 /* SmallOmniBarStateTests.swift in Sources */, 987130C9294AAB9F00AB05E0 /* BookmarkUtilsTests.swift in Sources */, C1BF0BA929B63E2200482B73 /* AutofillLoginPromptViewModelTests.swift in Sources */, + 4BCD146D2B05DB09000B1E4C /* NetworkProtectionAccessControllerTests.swift in Sources */, EE3B226B29DE0F110082298A /* MockInternalUserStoring.swift in Sources */, 987130C8294AAB9F00AB05E0 /* BookmarksTestHelpers.swift in Sources */, F198D7981E3A45D90088DA8A /* WKWebViewConfigurationExtensionTests.swift in Sources */, @@ -6578,9 +6780,12 @@ buildActionMask = 2147483647; files = ( 853273AE24FEF49600E3C778 /* ColorExtension.swift in Sources */, + 373608932ABB432600629E7F /* FavoritesDisplayMode+UserDefaults.swift in Sources */, 853273B324FF114700E3C778 /* DeepLinks.swift in Sources */, 853273B424FFB36100E3C778 /* UIColorExtension.swift in Sources */, 853273AB24FEF27500E3C778 /* WidgetViews.swift in Sources */, + 4B5C462B2AF2BDC4002A4432 /* VPNIntents.swift in Sources */, + 4BB7CBB02AF59C310014A35F /* VPNWidget.swift in Sources */, 8512EA5424ED30D20073EE19 /* Widgets.swift in Sources */, 85DB12EB2A1FE2A4000A4A72 /* LockScreenWidgets.swift in Sources */, 8544C37C250B827300A0FE73 /* UserText.swift in Sources */, @@ -6683,6 +6888,7 @@ CB2A7EF4285383B300885F67 /* AppLastCompiledRulesStore.swift in Sources */, 4B75EA9226A266CB00018634 /* PrintingUserScript.swift in Sources */, 37445F972A155F7C0029F789 /* SyncDataProviders.swift in Sources */, + EE9D68DE2AE2A65600B55EF4 /* UserDefaults+NetworkProtection.swift in Sources */, CB258D1F29A52B2500DEBA24 /* Configuration.swift in Sources */, 9847C00027A2DDBB00DB07AA /* AppPrivacyConfigurationDataProvider.swift in Sources */, F143C3281E4A9A0E00CFDE3A /* StringExtension.swift in Sources */, @@ -7448,6 +7654,37 @@ name = OmniBar.xib; sourceTree = ""; }; + CB1143DC2AF6D4B600C1CCD3 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + CB1143DD2AF6D4B600C1CCD3 /* bg */, + CB8EF4A32AF6D4C200EF158D /* cs */, + CBC8DC252AF6D4CD00BA681A /* da */, + CBF0FA762AF6D4D800FB1C5B /* de */, + CB18F2712AF6D4E400A0F8FE /* el */, + CB6CE65B2AF6D4EE00119848 /* es */, + CB9F2A4B2AF6D4FB00F924BB /* et */, + CBEF49902AF6D50600BFBD7D /* fi */, + CB15F4762AF6D5100062A994 /* fr */, + CB4448752AF6D51D001F93F7 /* hr */, + CB6ABD002AF6D52B004A8224 /* hu */, + CB8F1F7D2AF6D5370024BF0E /* it */, + CBB6B2542AF6D543006B777C /* lt */, + CBE099292AF6D54D000EFC47 /* lv */, + CB2C47822AF6D55800AEDCD9 /* nb */, + CB5038622AF6D563007FD69F /* nl */, + CB7407BC2AF6D56D0090A41C /* pl */, + CBA1DE942AF6D579007C9457 /* pt */, + CBC7AB542AF6D583008CB798 /* ro */, + CBECB27B2AF6D58D006960FA /* ru */, + CB1FAE472AF6D59B003F452F /* sk */, + CB75AA132AF6D5AA00AED266 /* sl */, + CBD7AE812AF6D5B6009052FD /* sv */, + CB29792D2AF6D5C1006C461D /* tr */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; EEDFE2DC2AC6ED4F00F0E19C /* Localizable.strings */ = { isa = PBXVariantGroup; children = ( @@ -7621,7 +7858,7 @@ CODE_SIGN_ENTITLEMENTS = PacketTunnelProvider/PacketTunnelProvider.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -7658,7 +7895,7 @@ CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -7750,7 +7987,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = ShareExtension/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -7777,7 +8014,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -7923,7 +8160,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -7947,7 +8184,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGo.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; INFOPLIST_FILE = DuckDuckGo/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -8011,7 +8248,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEAD_CODE_STRIPPING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = Widgets/Info.plist; @@ -8046,7 +8283,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8080,7 +8317,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; GCC_C_LANGUAGE_STANDARD = gnu11; INFOPLIST_FILE = OpenAction/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( @@ -8110,7 +8347,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8381,7 +8618,7 @@ MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG NETWORK_PROTECTION"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG NETWORK_PROTECTION ALPHA"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; TARGETED_DEVICE_FAMILY = "1,2"; VALID_ARCHS = "$(ARCHS_STANDARD_64_BIT)"; @@ -8396,7 +8633,7 @@ CODE_SIGN_ENTITLEMENTS = DuckDuckGo/DuckDuckGoAlpha.entitlements; CODE_SIGN_IDENTITY = "iPhone Distribution"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_ASSET_PATHS = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8422,7 +8659,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8454,7 +8691,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8491,7 +8728,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEAD_CODE_STRIPPING = NO; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; @@ -8527,7 +8764,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = HKE973VLUW; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -8562,11 +8799,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; + DYLIB_CURRENT_VERSION = 2; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -8740,11 +8977,11 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; + DYLIB_CURRENT_VERSION = 2; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -8773,10 +9010,10 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 2; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; + DYLIB_CURRENT_VERSION = 2; DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Core/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -8978,7 +9215,7 @@ repositoryURL = "https://github.com/duckduckgo/DesignResourcesKit"; requirement = { kind = exactVersion; - version = 1.1.1; + version = 2.0.0; }; }; F486D2EF25069482002D07D7 /* XCRemoteSwiftPackageReference "Kingfisher" */ = { @@ -9079,6 +9316,11 @@ package = 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; productName = Persistence; }; + 4BBBBA862B02E85400D965DA /* DesignResourcesKit */ = { + isa = XCSwiftPackageProductDependency; + package = F42D541B29DCA40B004C4FF1 /* XCRemoteSwiftPackageReference "DesignResourcesKit" */; + productName = DesignResourcesKit; + }; 851481872A600EFC00ABC65F /* RemoteMessaging */ = { isa = XCSwiftPackageProductDependency; package = 98A16C2928A11BDE00A6C003 /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index f76977d5f5..040cf6b099 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -15,7 +15,7 @@ "repositoryURL": "https://github.com/DuckDuckGo/BrowserServicesKit", "state": { "branch": "sam/add-netp-subscription-auth-support", - "revision": "b24418c49790bc46677469237afb46d00884624c", + "revision": "22506de8f9fcadddf2a949ac922ad4377b0a3188", "version": null } }, @@ -33,8 +33,8 @@ "repositoryURL": "https://github.com/duckduckgo/content-scope-scripts", "state": { "branch": null, - "revision": "74b6142c016be354144f28551de41b50c4864b1f", - "version": "4.37.0" + "revision": "b7ad9843e70cede0c2ca9c4260d970f62cb28156", + "version": "4.52.0" } }, { @@ -42,8 +42,8 @@ "repositoryURL": "https://github.com/duckduckgo/DesignResourcesKit", "state": { "branch": null, - "revision": "5de78d59358ff60651b2cf9322c74a2297927b4e", - "version": "1.1.1" + "revision": "d7ea2561ec7624c224f52e1c9b349075ddf1c782", + "version": "2.0.0" } }, { @@ -51,8 +51,8 @@ "repositoryURL": "https://github.com/duckduckgo/duckduckgo-autofill.git", "state": { "branch": null, - "revision": "55466f10a339843cb79a27af62d5d61c030740b2", - "version": "8.4.1" + "revision": "93677cc02cfe650ce7f417246afd0e8e972cd83e", + "version": "10.0.0" } }, { @@ -105,8 +105,8 @@ "repositoryURL": "https://github.com/duckduckgo/privacy-dashboard", "state": { "branch": null, - "revision": "51e2b46f413bf3ef18afefad631ca70f2c25ef70", - "version": "1.4.0" + "revision": "b4ac92a444e79d5651930482623b9f6dc9265667", + "version": "2.0.0" } }, { @@ -156,7 +156,7 @@ }, { "package": "TrackerRadarKit", - "repositoryURL": "https://github.com/duckduckgo/TrackerRadarKit.git", + "repositoryURL": "https://github.com/duckduckgo/TrackerRadarKit", "state": { "branch": null, "revision": "4684440d03304e7638a2c8086895367e90987463", diff --git a/DuckDuckGo/AboutViewController.swift b/DuckDuckGo/AboutViewController.swift index 0c9d4a1328..765b17d10a 100644 --- a/DuckDuckGo/AboutViewController.swift +++ b/DuckDuckGo/AboutViewController.swift @@ -19,64 +19,64 @@ import UIKit import Core +import SwiftUI +import DesignResourcesKit -class AboutViewController: UIViewController { +class AboutViewController: UIHostingController { - @IBOutlet weak var headerText: UILabel! - @IBOutlet weak var descriptionText: UILabel! - @IBOutlet weak var logoImage: UIImageView! - @IBOutlet weak var moreButton: UIButton! - - override func viewDidLoad() { - super.viewDidLoad() - - applyTheme(ThemeManager.shared.currentTheme) + convenience init() { + self.init(rootView: AboutView()) } - @IBAction func onPrivacyLinkTapped(_ sender: UIButton) { - dismiss(animated: true) { - UIApplication.shared.open(URL.aboutLink, options: [:]) +} + +struct AboutView: View { + + var body: some View { + ScrollView { + VStack(spacing: 12) { + Image("Logo") + .resizable() + .frame(width: 96, height: 96) + .padding(.top) + + Image("TextDuckDuckGo") + + Text("Welcome to the Duck Side!") + .daxHeadline() + + Rectangle() + .frame(width: 80, height: 0.5) + .foregroundColor(Color(designSystemColor: .lines)) + .padding() + + Text(LocalizedStringKey(UserText.aboutText)) + .lineLimit(nil) + .multilineTextAlignment(.leading) + .foregroundColor(.primary) + .tintIfAvailable(Color(designSystemColor: .accent)) + .padding(.horizontal, 32) + .padding(.bottom) + + Spacer() + } + .frame(maxWidth: .infinity) } + .background(Rectangle() + .ignoresSafeArea() + .foregroundColor(Color(designSystemColor: .background))) } + } -extension AboutViewController: Themable { +private extension View { - func decorate(with theme: Theme) { - view.backgroundColor = theme.backgroundColor - - switch theme.currentImageSet { - case .light: - logoImage?.image = UIImage(named: "LogoDarkText") - case .dark: - logoImage?.image = UIImage(named: "LogoLightText") + @ViewBuilder func tintIfAvailable(_ color: Color) -> some View { + if #available(iOS 16.0, *) { + tint(color) + } else { + self } - - decorateDescription(with: theme) - - headerText.textColor = theme.aboutScreenTextColor - moreButton.setTitleColor(theme.aboutScreenButtonColor, for: .normal) } - - private func decorateDescription(with theme: Theme) { - if let attributedText = descriptionText.attributedText, - var font = attributedText.attribute(NSAttributedString.Key.font, at: 0, effectiveRange: nil) as? UIFont { - - let attributes: [NSAttributedString.Key: Any] - if traitCollection.horizontalSizeClass == .regular, - traitCollection.verticalSizeClass == .regular { - font = font.withSize(24.0) - attributes = [.foregroundColor: theme.aboutScreenTextColor, - .font: font] - } else { - attributes = [.foregroundColor: theme.aboutScreenTextColor, - .font: font] - } - let decoratedText = NSMutableAttributedString(string: UserText.settingsAboutText) - decoratedText.addAttributes(attributes, range: NSRange(location: 0, length: decoratedText.length)) - - descriptionText.attributedText = decoratedText - } - } } diff --git a/DuckDuckGo/ActionMessageView.swift b/DuckDuckGo/ActionMessageView.swift index 37e86221b5..57ff705e6e 100644 --- a/DuckDuckGo/ActionMessageView.swift +++ b/DuckDuckGo/ActionMessageView.swift @@ -22,9 +22,9 @@ import UIKit extension ActionMessageView: NibLoading {} class ActionMessageView: UIView { - + enum PresentationLocation { - case withBottomBar + case withBottomBar(andAddressBarBottom: Bool) case withoutBottomBar } @@ -45,6 +45,11 @@ class ActionMessageView: UIView { return 70 } + + static var windowBottomPaddingWithAddressBar: CGFloat { + return windowBottomPaddingWithBottomBar + 52 + } + static var windowBottomPaddingWithoutBottomBar: CGFloat { return 0 } @@ -53,8 +58,8 @@ class ActionMessageView: UIView { private static func bottomPadding(for location: PresentationLocation) -> CGFloat { switch location { - case .withBottomBar: - return Constants.windowBottomPaddingWithBottomBar + case .withBottomBar(let isAddressBarBottom): + return isAddressBarBottom ? Constants.windowBottomPaddingWithAddressBar : Constants.windowBottomPaddingWithBottomBar case .withoutBottomBar: return Constants.windowBottomPaddingWithoutBottomBar } @@ -84,7 +89,7 @@ class ActionMessageView: UIView { static func present(message: NSAttributedString, numberOfLines: Int = 0, actionTitle: String? = nil, - presentationLocation: PresentationLocation = .withBottomBar, + presentationLocation: PresentationLocation = .withBottomBar(andAddressBarBottom: false), onAction: @escaping () -> Void = {}, onDidDismiss: @escaping () -> Void = {}) { let messageView = loadFromXib() @@ -100,7 +105,7 @@ class ActionMessageView: UIView { static func present(message: String, actionTitle: String? = nil, - presentationLocation: PresentationLocation = .withBottomBar, + presentationLocation: PresentationLocation = .withBottomBar(andAddressBarBottom: false), onAction: @escaping () -> Void = {}, onDidDismiss: @escaping () -> Void = {}) { let messageView = loadFromXib() @@ -116,7 +121,7 @@ class ActionMessageView: UIView { private static func present(messageView: ActionMessageView, message: String, actionTitle: String? = nil, - presentationLocation: PresentationLocation = .withBottomBar, + presentationLocation: PresentationLocation = .withBottomBar(andAddressBarBottom: false), onAction: @escaping () -> Void = {}, onDidDismiss: @escaping () -> Void = {}) { guard let window = UIApplication.shared.windows.filter({ $0.isKeyWindow }).first else { return } diff --git a/DuckDuckGo/AddOrEditBookmarkViewController.swift b/DuckDuckGo/AddOrEditBookmarkViewController.swift index 1b31051f25..188764d2ba 100644 --- a/DuckDuckGo/AddOrEditBookmarkViewController.swift +++ b/DuckDuckGo/AddOrEditBookmarkViewController.swift @@ -41,19 +41,23 @@ class AddOrEditBookmarkViewController: UIViewController { private let viewModel: BookmarkEditorViewModel private let bookmarksDatabase: CoreDataDatabase private let syncService: DDGSyncing + private let appSettings: AppSettings private var viewModelCancellable: AnyCancellable? init?(coder: NSCoder, editingEntityID: NSManagedObjectID, bookmarksDatabase: CoreDataDatabase, - syncService: DDGSyncing) { + syncService: DDGSyncing, + appSettings: AppSettings) { self.bookmarksDatabase = bookmarksDatabase self.viewModel = BookmarkEditorViewModel(editingEntityID: editingEntityID, bookmarksDatabase: bookmarksDatabase, + favoritesDisplayMode: appSettings.favoritesDisplayMode, syncService: syncService) self.syncService = syncService + self.appSettings = appSettings super.init(coder: coder) } @@ -61,13 +65,16 @@ class AddOrEditBookmarkViewController: UIViewController { init?(coder: NSCoder, parentFolderID: NSManagedObjectID?, bookmarksDatabase: CoreDataDatabase, - syncService: DDGSyncing) { + syncService: DDGSyncing, + appSettings: AppSettings) { self.bookmarksDatabase = bookmarksDatabase self.viewModel = BookmarkEditorViewModel(creatingFolderWithParentID: parentFolderID, bookmarksDatabase: bookmarksDatabase, + favoritesDisplayMode: appSettings.favoritesDisplayMode, syncService: syncService) self.syncService = syncService + self.appSettings = appSettings super.init(coder: coder) } @@ -138,7 +145,8 @@ class AddOrEditBookmarkViewController: UIViewController { coder: coder, parentFolderID: viewModel.bookmark.parent?.objectID, bookmarksDatabase: bookmarksDatabase, - syncService: syncService + syncService: syncService, + appSettings: appSettings ) else { fatalError("Failed to create controller") } diff --git a/DuckDuckGo/AddressBarPositionSettingsViewController.swift b/DuckDuckGo/AddressBarPositionSettingsViewController.swift new file mode 100644 index 0000000000..b7df80ab5e --- /dev/null +++ b/DuckDuckGo/AddressBarPositionSettingsViewController.swift @@ -0,0 +1,99 @@ +// +// AddressBarPositionSettingsViewController.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import Core + +class AddressBarPositionSettingsViewController: UITableViewController { + + private lazy var appSettings = AppDependencyProvider.shared.appSettings + + private lazy var options = AddressBarPosition.allCases + + override func viewDidLoad() { + super.viewDidLoad() + + applyTheme(ThemeManager.shared.currentTheme) + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return options.count + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + return tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) + } + + override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { + let theme = ThemeManager.shared.currentTheme + cell.backgroundColor = theme.tableCellBackgroundColor + cell.setHighlightedStateBackgroundColor(theme.tableCellHighlightedBackgroundColor) + + cell.tintColor = theme.buttonTintColor + cell.textLabel?.textColor = theme.tableCellTextColor + + cell.textLabel?.text = options[indexPath.row].descriptionText + cell.accessoryType = appSettings.currentAddressBarPosition.descriptionText == cell.textLabel?.text ? .checkmark : .none + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + appSettings.currentAddressBarPosition = AddressBarPosition.allCases[indexPath.row] + + switch appSettings.currentAddressBarPosition { + case .top: + Pixel.fire(pixel: .navigationBarPositionTop) + case .bottom: + Pixel.fire(pixel: .navigationbarPositionBottom) + } + + tableView.performBatchUpdates { + tableView.reloadSections(IndexSet(integer: 0), with: .automatic) + tableView.deselectRow(at: indexPath, animated: true) + } + } +} + +extension AddressBarPositionSettingsViewController: Themable { + + func decorate(with theme: Theme) { + + tableView.backgroundColor = theme.backgroundColor + tableView.separatorColor = theme.tableCellSeparatorColor + + tableView.reloadData() + } +} + +enum AddressBarPosition: String, CaseIterable { + case top + case bottom + + var isBottom: Bool { + self == .bottom + } + + var descriptionText: String { + switch self { + case .top: + return UserText.addressBarPositionTop + case .bottom: + return UserText.addressBarPositionBottom + } + } +} diff --git a/DuckDuckGo/AppDelegate+AppDeepLinks.swift b/DuckDuckGo/AppDelegate+AppDeepLinks.swift index 60788a6abc..ec8d49dbac 100644 --- a/DuckDuckGo/AppDelegate+AppDeepLinks.swift +++ b/DuckDuckGo/AppDelegate+AppDeepLinks.swift @@ -22,6 +22,7 @@ import Core extension AppDelegate { + // swiftlint:disable:next cyclomatic_complexity func handleAppDeepLink(_ app: UIApplication, _ mainViewController: MainViewController?, _ url: URL) -> Bool { guard let mainViewController else { return false } @@ -50,6 +51,11 @@ extension AppDelegate { case .newEmail: mainViewController.newEmailAddress() + case .openVPN: +#if NETWORK_PROTECTION + presentNetworkProtectionStatusSettingsModal() +#endif + default: guard app.applicationState == .active, let currentTab = mainViewController.currentTab else { diff --git a/DuckDuckGo/AppDelegate+Waitlists.swift b/DuckDuckGo/AppDelegate+Waitlists.swift new file mode 100644 index 0000000000..83f5a84a24 --- /dev/null +++ b/DuckDuckGo/AppDelegate+Waitlists.swift @@ -0,0 +1,97 @@ +// +// AppDelegate+Waitlists.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import BackgroundTasks +import NetworkProtection + +extension AppDelegate { + + func checkWaitlists() { + checkWindowsWaitlist() + +#if NETWORK_PROTECTION + checkNetworkProtectionWaitlist() +#endif + checkWaitlistBackgroundTasks() + + } + + private func checkWindowsWaitlist() { + WindowsBrowserWaitlist.shared.fetchInviteCodeIfAvailable { error in + guard error == nil else { return } + WindowsBrowserWaitlist.shared.sendInviteCodeAvailableNotification() + } + } + +#if NETWORK_PROTECTION + private func checkNetworkProtectionWaitlist() { + VPNWaitlist.shared.fetchInviteCodeIfAvailable { [weak self] error in + guard error == nil else { +#if !DEBUG + // If the user already has an invite code but their auth token has gone missing, attempt to redeem it again. + let tokenStore = NetworkProtectionKeychainTokenStore() + let waitlistStorage = VPNWaitlist.shared.waitlistStorage + if error == .alreadyHasInviteCode, + let inviteCode = waitlistStorage.getWaitlistInviteCode(), + !tokenStore.isFeatureActivated { + self?.fetchVPNWaitlistAuthToken(inviteCode: inviteCode) + } +#endif + return + + } + + guard let inviteCode = VPNWaitlist.shared.waitlistStorage.getWaitlistInviteCode() else { + return + } + + self?.fetchVPNWaitlistAuthToken(inviteCode: inviteCode) + } + } +#endif + + private func checkWaitlistBackgroundTasks() { + BGTaskScheduler.shared.getPendingTaskRequests { tasks in + let hasWindowsBrowserWaitlistTask = tasks.contains { $0.identifier == WindowsBrowserWaitlist.backgroundRefreshTaskIdentifier } + if !hasWindowsBrowserWaitlistTask { + WindowsBrowserWaitlist.shared.scheduleBackgroundRefreshTask() + } + +#if NETWORK_PROTECTION + let hasVPNWaitlistTask = tasks.contains { $0.identifier == VPNWaitlist.backgroundRefreshTaskIdentifier } + if !hasVPNWaitlistTask { + VPNWaitlist.shared.scheduleBackgroundRefreshTask() + } +#endif + } + } + +#if NETWORK_PROTECTION + func fetchVPNWaitlistAuthToken(inviteCode: String) { + Task { + do { + try await NetworkProtectionCodeRedemptionCoordinator().redeem(inviteCode) + VPNWaitlist.shared.sendInviteCodeAvailableNotification() + } catch {} + } + } +#endif + +} diff --git a/DuckDuckGo/AppDelegate.swift b/DuckDuckGo/AppDelegate.swift index 604b6db7f2..f9671de5dd 100644 --- a/DuckDuckGo/AppDelegate.swift +++ b/DuckDuckGo/AppDelegate.swift @@ -21,6 +21,7 @@ import UIKit import Combine import Common import Core +import CoreData import UserNotifications import Kingfisher import WidgetKit @@ -34,6 +35,10 @@ import Networking import DDGSync import SyncDataProviders +#if NETWORK_PROTECTION +import NetworkProtection +#endif + // swiftlint:disable file_length // swiftlint:disable type_body_length @@ -45,6 +50,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate { private struct ShortcutKey { static let clipboard = "com.duckduckgo.mobile.ios.clipboard" + +#if NETWORK_PROTECTION + static let openVPNSettings = "com.duckduckgo.mobile.ios.vpn.open-settings" +#endif } private var testing = false @@ -54,7 +63,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate { private lazy var privacyStore = PrivacyUserDefaults() private var bookmarksDatabase: CoreDataDatabase = BookmarksDatabase.make() + +#if APP_TRACKING_PROTECTION private var appTrackingProtectionDatabase: CoreDataDatabase = AppTrackingProtectionDatabase.make() +#endif + +#if NETWORK_PROTECTION + private let widgetRefreshModel = NetworkProtectionWidgetRefreshModel() +#endif + private var autoClear: AutoClear? private var showKeyboardIfSettingOn = true private var lastBackgroundDate: Date? @@ -80,6 +97,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } #endif + ContentBlocking.shared.onCriticalError = presentPreemptiveCrashAlert + // Can be removed after a couple of versions cleanUpMacPromoExperiment2() cleanUpIncrementalRolloutPixelTest() @@ -115,6 +134,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { removeEmailWaitlistState() + var shouldPresentInsufficientDiskSpaceAlertAndCrash = false Database.shared.loadStore { context, error in guard let context = context else { @@ -134,14 +154,44 @@ class AppDelegate: UIResponder, UIApplicationDelegate { Pixel.fire(pixel: .dbInitializationError, error: error, withAdditionalParameters: parameters) - Thread.sleep(forTimeInterval: 1) - fatalError("Could not create database stack: \(error.localizedDescription)") + if error.isDiskFull { + shouldPresentInsufficientDiskSpaceAlertAndCrash = true + return + } else { + Thread.sleep(forTimeInterval: 1) + fatalError("Could not create database stack: \(error.localizedDescription)") + } } } DatabaseMigration.migrate(to: context) } - bookmarksDatabase.loadStore { context, error in + var shouldResetBookmarksSyncTimestamp = false + + let preMigrationErrorHandling = EventMapping { _, error, _, _ in + if let error = error { + Pixel.fire(pixel: .bookmarksCouldNotLoadDatabase, + error: error) + } else { + Pixel.fire(pixel: .bookmarksCouldNotLoadDatabase) + } + + if shouldPresentInsufficientDiskSpaceAlertAndCrash { + return + } else { + Thread.sleep(forTimeInterval: 1) + fatalError("Could not create Bookmarks database stack: \(error?.localizedDescription ?? "err")") + } + } + + let oldFavoritesOrder = BookmarkFormFactorFavoritesMigration + .getFavoritesOrderFromPreV4Model( + dbContainerLocation: BookmarksDatabase.defaultDBLocation, + dbFileURL: BookmarksDatabase.defaultDBFileURL, + errorEvents: preMigrationErrorHandling + ) + + bookmarksDatabase.loadStore { [weak self] context, error in guard let context = context else { if let error = error { Pixel.fire(pixel: .bookmarksCouldNotLoadDatabase, @@ -150,8 +200,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate { Pixel.fire(pixel: .bookmarksCouldNotLoadDatabase) } - Thread.sleep(forTimeInterval: 1) - fatalError("Could not create Bookmarks database stack: \(error?.localizedDescription ?? "err")") + if shouldPresentInsufficientDiskSpaceAlertAndCrash { + return + } else { + Thread.sleep(forTimeInterval: 1) + fatalError("Could not create Bookmarks database stack: \(error?.localizedDescription ?? "err")") + } } let legacyStorage = LegacyBookmarksCoreDataStorage() @@ -160,9 +214,27 @@ class AppDelegate: UIResponder, UIApplicationDelegate { to: context) legacyStorage?.removeStore() + do { + BookmarkFormFactorFavoritesMigration.migrateToFormFactorSpecificFavorites(byCopyingExistingTo: .mobile, + preservingOrderOf: oldFavoritesOrder, + in: context) + if context.hasChanges { + try context.save(onErrorFire: .bookmarksMigrationCouldNotPrepareMultipleFavoriteFolders) + if let syncDataProviders = self?.syncDataProviders { + syncDataProviders.bookmarksAdapter.shouldResetBookmarksSyncTimestamp = true + } else { + shouldResetBookmarksSyncTimestamp = true + } + } + } catch { + Thread.sleep(forTimeInterval: 1) + fatalError("Could not prepare Bookmarks DB structure") + } + WidgetCenter.shared.reloadAllTimelines() } +#if APP_TRACKING_PROTECTION appTrackingProtectionDatabase.loadStore { context, error in guard context != nil else { if let error = error { @@ -171,10 +243,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate { Pixel.fire(pixel: .appTPCouldNotLoadDatabase) } - Thread.sleep(forTimeInterval: 1) - fatalError("Could not create AppTP database stack: \(error?.localizedDescription ?? "err")") + if shouldPresentInsufficientDiskSpaceAlertAndCrash { + return + } else { + Thread.sleep(forTimeInterval: 1) + fatalError("Could not create AppTP database stack: \(error?.localizedDescription ?? "err")") + } } } +#endif Favicons.shared.migrateFavicons(to: Favicons.Constants.maxFaviconSize) { WidgetCenter.shared.reloadAllTimelines() @@ -205,22 +282,43 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ).wrappedValue ) ?? defaultEnvironment - syncDataProviders = SyncDataProviders(bookmarksDatabase: bookmarksDatabase, secureVaultErrorReporter: SecureVaultErrorReporter.shared) + syncDataProviders = SyncDataProviders( + bookmarksDatabase: bookmarksDatabase, + secureVaultErrorReporter: SecureVaultErrorReporter.shared, + settingHandlers: [FavoritesDisplayModeSyncHandler()], + favoritesDisplayModeStorage: FavoritesDisplayModeStorage() + ) + syncDataProviders.bookmarksAdapter.shouldResetBookmarksSyncTimestamp = shouldResetBookmarksSyncTimestamp + let syncService = DDGSync(dataProvidersSource: syncDataProviders, errorEvents: SyncErrorHandler(), log: .syncLog, environment: environment) syncService.initializeIfNeeded() self.syncService = syncService +#if APP_TRACKING_PROTECTION let main = MainViewController(bookmarksDatabase: bookmarksDatabase, bookmarksDatabaseCleaner: syncDataProviders.bookmarksAdapter.databaseCleaner, appTrackingProtectionDatabase: appTrackingProtectionDatabase, syncService: syncService, - syncDataProviders: syncDataProviders) + syncDataProviders: syncDataProviders, + appSettings: AppDependencyProvider.shared.appSettings) +#else + let main = MainViewController(bookmarksDatabase: bookmarksDatabase, + bookmarksDatabaseCleaner: syncDataProviders.bookmarksAdapter.databaseCleaner, + syncService: syncService, + syncDataProviders: syncDataProviders, + appSettings: AppDependencyProvider.shared.appSettings) +#endif + main.loadViewIfNeeded() window = UIWindow(frame: UIScreen.main.bounds) window?.rootViewController = main window?.makeKeyAndVisible() - + + if shouldPresentInsufficientDiskSpaceAlertAndCrash { + presentInsufficientDiskSpaceAlert() + } + autoClear = AutoClear(worker: main) autoClear?.applicationDidLaunch() @@ -232,7 +330,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate { // Having both in `didBecomeActive` can sometimes cause the exception when running on a physical device, so registration happens here. AppConfigurationFetch.registerBackgroundRefreshTaskHandler() WindowsBrowserWaitlist.shared.registerBackgroundRefreshTaskHandler() - RemoteMessaging.registerBackgroundRefreshTaskHandler(bookmarksDatabase: bookmarksDatabase) + +#if NETWORK_PROTECTION + VPNWaitlist.shared.registerBackgroundRefreshTaskHandler() +#endif + + RemoteMessaging.registerBackgroundRefreshTaskHandler( + bookmarksDatabase: bookmarksDatabase, + favoritesDisplayMode: AppDependencyProvider.shared.appSettings.favoritesDisplayMode + ) UNUserNotificationCenter.current().delegate = self @@ -246,9 +352,26 @@ class AppDelegate: UIResponder, UIApplicationDelegate { AppDependencyProvider.shared.appSettings.setAutofillIsNewInstallForOnByDefault() } +#if NETWORK_PROTECTION + widgetRefreshModel.beginObservingVPNStatus() + NetworkProtectionAccessController().refreshNetworkProtectionAccess() +#endif + return true } + private func presentPreemptiveCrashAlert() { + Task { @MainActor in + let alertController = CriticalAlerts.makePreemptiveCrashAlert() + window?.rootViewController?.present(alertController, animated: true, completion: nil) + } + } + + private func presentInsufficientDiskSpaceAlert() { + let alertController = CriticalAlerts.makeInsufficientDiskSpaceAlert() + window?.rootViewController?.present(alertController, animated: true, completion: nil) + } + private func cleanUpMacPromoExperiment2() { UserDefaults.standard.removeObject(forKey: "com.duckduckgo.ios.macPromoMay23.exp2.cohort") } @@ -314,20 +437,18 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } } - WindowsBrowserWaitlist.shared.fetchInviteCodeIfAvailable { error in - guard error == nil else { return } - WindowsBrowserWaitlist.shared.sendInviteCodeAvailableNotification() - } - - BGTaskScheduler.shared.getPendingTaskRequests { tasks in - let hasWindowsBrowserWaitlistTask = tasks.contains { $0.identifier == WindowsBrowserWaitlist.backgroundRefreshTaskIdentifier } - if !hasWindowsBrowserWaitlistTask { - WindowsBrowserWaitlist.shared.scheduleBackgroundRefreshTask() - } - } - + checkWaitlists() syncService.scheduler.notifyAppLifecycleEvent() fireFailedCompilationsPixelIfNeeded() + refreshShortcuts() + +#if NETWORK_PROTECTION + widgetRefreshModel.refreshVPNWidget() +#endif + } + + func applicationWillResignActive(_ application: UIApplication) { + refreshShortcuts() } private func fireAppLaunchPixel() { @@ -419,7 +540,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate { private func refreshRemoteMessages() { Task { - try? await RemoteMessaging.fetchAndProcess(bookmarksDatabase: self.bookmarksDatabase) + try? await RemoteMessaging.fetchAndProcess( + bookmarksDatabase: self.bookmarksDatabase, + favoritesDisplayMode: AppDependencyProvider.shared.appSettings.favoritesDisplayMode + ) } } @@ -476,7 +600,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } NotificationCenter.default.post(name: AutofillLoginListAuthenticator.Notifications.invalidateContext, object: nil) - mainViewController?.clearNavigationStack() + + // The openVPN action handles the navigation stack on its own and does not need it to be cleared + if url != AppDeepLinkSchemes.openVPN.url { + mainViewController?.clearNavigationStack() + } + autoClear?.applicationWillMoveToForeground() showKeyboardIfSettingOn = false @@ -543,9 +672,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { overlayWindow = UIWindow(frame: frame) overlayWindow?.windowLevel = UIWindow.Level.alert - let overlay = BlankSnapshotViewController.loadFromStoryboard() + let overlay = BlankSnapshotViewController(appSettings: AppDependencyProvider.shared.appSettings) overlay.delegate = self - + overlayWindow?.rootViewController = overlay overlayWindow?.makeKeyAndVisible() window?.isHidden = true @@ -590,11 +719,20 @@ class AppDelegate: UIResponder, UIApplicationDelegate { private func handleShortCutItem(_ shortcutItem: UIApplicationShortcutItem) { os_log("Handling shortcut item: %s", log: .generalLog, type: .debug, shortcutItem.type) - mainViewController?.clearNavigationStack() + autoClear?.applicationWillMoveToForeground() - if shortcutItem.type == ShortcutKey.clipboard, let query = UIPasteboard.general.string { + + if shortcutItem.type == ShortcutKey.clipboard, let query = UIPasteboard.general.string { + mainViewController?.clearNavigationStack() mainViewController?.loadQueryInNewTab(query) + return } + +#if NETWORK_PROTECTION + if shortcutItem.type == ShortcutKey.openVPNSettings { + presentNetworkProtectionStatusSettingsModal() + } +#endif } private func removeEmailWaitlistState() { @@ -622,6 +760,25 @@ class AppDelegate: UIResponder, UIApplicationDelegate { private var mainViewController: MainViewController? { return window?.rootViewController as? MainViewController } + + func refreshShortcuts() { +#if NETWORK_PROTECTION + guard NetworkProtectionKeychainTokenStore().isFeatureActivated else { + return + } + + let items = [ + UIApplicationShortcutItem(type: ShortcutKey.openVPNSettings, + localizedTitle: UserText.netPOpenVPNQuickAction, + localizedSubtitle: nil, + icon: UIApplicationShortcutIcon(templateImageName: "VPN-16"), + userInfo: nil) + ] + + UIApplication.shared.shortcutItems = items +#endif + } + } extension AppDelegate: BlankSnapshotViewRecoveringDelegate { @@ -676,7 +833,7 @@ extension AppDelegate: UNUserNotificationCenterDelegate { withCompletionHandler completionHandler: @escaping () -> Void) { if response.actionIdentifier == UNNotificationDefaultActionIdentifier { let identifier = response.notification.request.identifier - if identifier == WindowsBrowserWaitlist.notificationIdentitier { + if identifier == WindowsBrowserWaitlist.notificationIdentifier { presentWindowsBrowserWaitlistSettingsModal() } @@ -684,6 +841,10 @@ extension AppDelegate: UNUserNotificationCenterDelegate { if NetworkProtectionNotificationIdentifier(rawValue: identifier) != nil { presentNetworkProtectionStatusSettingsModal() } + + if identifier == VPNWaitlist.notificationIdentifier { + presentNetworkProtectionWaitlistModal() + } #endif } @@ -696,15 +857,37 @@ extension AppDelegate: UNUserNotificationCenterDelegate { } #if NETWORK_PROTECTION - private func presentNetworkProtectionStatusSettingsModal() { - let networkProtectionRoot = NetworkProtectionRootViewController() - presentSettings(with: networkProtectionRoot) + private func presentNetworkProtectionWaitlistModal() { + if #available(iOS 15, *) { + let networkProtectionRoot = VPNWaitlistViewController(nibName: nil, bundle: nil) + presentSettings(with: networkProtectionRoot) + } + } + + func presentNetworkProtectionStatusSettingsModal() { + if #available(iOS 15, *) { + let networkProtectionRoot = NetworkProtectionRootViewController() + presentSettings(with: networkProtectionRoot) + } } #endif private func presentSettings(with viewController: UIViewController) { guard let window = window, let rootViewController = window.rootViewController as? MainViewController else { return } + if let navigationController = rootViewController.presentedViewController as? UINavigationController { + if let lastViewController = navigationController.viewControllers.last, lastViewController.isKind(of: type(of: viewController)) { + // Avoid presenting dismissing and re-presenting the view controller if it's already visible: + return + } else { + // Otherwise, replace existing view controllers with the presented one: + navigationController.popToRootViewController(animated: false) + navigationController.pushViewController(viewController, animated: false) + return + } + } + + // If the previous checks failed, make sure the nav stack is reset and present the view controller from scratch: rootViewController.clearNavigationStack() // Give the `clearNavigationStack` call time to complete. @@ -716,3 +899,15 @@ extension AppDelegate: UNUserNotificationCenterDelegate { } } } + +private extension Error { + + var isDiskFull: Bool { + let nsError = self as NSError + if let underlyingError = nsError.userInfo["NSUnderlyingError"] as? NSError, underlyingError.code == 13 { + return true + } + return false + } + +} diff --git a/DuckDuckGo/AppDependencyProvider.swift b/DuckDuckGo/AppDependencyProvider.swift index 3b42c75074..5f7854281c 100644 --- a/DuckDuckGo/AppDependencyProvider.swift +++ b/DuckDuckGo/AppDependencyProvider.swift @@ -34,6 +34,7 @@ protocol DependencyProvider { var voiceSearchHelper: VoiceSearchHelperProtocol { get } var downloadManager: DownloadManager { get } var autofillLoginSession: AutofillLoginSession { get } + var autofillNeverPromptWebsitesManager: AutofillNeverPromptWebsitesManager { get } var configurationManager: ConfigurationManager { get } } @@ -56,6 +57,7 @@ class AppDependencyProvider: DependencyProvider { let voiceSearchHelper: VoiceSearchHelperProtocol = VoiceSearchHelper() let downloadManager = DownloadManager() let autofillLoginSession = AutofillLoginSession() + lazy var autofillNeverPromptWebsitesManager = AutofillNeverPromptWebsitesManager() let configurationManager = ConfigurationManager() } diff --git a/DuckDuckGo/AppSettings.swift b/DuckDuckGo/AppSettings.swift index d79c06e746..0c3f6dd748 100644 --- a/DuckDuckGo/AppSettings.swift +++ b/DuckDuckGo/AppSettings.swift @@ -17,6 +17,8 @@ // limitations under the License. // +import Bookmarks + protocol AppSettings: AnyObject { var autocomplete: Bool { get set } var currentThemeName: ThemeName { get set } @@ -31,8 +33,11 @@ protocol AppSettings: AnyObject { var sendDoNotSell: Bool { get set } var currentFireButtonAnimation: FireButtonAnimationType { get set } - + var currentAddressBarPosition: AddressBarPosition { get set } + var textSize: Int { get set } + + var favoritesDisplayMode: FavoritesDisplayMode { get set } var autofillCredentialsEnabled: Bool { get set } var autofillCredentialsSavePromptShowAtLeastOnce: Bool { get set } @@ -46,4 +51,7 @@ protocol AppSettings: AnyObject { var autoconsentPromptSeen: Bool { get set } var autoconsentEnabled: Bool { get set } + + var isSyncBookmarksPaused: Bool { get } + var isSyncCredentialsPaused: Bool { get } } diff --git a/DuckDuckGo/AppUserDefaults.swift b/DuckDuckGo/AppUserDefaults.swift index 0a7d98d880..552e381a02 100644 --- a/DuckDuckGo/AppUserDefaults.swift +++ b/DuckDuckGo/AppUserDefaults.swift @@ -18,6 +18,7 @@ // import Foundation +import Bookmarks import Core import WidgetKit @@ -27,14 +28,18 @@ public class AppUserDefaults: AppSettings { public static let doNotSellStatusChange = Notification.Name("com.duckduckgo.app.DoNotSellStatusChange") public static let currentFireButtonAnimationChange = Notification.Name("com.duckduckgo.app.CurrentFireButtonAnimationChange") public static let textSizeChange = Notification.Name("com.duckduckgo.app.TextSizeChange") + public static let favoritesDisplayModeChange = Notification.Name("com.duckduckgo.app.FavoritesDisplayModeChange") + public static let syncPausedStateChanged = SyncBookmarksAdapter.syncBookmarksPausedStateChanged + public static let syncCredentialsPausedStateChanged = SyncCredentialsAdapter.syncCredentialsPausedStateChanged public static let autofillEnabledChange = Notification.Name("com.duckduckgo.app.AutofillEnabledChange") public static let didVerifyInternalUser = Notification.Name("com.duckduckgo.app.DidVerifyInternalUser") public static let inspectableWebViewsToggled = Notification.Name("com.duckduckgo.app.DidToggleInspectableWebViews") + public static let addressBarPositionChanged = Notification.Name("com.duckduckgo.app.AddressBarPositionChanged") } private let groupName: String - private struct Keys { + struct Keys { static let autocompleteKey = "com.duckduckgo.app.autocompleteDisabledKey" static let currentThemeNameKey = "com.duckduckgo.app.currentThemeNameKey" @@ -61,6 +66,8 @@ public class AppUserDefaults: AppSettings { static let autofillCredentialsEnabled = "com.duckduckgo.ios.autofillCredentialsEnabled" static let autofillIsNewInstallForOnByDefault = "com.duckduckgo.ios.autofillIsNewInstallForOnByDefault" + + static let favoritesDisplayMode = "com.duckduckgo.ios.favoritesDisplayMode" } private struct DebugKeys { @@ -71,6 +78,10 @@ public class AppUserDefaults: AppSettings { return UserDefaults(suiteName: groupName) } + private var bookmarksUserDefaults: UserDefaults? { + UserDefaults(suiteName: "group.com.duckduckgo.bookmarks") + } + lazy var featureFlagger = AppDependencyProvider.shared.featureFlagger init(groupName: String = "group.com.duckduckgo.app") { @@ -176,10 +187,43 @@ public class AppUserDefaults: AppSettings { userDefaults?.setValue(newValue.rawValue, forKey: Keys.currentFireButtonAnimationKey) } } - + + @UserDefaultsWrapper(key: .addressBarPosition, defaultValue: nil) + private var addressBarPositionStorage: String? + + var currentAddressBarPosition: AddressBarPosition { + get { + return AddressBarPosition(rawValue: addressBarPositionStorage?.lowercased() ?? "") ?? .top + } + + set { + addressBarPositionStorage = newValue.rawValue + NotificationCenter.default.post(name: Notifications.addressBarPositionChanged, object: currentAddressBarPosition) + } + } + @UserDefaultsWrapper(key: .textSize, defaultValue: 100) var textSize: Int + @UserDefaultsWrapper(key: .syncBookmarksPaused, defaultValue: false) + var isSyncBookmarksPaused: Bool + + @UserDefaultsWrapper(key: .syncCredentialsPaused, defaultValue: false) + var isSyncCredentialsPaused: Bool + + public var favoritesDisplayMode: FavoritesDisplayMode { + get { + guard let string = userDefaults?.string(forKey: Keys.favoritesDisplayMode), let favoritesDisplayMode = FavoritesDisplayMode(string) else { + return .default + } + return favoritesDisplayMode + } + set { + userDefaults?.setValue(newValue.description, forKey: Keys.favoritesDisplayMode) + bookmarksUserDefaults?.setValue(newValue.description, forKey: Keys.favoritesDisplayMode) + } + } + private func setAutofillCredentialsEnabledAutomaticallyIfNecessary() { if autofillCredentialsHasBeenEnabledAutomaticallyIfNecessary { return @@ -208,7 +252,7 @@ public class AppUserDefaults: AppSettings { userDefaults?.set(newValue, forKey: Keys.autofillCredentialsEnabled) } } - + @UserDefaultsWrapper(key: .autofillCredentialsSavePromptShowAtLeastOnce, defaultValue: false) var autofillCredentialsSavePromptShowAtLeastOnce: Bool diff --git a/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/Arrow-Down-Left-24.imageset/Arrow-Down-Left-24.svg b/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/Arrow-Down-Left-24.imageset/Arrow-Down-Left-24.svg new file mode 100644 index 0000000000..a2d3aa85a9 --- /dev/null +++ b/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/Arrow-Down-Left-24.imageset/Arrow-Down-Left-24.svg @@ -0,0 +1,3 @@ + + + diff --git a/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/Arrow-Down-Left-24.imageset/Contents.json b/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/Arrow-Down-Left-24.imageset/Contents.json new file mode 100644 index 0000000000..e7a4bdced3 --- /dev/null +++ b/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/Arrow-Down-Left-24.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "Arrow-Down-Left-24.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/Share-24.imageset/Contents.json b/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/Share-24.imageset/Contents.json index baf2e36c14..75bb7a584e 100644 --- a/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/Share-24.imageset/Contents.json +++ b/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/Share-24.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "Share-24.pdf", + "filename" : "Share-Apple-24.svg", "idiom" : "universal" } ], diff --git a/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/Share-24.imageset/Share-24.pdf b/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/Share-24.imageset/Share-24.pdf deleted file mode 100644 index 0031e2fa91..0000000000 Binary files a/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/Share-24.imageset/Share-24.pdf and /dev/null differ diff --git a/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/Share-24.imageset/Share-Apple-24.svg b/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/Share-24.imageset/Share-Apple-24.svg new file mode 100644 index 0000000000..4615b33b2c --- /dev/null +++ b/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/Share-24.imageset/Share-Apple-24.svg @@ -0,0 +1,4 @@ + + + + diff --git a/DuckDuckGo/Assets.xcassets/VPN-16.imageset/Contents.json b/DuckDuckGo/Assets.xcassets/VPN-16.imageset/Contents.json new file mode 100644 index 0000000000..eb05efadf9 --- /dev/null +++ b/DuckDuckGo/Assets.xcassets/VPN-16.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "VPN-16.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/DuckDuckGo/Assets.xcassets/VPN-16.imageset/VPN-16.pdf b/DuckDuckGo/Assets.xcassets/VPN-16.imageset/VPN-16.pdf new file mode 100644 index 0000000000..9b953299d8 Binary files /dev/null and b/DuckDuckGo/Assets.xcassets/VPN-16.imageset/VPN-16.pdf differ diff --git a/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/Card-16.imageset/Card-16.pdf b/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/Card-16.imageset/Card-16.pdf new file mode 100644 index 0000000000..ead0016185 Binary files /dev/null and b/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/Card-16.imageset/Card-16.pdf differ diff --git a/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/Card-16.imageset/Contents.json b/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/Card-16.imageset/Contents.json new file mode 100644 index 0000000000..0030995444 --- /dev/null +++ b/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/Card-16.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Card-16.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/Contents.json b/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/Contents.json new file mode 100644 index 0000000000..73c00596a7 --- /dev/null +++ b/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/InvitedVPNWaitlist.imageset/Contents.json b/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/InvitedVPNWaitlist.imageset/Contents.json new file mode 100644 index 0000000000..0223097db0 --- /dev/null +++ b/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/InvitedVPNWaitlist.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Gift-96.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/InvitedVPNWaitlist.imageset/Gift-96.pdf b/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/InvitedVPNWaitlist.imageset/Gift-96.pdf new file mode 100644 index 0000000000..41180d6d40 Binary files /dev/null and b/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/InvitedVPNWaitlist.imageset/Gift-96.pdf differ diff --git a/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/JoinVPNWaitlist.imageset/Contents.json b/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/JoinVPNWaitlist.imageset/Contents.json new file mode 100644 index 0000000000..e9f9e938f0 --- /dev/null +++ b/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/JoinVPNWaitlist.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Network-Protection-VPN-96.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/JoinVPNWaitlist.imageset/Network-Protection-VPN-96.pdf b/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/JoinVPNWaitlist.imageset/Network-Protection-VPN-96.pdf new file mode 100644 index 0000000000..2e31d6e5ae Binary files /dev/null and b/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/JoinVPNWaitlist.imageset/Network-Protection-VPN-96.pdf differ diff --git a/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/JoinedVPNWaitlist.imageset/Contents.json b/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/JoinedVPNWaitlist.imageset/Contents.json new file mode 100644 index 0000000000..3d42417002 --- /dev/null +++ b/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/JoinedVPNWaitlist.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Success-96.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/JoinedVPNWaitlist.imageset/Success-96.pdf b/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/JoinedVPNWaitlist.imageset/Success-96.pdf new file mode 100644 index 0000000000..98e6385c24 Binary files /dev/null and b/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/JoinedVPNWaitlist.imageset/Success-96.pdf differ diff --git a/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/Rocket-16.imageset/Contents.json b/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/Rocket-16.imageset/Contents.json new file mode 100644 index 0000000000..b23b52c784 --- /dev/null +++ b/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/Rocket-16.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Rocket-16.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/Rocket-16.imageset/Rocket-16.pdf b/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/Rocket-16.imageset/Rocket-16.pdf new file mode 100644 index 0000000000..c7dd245c4f Binary files /dev/null and b/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/Rocket-16.imageset/Rocket-16.pdf differ diff --git a/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/Shield-16.imageset/Contents.json b/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/Shield-16.imageset/Contents.json new file mode 100644 index 0000000000..510acba745 --- /dev/null +++ b/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/Shield-16.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Shield-16.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/Shield-16.imageset/Shield-16.pdf b/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/Shield-16.imageset/Shield-16.pdf new file mode 100644 index 0000000000..677edb434b Binary files /dev/null and b/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/Shield-16.imageset/Shield-16.pdf differ diff --git a/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/VPN-Ended.imageset/Contents.json b/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/VPN-Ended.imageset/Contents.json new file mode 100644 index 0000000000..b87a2c36f5 --- /dev/null +++ b/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/VPN-Ended.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "VPN-Ended.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/VPN-Ended.imageset/VPN-Ended.pdf b/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/VPN-Ended.imageset/VPN-Ended.pdf new file mode 100644 index 0000000000..a34830d0e0 Binary files /dev/null and b/DuckDuckGo/Assets.xcassets/Waitlist/VPN Waitlist/VPN-Ended.imageset/VPN-Ended.pdf differ diff --git a/DuckDuckGo/AutocompleteViewController.swift b/DuckDuckGo/AutocompleteViewController.swift index 6113b67a2d..cc13fc7ead 100644 --- a/DuckDuckGo/AutocompleteViewController.swift +++ b/DuckDuckGo/AutocompleteViewController.swift @@ -43,9 +43,17 @@ class AutocompleteViewController: UIViewController { private var bookmarksSearch: BookmarksStringSearch! + private var appSettings: AppSettings! + + var backgroundColor: UIColor { + appSettings.currentAddressBarPosition.isBottom ? + UIColor(designSystemColor: .background) : + UIColor.black.withAlphaComponent(0.2) + } + var showBackground = true { didSet { - view.backgroundColor = showBackground ? UIColor.black.withAlphaComponent(0.2) : UIColor.clear + view.backgroundColor = showBackground ? backgroundColor : UIColor.clear } } @@ -61,12 +69,15 @@ class AutocompleteViewController: UIViewController { @IBOutlet weak var tableView: UITableView! var shouldOffsetY = false - static func loadFromStoryboard(bookmarksSearch: BookmarksStringSearch) -> AutocompleteViewController { + static func loadFromStoryboard(bookmarksSearch: BookmarksStringSearch, + appSettings: AppSettings = AppDependencyProvider.shared.appSettings) -> AutocompleteViewController { let storyboard = UIStoryboard(name: "Autocomplete", bundle: nil) + guard let controller = storyboard.instantiateInitialViewController() as? AutocompleteViewController else { fatalError("Failed to instatiate correct Autocomplete view controller") } controller.bookmarksSearch = bookmarksSearch + controller.appSettings = appSettings return controller } @@ -101,13 +112,18 @@ class AutocompleteViewController: UIViewController { override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) - resetNaviagtionBar() + resetNavigationBar() } - private func resetNaviagtionBar() { + private func resetNavigationBar() { navigationController?.hidesBarsOnSwipe = hidesBarsOnSwipeDefault } + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { + super.traitCollectionDidChange(previousTraitCollection) + tableView.reloadData() + } + func updateQuery(query: String) { self.query = query selectedItem = -1 @@ -210,12 +226,16 @@ extension AutocompleteViewController: UITableViewDataSource { let currentTheme = ThemeManager.shared.currentTheme - cell.updateFor(query: query, suggestion: suggestions[indexPath.row], with: currentTheme) + cell.updateFor(query: query, + suggestion: suggestions[indexPath.row], + with: currentTheme, + isAddressBarAtBottom: appSettings.currentAddressBarPosition.isBottom) cell.plusButton.tag = indexPath.row - let color = indexPath.row == selectedItem ? currentTheme.tableCellSelectedColor : UIColor(designSystemColor: .panel) - - cell.backgroundColor = color + let baseBackgroundColor = isPad ? UIColor(designSystemColor: .panel) : UIColor(designSystemColor: .background) + let backgroundColor = indexPath.row == selectedItem ? currentTheme.tableCellSelectedColor : baseBackgroundColor + + cell.backgroundColor = backgroundColor cell.tintColor = currentTheme.autocompleteCellAccessoryColor cell.setHighlightedStateBackgroundColor(currentTheme.tableCellHighlightedBackgroundColor) @@ -229,14 +249,24 @@ extension AutocompleteViewController: UITableViewDataSource { } let currentTheme = ThemeManager.shared.currentTheme - cell.backgroundColor = UIColor(designSystemColor: .panel) + cell.backgroundColor = appSettings.currentAddressBarPosition.isBottom ? + UIColor(designSystemColor: .background) : + UIColor(designSystemColor: .panel) + cell.tintColor = currentTheme.autocompleteCellAccessoryColor cell.label?.textColor = currentTheme.tableCellTextColor cell.setHighlightedStateBackgroundColor(currentTheme.tableCellHighlightedBackgroundColor) - + return cell } + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + if appSettings.currentAddressBarPosition.isBottom && suggestions.isEmpty { + return view.frame.height + } + return 46 + } + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return receivedResponse ? max(Constants.minItems, suggestions.count) : 0 } diff --git a/DuckDuckGo/AutocompleteViewControllerDelegate.swift b/DuckDuckGo/AutocompleteViewControllerDelegate.swift index d345970988..fbb0b6d9fb 100644 --- a/DuckDuckGo/AutocompleteViewControllerDelegate.swift +++ b/DuckDuckGo/AutocompleteViewControllerDelegate.swift @@ -28,6 +28,7 @@ protocol AutocompleteViewControllerDelegate: AnyObject { func autocomplete(pressedPlusButtonForSuggestion suggestion: Suggestion) func autocompleteWasDismissed() + } protocol AutocompleteViewControllerPresentationDelegate: AnyObject { diff --git a/DuckDuckGo/Autoconsent/autoconsent-bundle.js b/DuckDuckGo/Autoconsent/autoconsent-bundle.js index 7a95df5f7e..549a4a732c 100644 --- a/DuckDuckGo/Autoconsent/autoconsent-bundle.js +++ b/DuckDuckGo/Autoconsent/autoconsent-bundle.js @@ -1 +1 @@ -!function(){"use strict";var e=class e{static setBase(t){e.base=t}static findElement(t,o=null,c=!1){let i=null;return i=null!=o?Array.from(o.querySelectorAll(t.selector)):null!=e.base?Array.from(e.base.querySelectorAll(t.selector)):Array.from(document.querySelectorAll(t.selector)),null!=t.textFilter&&(i=i.filter((e=>{const o=e.textContent.toLowerCase();if(Array.isArray(t.textFilter)){let e=!1;for(const c of t.textFilter)if(-1!==o.indexOf(c.toLowerCase())){e=!0;break}return e}if(null!=t.textFilter)return-1!==o.indexOf(t.textFilter.toLowerCase())}))),null!=t.styleFilters&&(i=i.filter((e=>{const o=window.getComputedStyle(e);let c=!0;for(const e of t.styleFilters){const t=o[e.option];c=e.negated?c&&t!==e.value:c&&t===e.value}return c}))),null!=t.displayFilter&&(i=i.filter((e=>t.displayFilter?0!==e.offsetHeight:0===e.offsetHeight))),null!=t.iframeFilter&&(i=i.filter((()=>t.iframeFilter?window.location!==window.parent.location:window.location===window.parent.location))),null!=t.childFilter&&(i=i.filter((o=>{const c=e.base;e.setBase(o);const i=e.find(t.childFilter);return e.setBase(c),null!=i.target}))),c?i:(i.length>1&&console.warn("Multiple possible targets: ",i,t,o),i[0])}static find(t,o=!1){const c=[];if(null!=t.parent){const i=e.findElement(t.parent,null,o);if(null!=i){if(i instanceof Array)return i.forEach((i=>{const n=e.findElement(t.target,i,o);n instanceof Array?n.forEach((e=>{c.push({parent:i,target:e})})):c.push({parent:i,target:n})})),c;{const n=e.findElement(t.target,i,o);n instanceof Array?n.forEach((e=>{c.push({parent:i,target:e})})):c.push({parent:i,target:n})}}}else{const i=e.findElement(t.target,null,o);i instanceof Array?i.forEach((e=>{c.push({parent:null,target:e})})):c.push({parent:null,target:i})}return 0===c.length&&c.push({parent:null,target:null}),o?c:(1!==c.length&&console.warn("Multiple results found, even though multiple false",c),c[0])}};e.base=null;var t=e;function o(e){const o=t.find(e);return"css"===e.type?!!o.target:"checkbox"===e.type?!!o.target&&o.target.checked:void 0}async function c(e,a){switch(e.type){case"click":return async function(e){const o=t.find(e);null!=o.target&&o.target.click();return n(i)}(e);case"list":return async function(e,t){for(const o of e.actions)await c(o,t)}(e,a);case"consent":return async function(e,t){for(const i of e.consents){const e=-1!==t.indexOf(i.type);if(i.matcher&&i.toggleAction){o(i.matcher)!==e&&await c(i.toggleAction)}else e?await c(i.trueAction):await c(i.falseAction)}}(e,a);case"ifcss":return async function(e,o){const i=t.find(e);i.target?e.falseAction&&await c(e.falseAction,o):e.trueAction&&await c(e.trueAction,o)}(e,a);case"waitcss":return async function(e){await new Promise((o=>{let c=e.retries||10;const i=e.waitTime||250,n=()=>{const a=t.find(e);(e.negated&&a.target||!e.negated&&!a.target)&&c>0?(c-=1,setTimeout(n,i)):o()};n()}))}(e);case"foreach":return async function(e,o){const i=t.find(e,!0),n=t.base;for(const n of i)n.target&&(t.setBase(n.target),await c(e.action,o));t.setBase(n)}(e,a);case"hide":return async function(e){const o=t.find(e);o.target&&o.target.classList.add("Autoconsent-Hidden")}(e);case"slide":return async function(e){const o=t.find(e),c=t.find(e.dragTarget);if(o.target){const e=o.target.getBoundingClientRect(),t=c.target.getBoundingClientRect();let i=t.top-e.top,n=t.left-e.left;"y"===this.config.axis.toLowerCase()&&(n=0),"x"===this.config.axis.toLowerCase()&&(i=0);const a=window.screenX+e.left+e.width/2,s=window.screenY+e.top+e.height/2,r=e.left+e.width/2,p=e.top+e.height/2,l=document.createEvent("MouseEvents");l.initMouseEvent("mousedown",!0,!0,window,0,a,s,r,p,!1,!1,!1,!1,0,o.target);const d=document.createEvent("MouseEvents");d.initMouseEvent("mousemove",!0,!0,window,0,a+n,s+i,r+n,p+i,!1,!1,!1,!1,0,o.target);const u=document.createEvent("MouseEvents");u.initMouseEvent("mouseup",!0,!0,window,0,a+n,s+i,r+n,p+i,!1,!1,!1,!1,0,o.target),o.target.dispatchEvent(l),await this.waitTimeout(10),o.target.dispatchEvent(d),await this.waitTimeout(10),o.target.dispatchEvent(u)}}(e);case"close":return async function(){window.close()}();case"wait":return async function(e){await n(e.waitTime)}(e);case"eval":return async function(e){return console.log("eval!",e.code),new Promise((t=>{try{e.async?(window.eval(e.code),setTimeout((()=>{t(window.eval("window.__consentCheckResult"))}),e.timeout||250)):t(window.eval(e.code))}catch(o){console.warn("eval error",o,e.code),t(!1)}}))}(e);default:throw"Unknown action type: "+e.type}}var i=0;function n(e){return new Promise((t=>{setTimeout((()=>{t()}),e)}))}function a(e="autoconsent-css-rules"){const t=`style#${e}`,o=document.querySelector(t);if(o&&o instanceof HTMLStyleElement)return o;{const t=document.head||document.getElementsByTagName("head")[0]||document.documentElement,o=document.createElement("style");return o.id=e,t.appendChild(o),o}}function s(e,t,o="display"){const c="opacity"===o?"opacity: 0":"display: none",i=`${t.join(",")} { ${c} !important; z-index: -1 !important; pointer-events: none !important; } `;return e instanceof HTMLStyleElement&&(e.innerText+=i,t.length>0)}async function r(e,t,o){const c=await e();return!c&&t>0?new Promise((c=>{setTimeout((async()=>{c(r(e,t-1,o))}),o)})):Promise.resolve(c)}function p(e){if(!e)return!1;if(null!==e.offsetParent)return!0;{const t=window.getComputedStyle(e);if("fixed"===t.position&&"none"!==t.display)return!0}return!1}function l(e,t=!1){const o=_(e);return o.length>0&&(t?o.forEach((e=>e.click())):o[0].click()),o.length>0}function d(e){return _(e).length>0}function u(e,t){const o=_(e),c=new Array(o.length);return o.forEach(((e,t)=>{c[t]=p(e)})),"none"===t?c.every((e=>!e)):0!==c.length&&("any"===t?c.some((e=>e)):c.every((e=>e)))}function m(e,t=1e4){return r((()=>_(e).length>0),Math.ceil(t/200),200)}async function h(e,t=1e4,o=!1){return await m(e,t),l(e,o)}function k(e){return new Promise((t=>{setTimeout((()=>{t(!0)}),e)}))}function b(e,t=document){if(e.startsWith("aria/"))return[];if(e.startsWith("xpath/")){const o=e.slice(6),c=document.evaluate(o,t,null,XPathResult.ANY_TYPE,null);let i=null;const n=[];for(;i=c.iterateNext();)n.push(i);return n}return e.startsWith("text/")||e.startsWith("pierce/")?[]:t.shadowRoot?Array.from(t.shadowRoot.querySelectorAll(e)):Array.from(t.querySelectorAll(e))}function _(e){return"string"==typeof e?b(e):function(e){let t,o=document;for(const c of e){if(t=b(c,o),0===t.length)return[];o=t[0]}return t}(e)}function g(){return crypto&&void 0!==crypto.randomUUID?crypto.randomUUID():Math.random().toString()}var y=class{constructor(e,t=1e3){this.id=e,this.promise=new Promise(((e,t)=>{this.resolve=e,this.reject=t})),this.timer=window.setTimeout((()=>{this.reject(new Error("timeout"))}),t)}},w={pending:new Map,sendContentMessage:null};var C={EVAL_0:()=>console.log(1),EVAL_CONSENTMANAGER_1:()=>window.__cmp&&"object"==typeof __cmp("getCMPData"),EVAL_CONSENTMANAGER_2:()=>!__cmp("consentStatus").userChoiceExists,EVAL_CONSENTMANAGER_3:()=>__cmp("setConsent",0),EVAL_CONSENTMANAGER_4:()=>__cmp("setConsent",1),EVAL_CONSENTMANAGER_5:()=>__cmp("consentStatus").userChoiceExists,EVAL_COOKIEBOT_1:()=>!0!==window.CookieConsent.hasResponse,EVAL_COOKIEBOT_2:()=>window.Cookiebot.dialog.submitConsent(),EVAL_COOKIEBOT_3:()=>endCookieProcess(),EVAL_COOKIEBOT_4:()=>!0===window.CookieConsent.declined,EVAL_KLARO_1:()=>klaro.getManager().config.services.every((e=>e.required||!klaro.getManager().consents[e.name])),EVAL_ONETRUST_1:()=>window.OnetrustActiveGroups.split(",").filter((e=>e.length>0)).length<=1,EVAL_TRUSTARC_TOP:()=>window&&window.truste&&"0"===window.truste.eu.bindMap.prefCookie,EVAL_ADROLL_0:()=>!document.cookie.includes("__adroll_fpc"),EVAL_AFFINITY_SERIF_COM_0:()=>document.cookie.includes("serif_manage_cookies_viewed")&&!document.cookie.includes("serif_allow_analytics"),EVAL_AXEPTIO_0:()=>document.cookie.includes("axeptio_authorized_vendors=%2C%2C"),EVAL_BING_0:()=>document.cookie.includes("AL=0")&&document.cookie.includes("AD=0")&&document.cookie.includes("SM=0"),EVAL_BORLABS_0:()=>!JSON.parse(decodeURIComponent(document.cookie.split(";").find((e=>-1!==e.indexOf("borlabs-cookie"))).split("=",2)[1])).consents.statistics,EVAL_BUNDESREGIERUNG_DE_0:()=>document.cookie.match("cookie-allow-tracking=0"),EVAL_CANVA_0:()=>!document.cookie.includes("gtm_fpc_engagement_event"),EVAL_CLICKIO_0:()=>document.cookie.includes("__lxG__consent__v2_daisybit="),EVAL_CLINCH_0:()=>document.cookie.includes("ctc_rejected=1"),EVAL_COINBASE_0:()=>JSON.parse(decodeURIComponent(document.cookie.match(/cm_(eu|default)_preferences=([0-9a-zA-Z\\{\\}\\[\\]%:]*);?/)[2])).consent.length<=1,EVAL_COMPLIANZ_BANNER_0:()=>document.cookie.includes("cmplz_banner-status=dismissed"),EVAL_COMPLIANZ_CATEGORIES_0:()=>!!document.cookie.match(/cmplz_[^=]+=deny/),EVAL_COMPLIANZ_OPTIN_0:()=>!!document.cookie.match(/cookieconsent_preferences_disabled=[^;]+/),EVAL_COOKIE_LAW_INFO_0:()=>CLI.disableAllCookies()||CLI.reject_close()||!0,EVAL_COOKIE_LAW_INFO_1:()=>-1===document.cookie.indexOf("cookielawinfo-checkbox-non-necessary=yes"),EVAL_COOKIE_MANAGER_POPUP_0:()=>!1===JSON.parse(document.cookie.split(";").find((e=>e.trim().startsWith("CookieLevel"))).split("=")[1]).social,EVAL_COOKIEALERT_0:()=>document.querySelector("body").removeAttribute("style")||!0,EVAL_COOKIEALERT_1:()=>document.querySelector("body").removeAttribute("style")||!0,EVAL_COOKIEALERT_2:()=>!0===window.CookieConsent.declined,EVAL_COOKIEFIRST_0:()=>{return!1===(e=JSON.parse(decodeURIComponent(document.cookie.split(";").find((e=>-1!==e.indexOf("cookiefirst"))).trim()).split("=")[1])).performance&&!1===e.functional&&!1===e.advertising;var e},EVAL_COOKIEFIRST_1:()=>document.querySelectorAll("button[data-cookiefirst-accent-color=true][role=checkbox]:not([disabled])").forEach((e=>"true"==e.getAttribute("aria-checked")&&e.click()))||!0,EVAL_COOKIEINFORMATION_0:()=>CookieInformation.declineAllCategories()||!0,EVAL_COOKIEINFORMATION_1:()=>CookieInformation.submitAllCategories()||!0,EVAL_COOKIEINFORMATION_2:()=>document.cookie.includes("CookieInformationConsent="),EVAL_DAILYMOTION_0:()=>!!document.cookie.match("dm-euconsent-v2"),EVAL_DSGVO_0:()=>!document.cookie.includes("sp_dsgvo_cookie_settings"),EVAL_DUNELM_0:()=>document.cookie.includes("cc_functional=0")&&document.cookie.includes("cc_targeting=0"),EVAL_ETSY_0:()=>document.querySelectorAll(".gdpr-overlay-body input").forEach((e=>{e.checked=!1}))||!0,EVAL_ETSY_1:()=>document.querySelector(".gdpr-overlay-view button[data-wt-overlay-close]").click()||!0,EVAL_EU_COOKIE_COMPLIANCE_0:()=>-1===document.cookie.indexOf("cookie-agreed=2"),EVAL_EU_COOKIE_LAW_0:()=>!document.cookie.includes("euCookie"),EVAL_EZOIC_0:()=>ezCMP.handleAcceptAllClick(),EVAL_EZOIC_1:()=>!!document.cookie.match(/ezCMPCookieConsent=[^;]+\|2=0\|3=0\|4=0/),EVAL_GOOGLE_0:()=>!!document.cookie.match(/SOCS=CAE/),EVAL_IUBENDA_0:()=>document.querySelectorAll(".purposes-item input[type=checkbox]:not([disabled])").forEach((e=>{e.checked&&e.click()}))||!0,EVAL_IUBENDA_1:()=>!!document.cookie.match(/_iub_cs-\d+=/),EVAL_JQUERY_COOKIEBAR_0:()=>!document.cookie.includes("cookies-state=accepted"),EVAL_MEDIAVINE_0:()=>document.querySelectorAll('[data-name="mediavine-gdpr-cmp"] input[type=checkbox]').forEach((e=>e.checked&&e.click()))||!0,EVAL_MICROSOFT_0:()=>Array.from(document.querySelectorAll("div > button")).filter((e=>e.innerText.match("Reject|Ablehnen")))[0].click()||!0,EVAL_MICROSOFT_1:()=>Array.from(document.querySelectorAll("div > button")).filter((e=>e.innerText.match("Accept|Annehmen")))[0].click()||!0,EVAL_MICROSOFT_2:()=>!!document.cookie.match("MSCC"),EVAL_MOOVE_0:()=>document.querySelectorAll("#moove_gdpr_cookie_modal input").forEach((e=>{e.disabled||"moove_gdpr_strict_cookies"===e.name||(e.checked=!1)}))||!0,EVAL_ONENINETWO_0:()=>document.cookie.includes("CC_ADVERTISING=NO")&&document.cookie.includes("CC_ANALYTICS=NO"),EVAL_PAYPAL_0:()=>!0===document.cookie.includes("cookie_prefs"),EVAL_PRIMEBOX_0:()=>!document.cookie.includes("cb-enabled=accepted"),EVAL_PUBTECH_0:()=>document.cookie.includes("euconsent-v2")&&(document.cookie.match(/.YAAAAAAAAAAA/)||document.cookie.match(/.aAAAAAAAAAAA/)||document.cookie.match(/.YAAACFgAAAAA/)),EVAL_REDDIT_0:()=>document.cookie.includes("eu_cookie={%22opted%22:true%2C%22nonessential%22:false}"),EVAL_SIBBO_0:()=>!!window.localStorage.getItem("euconsent-v2"),EVAL_SIRDATA_0:()=>document.cookie.includes("euconsent-v2"),EVAL_SNIGEL_0:()=>!!document.cookie.match("snconsent"),EVAL_STEAMPOWERED_0:()=>2===JSON.parse(decodeURIComponent(document.cookie.split(";").find((e=>e.trim().startsWith("cookieSettings"))).split("=")[1])).preference_state,EVAL_TARTEAUCITRON_0:()=>tarteaucitron.userInterface.respondAll(!1)||!0,EVAL_TARTEAUCITRON_1:()=>tarteaucitron.userInterface.respondAll(!0)||!0,EVAL_TARTEAUCITRON_2:()=>document.cookie.match(/tarteaucitron=[^;]*/)[0].includes("false"),EVAL_TEALIUM_0:()=>void 0!==window.utag&&"object"==typeof utag.gdpr,EVAL_TEALIUM_1:()=>utag.gdpr.setConsentValue(!1)||!0,EVAL_TEALIUM_2:()=>utag.gdpr.setConsentValue(!0)||!0,EVAL_TEALIUM_3:()=>1!==utag.gdpr.getConsentState(),EVAL_TESTCMP_0:()=>"button_clicked"===window.results.results[0],EVAL_TESTCMP_COSMETIC_0:()=>"banner_hidden"===window.results.results[0],EVAL_THEFREEDICTIONARY_0:()=>cmpUi.showPurposes()||cmpUi.rejectAll()||!0,EVAL_THEFREEDICTIONARY_1:()=>cmpUi.allowAll()||!0,EVAL_THEVERGE_0:()=>document.cookie.includes("_duet_gdpr_acknowledged=1"),EVAL_UBUNTU_COM_0:()=>"_cookies_accepted=essential"===document.cookie,EVAL_UK_COOKIE_CONSENT_0:()=>!document.cookie.includes("catAccCookies"),EVAL_USERCENTRICS_API_0:()=>"object"==typeof UC_UI,EVAL_USERCENTRICS_API_1:()=>!!UC_UI.closeCMP(),EVAL_USERCENTRICS_API_2:()=>!!UC_UI.denyAllConsents(),EVAL_USERCENTRICS_API_3:()=>!!UC_UI.acceptAllConsents(),EVAL_USERCENTRICS_API_4:()=>!!UC_UI.closeCMP(),EVAL_USERCENTRICS_API_5:()=>!0===UC_UI.areAllConsentsAccepted(),EVAL_USERCENTRICS_API_6:()=>!1===UC_UI.areAllConsentsAccepted(),EVAL_USERCENTRICS_BUTTON_0:()=>JSON.parse(localStorage.getItem("usercentrics")).consents.every((e=>e.isEssential||!e.consentStatus)),EVAL_WAITROSE_0:()=>Array.from(document.querySelectorAll("label[id$=cookies-deny-label]")).forEach((e=>e.click()))||!0,EVAL_WAITROSE_1:()=>document.cookie.includes("wtr_cookies_advertising=0")&&document.cookie.includes("wtr_cookies_analytics=0"),EVAL_WP_COOKIE_NOTICE_0:()=>document.cookie.includes("wpl_viewed_cookie=no"),EVAL_XING_0:()=>document.cookie.includes("userConsent=%7B%22marketing%22%3Afalse"),EVAL_YOUTUBE_DESKTOP_0:()=>!!document.cookie.match(/SOCS=CAE/),EVAL_YOUTUBE_MOBILE_0:()=>!!document.cookie.match(/SOCS=CAE/)};var v={main:!0,frame:!1,urlPattern:""},f=class{constructor(e){this.runContext=v,this.autoconsent=e}get hasSelfTest(){throw new Error("Not Implemented")}get isIntermediate(){throw new Error("Not Implemented")}get isCosmetic(){throw new Error("Not Implemented")}mainWorldEval(e){const t=C[e];if(!t)return console.warn("Snippet not found",e),Promise.resolve(!1);if(this.autoconsent.config.isMainWorld){let e=!1;try{e=!!t.call(globalThis)}catch(e){}return Promise.resolve(e)}return function(e){const t=g();w.sendContentMessage({type:"eval",id:t,code:e});const o=new y(t);return w.pending.set(o.id,o),o.promise}(function(e){const t=e.toString();return t.substring(t.indexOf("=>")+2)}(t)).catch((e=>!1))}checkRunContext(){const e={...v,...this.runContext},t=window.top===window;return!(t&&!e.main)&&(!(!t&&!e.frame)&&!(e.urlPattern&&!window.location.href.match(e.urlPattern)))}detectCmp(){throw new Error("Not Implemented")}async detectPopup(){return!1}optOut(){throw new Error("Not Implemented")}optIn(){throw new Error("Not Implemented")}openCmp(){throw new Error("Not Implemented")}async test(){return Promise.resolve(!0)}},A=class extends f{constructor(e,t){super(t),this.config=e,this.name=e.name,this.runContext=e.runContext||v}get hasSelfTest(){return!!this.config.test}get isIntermediate(){return!!this.config.intermediate}get isCosmetic(){return!!this.config.cosmetic}get prehideSelectors(){return this.config.prehideSelectors}async detectCmp(){return!!this.config.detectCmp&&this._runRulesParallel(this.config.detectCmp)}async detectPopup(){return!!this.config.detectPopup&&this._runRulesSequentially(this.config.detectPopup)}async optOut(){return!!this.config.optOut&&this._runRulesSequentially(this.config.optOut)}async optIn(){return!!this.config.optIn&&this._runRulesSequentially(this.config.optIn)}async openCmp(){return!!this.config.openCmp&&this._runRulesSequentially(this.config.openCmp)}async test(){return this.hasSelfTest?this._runRulesSequentially(this.config.test):super.test()}async evaluateRuleStep(e){const t=[];if(e.exists&&t.push(d(e.exists)),e.visible&&t.push(u(e.visible,e.check)),e.eval){const o=this.mainWorldEval(e.eval);t.push(o)}var o,c;if(e.waitFor&&t.push(m(e.waitFor,e.timeout)),e.waitForVisible&&t.push(function(e,t=1e4,o="any"){return r((()=>u(e,o)),Math.ceil(t/200),200)}(e.waitForVisible,e.timeout,e.check)),e.click&&t.push(l(e.click,e.all)),e.waitForThenClick&&t.push(h(e.waitForThenClick,e.timeout,e.all)),e.wait&&t.push(k(e.wait)),e.hide&&t.push((o=e.hide,c=e.method,s(a(),o,c))),e.if){if(!e.if.exists&&!e.if.visible)return console.error("invalid conditional rule",e.if),!1;await this.evaluateRuleStep(e.if)?t.push(this._runRulesSequentially(e.then)):e.else&&t.push(this._runRulesSequentially(e.else))}if(e.any){for(const t of e.any)if(await this.evaluateRuleStep(t))return!0;return!1}if(0===t.length)return!1;return(await Promise.all(t)).reduce(((e,t)=>e&&t),!0)}async _runRulesParallel(e){const t=e.map((e=>this.evaluateRuleStep(e)));return(await Promise.all(t)).every((e=>!!e))}async _runRulesSequentially(e){for(const t of e){if(!await this.evaluateRuleStep(t)&&!t.optional)return!1}return!0}},E=class{constructor(e,t){this.name=e,this.config=t,this.methods=new Map,this.runContext=v,this.isCosmetic=!1,t.methods.forEach((e=>{e.action&&this.methods.set(e.name,e.action)})),this.hasSelfTest=!1}get isIntermediate(){return!1}checkRunContext(){return!0}async detectCmp(){return this.config.detectors.map((e=>o(e.presentMatcher))).some((e=>!!e))}async detectPopup(){return this.config.detectors.map((e=>o(e.showingMatcher))).some((e=>!!e))}async executeAction(e,t){return!this.methods.has(e)||c(this.methods.get(e),t)}async optOut(){return await this.executeAction("HIDE_CMP"),await this.executeAction("OPEN_OPTIONS"),await this.executeAction("HIDE_CMP"),await this.executeAction("DO_CONSENT",[]),await this.executeAction("SAVE_CONSENT"),!0}async optIn(){return await this.executeAction("HIDE_CMP"),await this.executeAction("OPEN_OPTIONS"),await this.executeAction("HIDE_CMP"),await this.executeAction("DO_CONSENT",["D","A","B","E","F","X"]),await this.executeAction("SAVE_CONSENT"),!0}async openCmp(){return await this.executeAction("HIDE_CMP"),await this.executeAction("OPEN_OPTIONS"),!0}async test(){return!0}},O="#truste-show-consent",x="#truste-consent-track",S=[class extends f{constructor(e){super(e),this.name="TrustArc-top",this.prehideSelectors=[".trustarc-banner-container",`.truste_popframe,.truste_overlay,.truste_box_overlay,${x}`],this.runContext={main:!0,frame:!1},this._shortcutButton=null,this._optInDone=!1}get hasSelfTest(){return!1}get isIntermediate(){return!this._optInDone&&!this._shortcutButton}get isCosmetic(){return!1}async detectCmp(){const e=d(`${O},${x}`);return e&&(this._shortcutButton=document.querySelector("#truste-consent-required")),e}async detectPopup(){return u(`#truste-consent-content,#trustarc-banner-overlay,${x}`,"all")}openFrame(){l(O)}async optOut(){return this._shortcutButton?(this._shortcutButton.click(),!0):(s(a(),[".truste_popframe",".truste_overlay",".truste_box_overlay",x]),l(O),setTimeout((()=>{a().remove()}),1e4),!0)}async optIn(){return this._optInDone=!0,l("#truste-consent-button")}async openCmp(){return!0}async test(){return await this.mainWorldEval("EVAL_TRUSTARC_TOP")}},class extends f{constructor(){super(...arguments),this.name="TrustArc-frame",this.runContext={main:!1,frame:!0,urlPattern:"^https://consent-pref\\.trustarc\\.com/\\?"}}get hasSelfTest(){return!1}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return!0}async detectPopup(){return u("#defaultpreferencemanager","any")&&u(".mainContent","any")}async navigateToSettings(){return await r((async()=>d(".shp")||u(".advance","any")||d(".switch span:first-child")),10,500),d(".shp")&&l(".shp"),await m(".prefPanel",5e3),u(".advance","any")&&l(".advance"),await r((()=>u(".switch span:first-child","any")),5,1e3)}async optOut(){return await r((()=>"complete"===document.readyState),20,100),await m(".mainContent[aria-hidden=false]",5e3),!!l(".rejectAll")||(d(".prefPanel")&&await m('.prefPanel[style="visibility: visible;"]',3e3),l("#catDetails0")?(l(".submit"),!0):(l(".required")||(await this.navigateToSettings(),l(".switch span:nth-child(1):not(.active)",!0),l(".submit"),m("#gwt-debug-close_id",3e5).then((()=>{l("#gwt-debug-close_id")}))),!0))}async optIn(){return l(".call")||(await this.navigateToSettings(),l(".switch span:nth-child(2)",!0),l(".submit"),m("#gwt-debug-close_id",3e5).then((()=>{l("#gwt-debug-close_id")}))),!0}},class extends f{constructor(){super(...arguments),this.name="Cybotcookiebot",this.prehideSelectors=["#CybotCookiebotDialog,#dtcookie-container,#cookiebanner,#cb-cookieoverlay"]}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return d("#CybotCookiebotDialogBodyLevelButtonPreferences")}async detectPopup(){return d("#CybotCookiebotDialog,#dtcookie-container,#cookiebanner,#cb-cookiebanner")}async optOut(){return l(".cookie-alert-extended-detail-link")?(await m(".cookie-alert-configuration",2e3),l(".cookie-alert-configuration-input:checked",!0),l(".cookie-alert-extended-button-secondary"),!0):d("#dtcookie-container")?l(".h-dtcookie-decline"):(l(".cookiebot__button--settings")||l("#CybotCookiebotDialogBodyButtonDecline")||(l(".cookiebanner__link--details"),l('.CybotCookiebotDialogBodyLevelButton:checked:enabled,input[id*="CybotCookiebotDialogBodyLevelButton"]:checked:enabled',!0),l("#CybotCookiebotDialogBodyButtonDecline"),l("input[id^=CybotCookiebotDialogBodyLevelButton]:checked",!0),d("#CybotCookiebotDialogBodyButtonAcceptSelected")?l("#CybotCookiebotDialogBodyButtonAcceptSelected"):l("#CybotCookiebotDialogBodyLevelButtonAccept,#CybotCookiebotDialogBodyButtonAccept,#CybotCookiebotDialogBodyLevelButtonLevelOptinAllowallSelection",!0),await this.mainWorldEval("EVAL_COOKIEBOT_1")&&(await this.mainWorldEval("EVAL_COOKIEBOT_2"),await k(500)),d("#cb-confirmedSettings")&&await this.mainWorldEval("EVAL_COOKIEBOT_3")),!0)}async optIn(){return d("#dtcookie-container")?l(".h-dtcookie-accept"):(l(".CybotCookiebotDialogBodyLevelButton:not(:checked):enabled",!0),l("#CybotCookiebotDialogBodyLevelButtonAccept"),l("#CybotCookiebotDialogBodyButtonAccept"),!0)}async test(){return this.mainWorldEval("EVAL_COOKIEBOT_4")}},class extends f{constructor(){super(...arguments),this.name="Sourcepoint-frame",this.prehideSelectors=["div[id^='sp_message_container_'],.message-overlay","#sp_privacy_manager_container"],this.ccpaNotice=!1,this.ccpaPopup=!1,this.runContext={main:!1,frame:!0}}get hasSelfTest(){return!1}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){const e=new URL(location.href);return e.searchParams.has("message_id")&&"ccpa-notice.sp-prod.net"===e.hostname?(this.ccpaNotice=!0,!0):"ccpa-pm.sp-prod.net"===e.hostname?(this.ccpaPopup=!0,!0):("/index.html"===e.pathname||"/privacy-manager/index.html"===e.pathname)&&(e.searchParams.has("message_id")||e.searchParams.has("requestUUID")||e.searchParams.has("consentUUID"))}async detectPopup(){return!!this.ccpaNotice||(this.ccpaPopup?await m(".priv-save-btn",2e3):(await m(".sp_choice_type_11,.sp_choice_type_12,.sp_choice_type_13,.sp_choice_type_ACCEPT_ALL",2e3),!d(".sp_choice_type_9")))}async optIn(){return await m(".sp_choice_type_11,.sp_choice_type_ACCEPT_ALL",2e3),!!l(".sp_choice_type_11")||!!l(".sp_choice_type_ACCEPT_ALL")}isManagerOpen(){return"/privacy-manager/index.html"===location.pathname}async optOut(){if(this.ccpaPopup){const e=document.querySelectorAll(".priv-purpose-container .sp-switch-arrow-block a.neutral.on .right");for(const t of e)t.click();const t=document.querySelectorAll(".priv-purpose-container .sp-switch-arrow-block a.switch-bg.on");for(const e of t)e.click();return l(".priv-save-btn")}if(!this.isManagerOpen()){if(!await m(".sp_choice_type_12,.sp_choice_type_13"))return!1;if(!d(".sp_choice_type_12"))return l(".sp_choice_type_13");l(".sp_choice_type_12"),await r((()=>this.isManagerOpen()),200,100)}await m(".type-modal",2e4);try{const e=".sp_choice_type_REJECT_ALL",t=".reject-toggle",o=await Promise.race([m(e,2e3).then((e=>e?0:-1)),m(t,2e3).then((e=>e?1:-1)),m(".pm-features",2e3).then((e=>e?2:-1))]);if(0===o)return await k(1e3),l(e);1===o?l(t):2===o&&(await m(".pm-features",1e4),l(".checked > span",!0),l(".chevron"))}catch(e){}return l(".sp_choice_type_SAVE_AND_EXIT")}},class extends f{constructor(){super(...arguments),this.name="consentmanager.net",this.prehideSelectors=["#cmpbox,#cmpbox2"],this.apiAvailable=!1}get hasSelfTest(){return this.apiAvailable}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.apiAvailable=await this.mainWorldEval("EVAL_CONSENTMANAGER_1"),!!this.apiAvailable||d("#cmpbox")}async detectPopup(){return this.apiAvailable?(await k(500),await this.mainWorldEval("EVAL_CONSENTMANAGER_2")):u("#cmpbox .cmpmore","any")}async optOut(){return await k(500),this.apiAvailable?await this.mainWorldEval("EVAL_CONSENTMANAGER_3"):!!l(".cmpboxbtnno")||(d(".cmpwelcomeprpsbtn")?(l(".cmpwelcomeprpsbtn > a[aria-checked=true]",!0),l(".cmpboxbtnsave"),!0):(l(".cmpboxbtncustom"),await m(".cmptblbox",2e3),l(".cmptdchoice > a[aria-checked=true]",!0),l(".cmpboxbtnyescustomchoices"),!0))}async optIn(){return this.apiAvailable?await this.mainWorldEval("EVAL_CONSENTMANAGER_4"):l(".cmpboxbtnyes")}async test(){if(this.apiAvailable)return await this.mainWorldEval("EVAL_CONSENTMANAGER_5")}},class extends f{constructor(){super(...arguments),this.name="Evidon"}get hasSelfTest(){return!1}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return d("#_evidon_banner")}async detectPopup(){return u("#_evidon_banner","any")}async optOut(){return l("#_evidon-decline-button")||(s(a(),["#evidon-prefdiag-overlay","#evidon-prefdiag-background"]),l("#_evidon-option-button"),await m("#evidon-prefdiag-overlay",5e3),l("#evidon-prefdiag-decline")),!0}async optIn(){return l("#_evidon-accept-button")}},class extends f{constructor(){super(...arguments),this.name="Onetrust",this.prehideSelectors=["#onetrust-banner-sdk,#onetrust-consent-sdk,.onetrust-pc-dark-filter,.js-consent-banner"],this.runContext={urlPattern:"^(?!.*https://www\\.nba\\.com/)"}}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return d("#onetrust-banner-sdk")}async detectPopup(){return u("#onetrust-banner-sdk","all")}async optOut(){return d("#onetrust-pc-btn-handler")?l("#onetrust-pc-btn-handler"):l(".ot-sdk-show-settings,button.js-cookie-settings"),await m("#onetrust-consent-sdk",2e3),await k(1e3),l("#onetrust-consent-sdk input.category-switch-handler:checked,.js-editor-toggle-state:checked",!0),await k(1e3),await m(".save-preference-btn-handler,.js-consent-save",2e3),l(".save-preference-btn-handler,.js-consent-save"),await r((()=>u("#onetrust-banner-sdk","none")),10,500),!0}async optIn(){return l("#onetrust-accept-btn-handler,.js-accept-cookies")}async test(){return await this.mainWorldEval("EVAL_ONETRUST_1")}},class extends f{constructor(){super(...arguments),this.name="Klaro",this.prehideSelectors=[".klaro"],this.settingsOpen=!1}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return d(".klaro > .cookie-modal")?(this.settingsOpen=!0,!0):d(".klaro > .cookie-notice")}async detectPopup(){return u(".klaro > .cookie-notice,.klaro > .cookie-modal","any")}async optOut(){return!!l(".klaro .cn-decline")||(this.settingsOpen||(l(".klaro .cn-learn-more"),await m(".klaro > .cookie-modal",2e3),this.settingsOpen=!0),!!l(".klaro .cn-decline")||(l(".cm-purpose:not(.cm-toggle-all) > input:not(.half-checked)",!0),l(".cm-btn-accept")))}async optIn(){return!!l(".klaro .cm-btn-accept-all")||(this.settingsOpen?(l(".cm-purpose:not(.cm-toggle-all) > input.half-checked",!0),l(".cm-btn-accept")):l(".klaro .cookie-notice .cm-btn-success"))}async test(){return await this.mainWorldEval("EVAL_KLARO_1")}},class extends f{constructor(){super(...arguments),this.name="Uniconsent"}get prehideSelectors(){return[".unic",".modal:has(.unic)"]}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return d(".unic .unic-box,.unic .unic-bar")}async detectPopup(){return u(".unic .unic-box,.unic .unic-bar","any")}async optOut(){if(await m(".unic button",1e3),document.querySelectorAll(".unic button").forEach((e=>{const t=e.textContent;(t.includes("Manage Options")||t.includes("Optionen verwalten"))&&e.click()})),await m(".unic input[type=checkbox]",1e3)){await m(".unic button",1e3),document.querySelectorAll(".unic input[type=checkbox]").forEach((e=>{e.checked&&e.click()}));for(const e of document.querySelectorAll(".unic button")){const t=e.textContent;for(const o of["Confirm Choices","Save Choices","Auswahl speichern"])if(t.includes(o))return e.click(),await k(500),!0}}return!1}async optIn(){return h(".unic #unic-agree")}async test(){await k(1e3);return!d(".unic .unic-box,.unic .unic-bar")}},class extends f{constructor(){super(...arguments),this.prehideSelectors=[".cmp-root"],this.name="Conversant"}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return d(".cmp-root .cmp-receptacle")}async detectPopup(){return u(".cmp-root .cmp-receptacle","any")}async optOut(){if(!await h(".cmp-main-button:not(.cmp-main-button--primary)"))return!1;if(!await m(".cmp-view-tab-tabs"))return!1;await h(".cmp-view-tab-tabs > :first-child"),await h(".cmp-view-tab-tabs > .cmp-view-tab--active:first-child");for(const e of Array.from(document.querySelectorAll(".cmp-accordion-item"))){e.querySelector(".cmp-accordion-item-title").click(),await r((()=>!!e.querySelector(".cmp-accordion-item-content.cmp-active")),10,50);const t=e.querySelector(".cmp-accordion-item-content.cmp-active");t.querySelectorAll(".cmp-toggle-actions .cmp-toggle-deny:not(.cmp-toggle-deny--active)").forEach((e=>e.click())),t.querySelectorAll(".cmp-toggle-actions .cmp-toggle-checkbox:not(.cmp-toggle-checkbox--active)").forEach((e=>e.click()))}return await l(".cmp-main-button:not(.cmp-main-button--primary)"),!0}async optIn(){return h(".cmp-main-button.cmp-main-button--primary")}async test(){return document.cookie.includes("cmp-data=0")}},class extends f{constructor(){super(...arguments),this.name="tiktok.com",this.runContext={urlPattern:"tiktok"}}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}getShadowRoot(){const e=document.querySelector("tiktok-cookie-banner");return e?e.shadowRoot:null}async detectCmp(){return d("tiktok-cookie-banner")}async detectPopup(){return p(this.getShadowRoot().querySelector(".tiktok-cookie-banner"))}async optOut(){const e=this.getShadowRoot().querySelector(".button-wrapper button:first-child");return!!e&&(e.click(),!0)}async optIn(){const e=this.getShadowRoot().querySelector(".button-wrapper button:last-child");return!!e&&(e.click(),!0)}async test(){const e=document.cookie.match(/cookie-consent=([^;]+)/);if(!e)return!1;const t=JSON.parse(decodeURIComponent(e[1]));return Object.values(t).every((e=>"boolean"!=typeof e||!1===e))}},class extends f{constructor(){super(...arguments),this.runContext={urlPattern:"^https://(www\\.)?airbnb\\.[^/]+/"},this.prehideSelectors=["div[data-testid=main-cookies-banner-container]",'div:has(> div:first-child):has(> div:last-child):has(> section [data-testid="strictly-necessary-cookies"])']}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return d("div[data-testid=main-cookies-banner-container]")}async detectPopup(){return u("div[data-testid=main-cookies-banner-container","any")}async optOut(){let e;for(await h("div[data-testid=main-cookies-banner-container] button._snbhip0");e=document.querySelector("[data-testid=modal-container] button[aria-checked=true]:not([disabled])");)e.click();return h("button[data-testid=save-btn]")}async optIn(){return h("div[data-testid=main-cookies-banner-container] button._148dgdpk")}async test(){return await r((()=>!!document.cookie.match("OptanonAlertBoxClosed")),20,200)}}];var I=[{name:"192.com",detectCmp:[{exists:".ont-cookies"}],detectPopup:[{visible:".ont-cookies"}],optIn:[{click:".ont-btn-main.ont-cookies-btn.js-ont-btn-ok2"}],optOut:[{click:".ont-cookes-btn-manage"},{click:".ont-btn-main.ont-cookies-btn.js-ont-btn-choose"}],test:[{eval:"EVAL_ONENINETWO_0"}]},{name:"1password-com",cosmetic:!0,prehideSelectors:['footer #footer-root [aria-label="Cookie Consent"]'],detectCmp:[{exists:'footer #footer-root [aria-label="Cookie Consent"]'}],detectPopup:[{visible:'footer #footer-root [aria-label="Cookie Consent"]'}],optIn:[{click:'footer #footer-root [aria-label="Cookie Consent"] button'}],optOut:[{hide:['footer #footer-root [aria-label="Cookie Consent"]']}]},{name:"Adroll",prehideSelectors:["#adroll_consent_container"],detectCmp:[{exists:"#adroll_consent_container"}],detectPopup:[{visible:"#adroll_consent_container"}],optIn:[{waitForThenClick:"#adroll_consent_accept"}],optOut:[{waitForThenClick:"#adroll_consent_reject"}],test:[{eval:"EVAL_ADROLL_0"}]},{name:"affinity.serif.com",detectCmp:[{exists:".c-cookie-banner button[data-qa='allow-all-cookies']"}],detectPopup:[{visible:".c-cookie-banner"}],optIn:[{click:'button[data-qa="allow-all-cookies"]'}],optOut:[{click:'button[data-qa="manage-cookies"]'},{waitFor:'.c-cookie-banner ~ [role="dialog"]'},{waitForThenClick:'.c-cookie-banner ~ [role="dialog"] input[type="checkbox"][value="true"]',all:!0},{click:'.c-cookie-banner ~ [role="dialog"] .c-modal__action button'}],test:[{wait:500},{eval:"EVAL_AFFINITY_SERIF_COM_0"}]},{name:"agolde.com",cosmetic:!0,prehideSelectors:["#modal-1 div[data-micromodal-close]"],detectCmp:[{exists:"#modal-1 div[aria-labelledby=modal-1-title]"}],detectPopup:[{exists:"#modal-1 div[data-micromodal-close]"}],optIn:[{click:'button[aria-label="Close modal"]'}],optOut:[{hide:["#modal-1 div[data-micromodal-close]"]}]},{name:"altium.com",cosmetic:!0,prehideSelectors:[".altium-privacy-bar"],detectCmp:[{exists:".altium-privacy-bar"}],detectPopup:[{exists:".altium-privacy-bar"}],optIn:[{click:"a.altium-privacy-bar__btn"}],optOut:[{hide:[".altium-privacy-bar"]}]},{name:"amazon.com",prehideSelectors:['span[data-action="sp-cc"][data-sp-cc*="rejectAllAction"]'],detectCmp:[{exists:'span[data-action="sp-cc"][data-sp-cc*="rejectAllAction"]'}],detectPopup:[{visible:'span[data-action="sp-cc"][data-sp-cc*="rejectAllAction"]'}],optIn:[{waitForVisible:"#sp-cc-accept"},{wait:500},{click:"#sp-cc-accept"}],optOut:[{waitForVisible:"#sp-cc-rejectall-link"},{wait:500},{click:"#sp-cc-rejectall-link"}]},{name:"aquasana.com",cosmetic:!0,prehideSelectors:["#consent-tracking"],detectCmp:[{exists:"#consent-tracking"}],detectPopup:[{exists:"#consent-tracking"}],optIn:[{click:"#accept_consent"}],optOut:[{hide:["#consent-tracking"]}]},{name:"arzt-auskunft.de",prehideSelectors:["#cookiescript_injected"],detectCmp:[{exists:"#cookiescript_injected"}],detectPopup:[{visible:"#cookiescript_injected"}],optOut:[{click:"#cookiescript_reject"}],optIn:[{click:"#cookiescript_accept"}]},{name:"athlinks-com",runContext:{urlPattern:"^https://(www\\.)?athlinks\\.com/"},cosmetic:!0,prehideSelectors:["#footer-container ~ div"],detectCmp:[{exists:"#footer-container ~ div"}],detectPopup:[{visible:"#footer-container > div"}],optIn:[{click:"#footer-container ~ div button"}],optOut:[{hide:["#footer-container ~ div"]}]},{name:"ausopen.com",cosmetic:!0,detectCmp:[{exists:".gdpr-popup__message"}],detectPopup:[{visible:".gdpr-popup__message"}],optOut:[{hide:[".gdpr-popup__message"]}],optIn:[{click:".gdpr-popup__message button"}]},{name:"automattic-cmp-optout",prehideSelectors:['form[class*="cookie-banner"][method="post"]'],detectCmp:[{exists:'form[class*="cookie-banner"][method="post"]'}],detectPopup:[{visible:'form[class*="cookie-banner"][method="post"]'}],optIn:[{click:'a[class*="accept-all-button"]'}],optOut:[{click:'form[class*="cookie-banner"] div[class*="simple-options"] a[class*="customize-button"]'},{waitForThenClick:"input[type=checkbox][checked]:not([disabled])",all:!0},{click:'a[class*="accept-selection-button"]'}]},{name:"aws.amazon.com",prehideSelectors:["#awsccc-cb-content","#awsccc-cs-container","#awsccc-cs-modalOverlay","#awsccc-cs-container-inner"],detectCmp:[{exists:"#awsccc-cb-content"}],detectPopup:[{visible:"#awsccc-cb-content"}],optIn:[{click:"button[data-id=awsccc-cb-btn-accept"}],optOut:[{click:"button[data-id=awsccc-cb-btn-customize]"},{waitFor:"input[aria-checked]"},{click:"input[aria-checked=true]",all:!0,optional:!0},{click:"button[data-id=awsccc-cs-btn-save]"}]},{name:"axeptio",prehideSelectors:[".axeptio_widget"],detectCmp:[{exists:".axeptio_widget"}],detectPopup:[{visible:".axeptio_widget"}],optIn:[{waitFor:".axeptio-widget--open"},{click:"button#axeptio_btn_acceptAll"}],optOut:[{waitFor:".axeptio-widget--open"},{click:"button#axeptio_btn_dismiss"}],test:[{eval:"EVAL_AXEPTIO_0"}]},{name:"baden-wuerttemberg.de",prehideSelectors:[".cookie-alert.t-dark"],cosmetic:!0,detectCmp:[{exists:".cookie-alert.t-dark"}],detectPopup:[{visible:".cookie-alert.t-dark"}],optIn:[{click:".cookie-alert__form input:not([disabled]):not([checked])"},{click:".cookie-alert__button button"}],optOut:[{hide:[".cookie-alert.t-dark"]}]},{name:"bbb.org",runContext:{urlPattern:"^https://www\\.bbb\\.org/"},cosmetic:!0,prehideSelectors:['div[aria-label="use of cookies on bbb.org"]'],detectCmp:[{exists:'div[aria-label="use of cookies on bbb.org"]'}],detectPopup:[{visible:'div[aria-label="use of cookies on bbb.org"]'}],optIn:[{click:'div[aria-label="use of cookies on bbb.org"] button.bds-button-unstyled span.visually-hidden'}],optOut:[{hide:['div[aria-label="use of cookies on bbb.org"]']}]},{name:"bing.com",prehideSelectors:["#bnp_container"],detectCmp:[{exists:"#bnp_cookie_banner"}],detectPopup:[{visible:"#bnp_cookie_banner"}],optIn:[{click:"#bnp_btn_accept"}],optOut:[{click:"#bnp_btn_preference"},{click:"#mcp_savesettings"}],test:[{eval:"EVAL_BING_0"}]},{name:"borlabs",detectCmp:[{exists:"._brlbs-block-content"}],detectPopup:[{visible:"._brlbs-bar-wrap,._brlbs-box-wrap"}],optIn:[{click:"a[data-cookie-accept-all]"}],optOut:[{click:"a[data-cookie-individual]"},{waitForVisible:".cookie-preference"},{click:"input[data-borlabs-cookie-checkbox]:checked",all:!0,optional:!0},{click:"#CookiePrefSave"},{wait:500}],prehideSelectors:["#BorlabsCookieBox"],test:[{eval:"EVAL_BORLABS_0"}]},{name:"bundesregierung.de",prehideSelectors:[".bpa-cookie-banner"],detectCmp:[{exists:".bpa-cookie-banner"}],detectPopup:[{visible:".bpa-cookie-banner .bpa-module-full-hero"}],optIn:[{click:".bpa-accept-all-button"}],optOut:[{wait:500,comment:"click is not immediately recognized"},{waitForThenClick:".bpa-close-button"}],test:[{eval:"EVAL_BUNDESREGIERUNG_DE_0"}]},{name:"burpee.com",cosmetic:!0,prehideSelectors:["#notice-cookie-block"],detectCmp:[{exists:"#notice-cookie-block"}],detectPopup:[{exists:"#html-body #notice-cookie-block"}],optIn:[{click:"#btn-cookie-allow"}],optOut:[{hide:["#html-body #notice-cookie-block","#notice-cookie"]}]},{name:"canva.com",prehideSelectors:['div[role="dialog"] a[data-anchor-id="cookie-policy"]'],detectCmp:[{exists:'div[role="dialog"] a[data-anchor-id="cookie-policy"]'}],detectPopup:[{exists:'div[role="dialog"] a[data-anchor-id="cookie-policy"]'}],optIn:[{click:'div[role="dialog"] button:nth-child(1)'}],optOut:[{if:{exists:'div[role="dialog"] button:nth-child(3)'},then:[{click:'div[role="dialog"] button:nth-child(2)'}],else:[{click:'div[role="dialog"] button:nth-child(2)'},{waitFor:'div[role="dialog"] a[data-anchor-id="privacy-policy"]'},{click:'div[role="dialog"] button:nth-child(2)'},{click:'div[role="dialog"] div:last-child button:only-child'}]}],test:[{eval:"EVAL_CANVA_0"}]},{name:"cc_banner",cosmetic:!0,prehideSelectors:[".cc_banner-wrapper"],detectCmp:[{exists:".cc_banner-wrapper"}],detectPopup:[{visible:".cc_banner"}],optIn:[{click:".cc_btn_accept_all"}],optOut:[{hide:[".cc_banner-wrapper"]}]},{comment:"https://www.civicuk.com/cookie-control/",name:"civic-cookie-control",prehideSelectors:["#ccc-module,#ccc-overlay"],detectCmp:[{exists:"#ccc-module"}],detectPopup:[{visible:"#ccc"},{visible:"#ccc-module"}],optOut:[{click:"#ccc-reject-settings"}],optIn:[{click:"#ccc-recommended-settings"}]},{name:"click.io",prehideSelectors:["#cl-consent"],detectCmp:[{exists:"#cl-consent"}],detectPopup:[{visible:"#cl-consent"}],optIn:[{waitForThenClick:'#cl-consent [data-role="b_agree"]'}],optOut:[{waitFor:'#cl-consent [data-role="b_options"]'},{wait:500},{click:'#cl-consent [data-role="b_options"]'},{waitFor:'.cl-consent-popup.cl-consent-visible [data-role="alloff"]'},{click:'.cl-consent-popup.cl-consent-visible [data-role="alloff"]',all:!0},{click:'[data-role="b_save"]'}],test:[{eval:"EVAL_CLICKIO_0",comment:"TODO: this only checks if we interacted at all"}]},{name:"clinch",intermediate:!1,runContext:{frame:!1,main:!0},prehideSelectors:[".consent-modal[role=dialog]"],detectCmp:[{exists:".consent-modal[role=dialog]"}],detectPopup:[{visible:".consent-modal[role=dialog]"}],optIn:[{click:"#consent_agree"}],optOut:[{click:"#manage_cookie_preferences"},{click:"#cookie_consent_preferences input:checked",all:!0,optional:!0},{click:"#consent_save"}],test:[{eval:"EVAL_CLINCH_0"}]},{name:"clustrmaps.com",runContext:{urlPattern:"^https://(www\\.)?clustrmaps\\.com/"},cosmetic:!0,prehideSelectors:["#gdpr-cookie-message"],detectCmp:[{exists:"#gdpr-cookie-message"}],detectPopup:[{visible:"#gdpr-cookie-message"}],optIn:[{click:"button#gdpr-cookie-accept"}],optOut:[{hide:["#gdpr-cookie-message"]}]},{name:"coinbase",intermediate:!1,runContext:{frame:!0,main:!0,urlPattern:"^https://(www|help)\\.coinbase\\.com"},prehideSelectors:[],detectCmp:[{exists:"div[class^=CookieBannerContent__Container]"}],detectPopup:[{visible:"div[class^=CookieBannerContent__Container]"}],optIn:[{click:"div[class^=CookieBannerContent__CTA] :nth-last-child(1)"}],optOut:[{click:"button[class^=CookieBannerContent__Settings]"},{click:"div[class^=CookiePreferencesModal__CategoryContainer] input:checked",all:!0,optional:!0},{click:"div[class^=CookiePreferencesModal__ButtonContainer] > button"}],test:[{eval:"EVAL_COINBASE_0"}]},{name:"Complianz banner",prehideSelectors:["#cmplz-cookiebanner-container"],detectCmp:[{exists:"#cmplz-cookiebanner-container .cmplz-cookiebanner"}],detectPopup:[{visible:"#cmplz-cookiebanner-container .cmplz-cookiebanner",check:"any"}],optIn:[{waitForThenClick:".cmplz-cookiebanner .cmplz-accept"}],optOut:[{waitForThenClick:".cmplz-cookiebanner .cmplz-deny"}],test:[{eval:"EVAL_COMPLIANZ_BANNER_0"}]},{name:"Complianz categories",prehideSelectors:['.cc-type-categories[aria-describedby="cookieconsent:desc"]'],detectCmp:[{exists:'.cc-type-categories[aria-describedby="cookieconsent:desc"]'}],detectPopup:[{visible:'.cc-type-categories[aria-describedby="cookieconsent:desc"]'}],optIn:[{click:".cc-accept-all",optional:!0},{click:".cc-allow",optional:!0},{click:".cc-dismiss",optional:!0}],optOut:[{click:".cc-dismiss"}],test:[{eval:"EVAL_COMPLIANZ_CATEGORIES_0"}]},{name:"Complianz notice",prehideSelectors:['.cc-type-info[aria-describedby="cookieconsent:desc"]'],cosmetic:!0,detectCmp:[{exists:'.cc-type-info[aria-describedby="cookieconsent:desc"]'}],detectPopup:[{visible:'.cc-type-info[aria-describedby="cookieconsent:desc"]'}],optIn:[{click:".cc-accept-all",optional:!0},{click:".cc-allow",optional:!0},{click:".cc-dismiss",optional:!0}],optOut:[{hide:['[aria-describedby="cookieconsent:desc"]']}]},{name:"Complianz optin",prehideSelectors:['.cc-type-opt-in[aria-describedby="cookieconsent:desc"]'],detectCmp:[{exists:'.cc-type-opt-in[aria-describedby="cookieconsent:desc"]'}],detectPopup:[{visible:'.cc-type-opt-in[aria-describedby="cookieconsent:desc"]'}],optIn:[{click:".cc-accept-all",optional:!0},{click:".cc-allow",optional:!0},{click:".cc-dismiss",optional:!0}],optOut:[{click:".cc-settings"},{waitForVisible:'[aria-label="cookies preferences popup"]'},{click:'[aria-label="cookies preferences popup"] input[type=checkbox]:not([disabled]):checked',all:!0,optional:!0},{click:'[aria-label="cookies preferences popup"] [aria-label="Accept Selected"], [aria-label="cookies preferences popup"] [aria-label="Save my choice"], .cc-btn-accept-selected, .cc-deny',optional:!0}],test:[{eval:"EVAL_COMPLIANZ_OPTIN_0"}]},{name:"cookie-law-info",prehideSelectors:["#cookie-law-info-bar"],detectCmp:[{exists:"#cookie-law-info-bar"}],detectPopup:[{visible:"#cookie-law-info-bar"}],optIn:[{click:'[data-cli_action="accept_all"]'}],optOut:[{hide:["#cookie-law-info-bar"]},{eval:"EVAL_COOKIE_LAW_INFO_0"}],test:[{eval:"EVAL_COOKIE_LAW_INFO_1"}]},{name:"cookie-manager-popup",cosmetic:!1,runContext:{main:!0,frame:!1},intermediate:!1,detectCmp:[{exists:"#notice-cookie-block #allow-functional-cookies, #notice-cookie-block #btn-cookie-settings"}],detectPopup:[{visible:"#notice-cookie-block"}],optIn:[{click:"#btn-cookie-allow"}],optOut:[{if:{exists:"#allow-functional-cookies"},then:[{click:"#allow-functional-cookies"}],else:[{waitForThenClick:"#btn-cookie-settings"},{waitForVisible:".modal-body"},{click:'.modal-body input:checked, .switch[data-switch="on"]',all:!0,optional:!0},{click:'[role="dialog"] .modal-footer button'}]}],prehideSelectors:["#btn-cookie-settings"],test:[{eval:"EVAL_COOKIE_MANAGER_POPUP_0"}]},{name:"cookie-notice",prehideSelectors:["#cookie-notice"],cosmetic:!0,detectCmp:[{visible:"#cookie-notice .cookie-notice-container"}],detectPopup:[{visible:"#cookie-notice"}],optIn:[{click:"#cn-accept-cookie"}],optOut:[{hide:["#cookie-notice"]}]},{name:"cookiealert",intermediate:!1,prehideSelectors:[],runContext:{frame:!0,main:!0},detectCmp:[{exists:".cookie-alert-extended"}],detectPopup:[{visible:".cookie-alert-extended-modal"}],optIn:[{click:"button[data-controller='cookie-alert/extended/button/accept']"},{eval:"EVAL_COOKIEALERT_0"}],optOut:[{click:"a[data-controller='cookie-alert/extended/detail-link']"},{click:".cookie-alert-configuration-input:checked",all:!0,optional:!0},{click:"button[data-controller='cookie-alert/extended/button/configuration']"},{eval:"EVAL_COOKIEALERT_0"}],test:[{eval:"EVAL_COOKIEALERT_2"}]},{name:"cookiefirst.com",prehideSelectors:["#cookiefirst-root,.cookiefirst-root,[aria-labelledby=cookie-preference-panel-title]"],detectCmp:[{exists:"#cookiefirst-root,.cookiefirst-root"}],detectPopup:[{visible:"#cookiefirst-root,.cookiefirst-root"}],optIn:[{click:"button[data-cookiefirst-action=accept]"}],optOut:[{if:{exists:"button[data-cookiefirst-action=adjust]"},then:[{click:"button[data-cookiefirst-action=adjust]"},{waitForVisible:"[data-cookiefirst-widget=modal]",timeout:1e3},{eval:"EVAL_COOKIEFIRST_1"},{wait:1e3},{click:"button[data-cookiefirst-action=save]"}],else:[{click:"button[data-cookiefirst-action=reject]"}]}],test:[{eval:"EVAL_COOKIEFIRST_0"}]},{name:"Cookie Information Banner",prehideSelectors:["#cookie-information-template-wrapper"],detectCmp:[{exists:"#cookie-information-template-wrapper"}],detectPopup:[{visible:"#cookie-information-template-wrapper"}],optIn:[{eval:"EVAL_COOKIEINFORMATION_1"}],optOut:[{hide:["#cookie-information-template-wrapper"],comment:"some templates don't hide the banner automatically"},{eval:"EVAL_COOKIEINFORMATION_0"}],test:[{eval:"EVAL_COOKIEINFORMATION_2"}]},{name:"corona-in-zahlen.de",prehideSelectors:[".cookiealert"],detectCmp:[{exists:".cookiealert"}],detectPopup:[{visible:".cookiealert"}],optOut:[{click:".configurecookies"},{click:".confirmcookies"}],optIn:[{click:".acceptcookies"}]},{name:"crossfit-com",cosmetic:!0,prehideSelectors:['body #modal > div > div[class^="_wrapper_"]'],detectCmp:[{exists:'body #modal > div > div[class^="_wrapper_"]'}],detectPopup:[{visible:'body #modal > div > div[class^="_wrapper_"]'}],optIn:[{click:'button[aria-label="accept cookie policy"]'}],optOut:[{hide:['body #modal > div > div[class^="_wrapper_"]']}]},{name:"dailymotion-us",cosmetic:!0,prehideSelectors:['div[class*="CookiePopup__desktopContainer"]:has(div[class*="CookiePopup"])'],detectCmp:[{exists:'div[class*="CookiePopup__desktopContainer"]'}],detectPopup:[{visible:'div[class*="CookiePopup__desktopContainer"]'}],optIn:[{click:'div[class*="CookiePopup__desktopContainer"] > button > span'}],optOut:[{hide:['div[class*="CookiePopup__desktopContainer"]']}]},{name:"dailymotion.com",runContext:{urlPattern:"^https://(www\\.)?dailymotion\\.com/"},prehideSelectors:['div[class*="Overlay__container"]:has(div[class*="TCF2Popup"])'],detectCmp:[{exists:'div[class*="TCF2Popup"]'}],detectPopup:[{visible:'[class*="TCF2Popup"] a[href^="https://www.dailymotion.com/legal/cookiemanagement"]'}],optIn:[{waitForThenClick:'button[class*="TCF2Popup__button"]:not([class*="TCF2Popup__personalize"])'}],optOut:[{waitForThenClick:'button[class*="TCF2ContinueWithoutAcceptingButton"]'}],test:[{eval:"EVAL_DAILYMOTION_0"}]},{name:"deepl.com",prehideSelectors:[".dl_cookieBanner_container"],detectCmp:[{exists:".dl_cookieBanner_container"}],detectPopup:[{visible:".dl_cookieBanner_container"}],optOut:[{click:".dl_cookieBanner--buttonSelected"}],optIn:[{click:".dl_cookieBanner--buttonAll"}]},{name:"delta.com",runContext:{urlPattern:"^https://www\\.delta\\.com/"},cosmetic:!0,prehideSelectors:["ngc-cookie-banner"],detectCmp:[{exists:"div.cookie-footer-container"}],detectPopup:[{visible:"div.cookie-footer-container"}],optIn:[{click:" button.cookie-close-icon"}],optOut:[{hide:["div.cookie-footer-container"]}]},{name:"dmgmedia-us",prehideSelectors:["#mol-ads-cmp-iframe, div.mol-ads-cmp > form > div"],detectCmp:[{exists:"div.mol-ads-cmp > form > div"}],detectPopup:[{waitForVisible:"div.mol-ads-cmp > form > div"}],optIn:[{waitForThenClick:"button.mol-ads-cmp--btn-primary"}],optOut:[{waitForThenClick:"div.mol-ads-ccpa--message > u > a"},{waitForVisible:".mol-ads-cmp--modal-dialog"},{waitForThenClick:"a.mol-ads-cmp-footer-privacy"},{waitForThenClick:"button.mol-ads-cmp--btn-secondary"}]},{name:"dmgmedia",prehideSelectors:['[data-project="mol-fe-cmp"]'],detectCmp:[{exists:'[data-project="mol-fe-cmp"]'}],detectPopup:[{visible:'[data-project="mol-fe-cmp"]'}],optIn:[{waitForThenClick:'[data-project="mol-fe-cmp"] button[class*=primary]'}],optOut:[{waitForThenClick:'[data-project="mol-fe-cmp"] button[class*=basic]'},{waitForVisible:'[data-project="mol-fe-cmp"] div[class*="tabContent"]'},{waitForThenClick:'[data-project="mol-fe-cmp"] div[class*="toggle"][class*="enabled"]',all:!0},{waitForThenClick:'[data-project="mol-fe-cmp"] button[class*=white]'}]},{name:"Drupal",detectCmp:[{exists:"#drupalorg-crosssite-gdpr"}],detectPopup:[{visible:"#drupalorg-crosssite-gdpr"}],optOut:[{click:".no"}],optIn:[{click:".yes"}]},{name:"WP DSGVO Tools",link:"https://wordpress.org/plugins/shapepress-dsgvo/",prehideSelectors:[".sp-dsgvo"],cosmetic:!0,detectCmp:[{exists:".sp-dsgvo.sp-dsgvo-popup-overlay"}],detectPopup:[{visible:".sp-dsgvo.sp-dsgvo-popup-overlay",check:"any"}],optIn:[{click:".sp-dsgvo-privacy-btn-accept-all",all:!0}],optOut:[{hide:[".sp-dsgvo.sp-dsgvo-popup-overlay"]}],test:[{eval:"EVAL_DSGVO_0"}]},{name:"dunelm.com",prehideSelectors:["div[data-testid=cookie-consent-modal-backdrop]"],detectCmp:[{exists:"div[data-testid=cookie-consent-message-contents]"}],detectPopup:[{visible:"div[data-testid=cookie-consent-message-contents]"}],optIn:[{click:'[data-testid="cookie-consent-allow-all"]'}],optOut:[{click:"button[data-testid=cookie-consent-adjust-settings]"},{click:"button[data-testid=cookie-consent-preferences-save]"}],test:[{eval:"EVAL_DUNELM_0"}]},{name:"etsy",prehideSelectors:["#gdpr-single-choice-overlay","#gdpr-privacy-settings"],detectCmp:[{exists:"#gdpr-single-choice-overlay"}],detectPopup:[{visible:"#gdpr-single-choice-overlay"}],optOut:[{click:"button[data-gdpr-open-full-settings]"},{waitForVisible:".gdpr-overlay-body input",timeout:3e3},{wait:1e3},{eval:"EVAL_ETSY_0"},{eval:"EVAL_ETSY_1"}],optIn:[{click:"button[data-gdpr-single-choice-accept]"}]},{name:"eu-cookie-compliance-banner",detectCmp:[{exists:".eu-cookie-compliance-banner-info"}],detectPopup:[{exists:".eu-cookie-compliance-popup-open"}],optIn:[{click:".agree-button"}],optOut:[{click:".decline-button,.eu-cookie-compliance-save-preferences-button",optional:!0},{hide:[".eu-cookie-compliance-banner-info","#sliding-popup"]}],test:[{eval:"EVAL_EU_COOKIE_COMPLIANCE_0"}]},{name:"EU Cookie Law",prehideSelectors:[".pea_cook_wrapper,.pea_cook_more_info_popover"],cosmetic:!0,detectCmp:[{exists:".pea_cook_wrapper"}],detectPopup:[{wait:500},{visible:".pea_cook_wrapper"}],optIn:[{click:"#pea_cook_btn"}],optOut:[{hide:[".pea_cook_wrapper"]}],test:[{eval:"EVAL_EU_COOKIE_LAW_0"}]},{name:"EZoic",prehideSelectors:["#ez-cookie-dialog-wrapper"],detectCmp:[{exists:"#ez-cookie-dialog-wrapper"}],detectPopup:[{visible:"#ez-cookie-dialog-wrapper"}],optIn:[{click:"#ez-accept-all",optional:!0},{eval:"EVAL_EZOIC_0",optional:!0}],optOut:[{wait:500},{click:"#ez-manage-settings"},{waitFor:"#ez-cookie-dialog input[type=checkbox]"},{click:"#ez-cookie-dialog input[type=checkbox][checked]",all:!0},{click:"#ez-save-settings"}],test:[{eval:"EVAL_EZOIC_1"}]},{name:"facebook",runContext:{urlPattern:"^https://([a-z0-9-]+\\.)?facebook\\.com/"},prehideSelectors:['div[data-testid="cookie-policy-manage-dialog"]'],detectCmp:[{exists:'div[data-testid="cookie-policy-manage-dialog"]'}],detectPopup:[{visible:'div[data-testid="cookie-policy-manage-dialog"]'}],optIn:[{waitForThenClick:'button[data-cookiebanner="accept_button"]'},{waitForVisible:'div[data-testid="cookie-policy-manage-dialog"]',check:"none"}],optOut:[{waitForThenClick:'button[data-cookiebanner="accept_only_essential_button"]'},{waitForVisible:'div[data-testid="cookie-policy-manage-dialog"]',check:"none"}]},{name:"funding-choices",prehideSelectors:[".fc-consent-root,.fc-dialog-container,.fc-dialog-overlay,.fc-dialog-content"],detectCmp:[{exists:".fc-consent-root"}],detectPopup:[{exists:".fc-dialog-container"}],optOut:[{click:".fc-cta-do-not-consent,.fc-cta-manage-options"},{click:".fc-preference-consent:checked,.fc-preference-legitimate-interest:checked",all:!0,optional:!0},{click:".fc-confirm-choices",optional:!0}],optIn:[{click:".fc-cta-consent"}]},{name:"geeks-for-geeks",runContext:{urlPattern:"^https://www\\.geeksforgeeks\\.org/"},cosmetic:!0,prehideSelectors:[".cookie-consent"],detectCmp:[{exists:".cookie-consent"}],detectPopup:[{visible:".cookie-consent"}],optIn:[{click:".cookie-consent button.consent-btn"}],optOut:[{hide:[".cookie-consent"]}]},{name:"generic-cosmetic",cosmetic:!0,prehideSelectors:["#js-cookie-banner,.js-cookie-banner,.cookie-banner,#cookie-banner"],detectCmp:[{exists:"#js-cookie-banner,.js-cookie-banner,.cookie-banner,#cookie-banner"}],detectPopup:[{visible:"#js-cookie-banner,.js-cookie-banner,.cookie-banner,#cookie-banner"}],optIn:[],optOut:[{hide:["#js-cookie-banner,.js-cookie-banner,.cookie-banner,#cookie-banner"]}]},{name:"google-consent-standalone",prehideSelectors:[],detectCmp:[{exists:'a[href^="https://policies.google.com/technologies/cookies"'},{exists:'form[action^="https://consent."][action$=".com/save"]'}],detectPopup:[{visible:'a[href^="https://policies.google.com/technologies/cookies"'}],optIn:[{waitForThenClick:'form[action^="https://consent."][action$=".com/save"]:has(input[name=set_eom][value=false]) button'}],optOut:[{waitForThenClick:'form[action^="https://consent."][action$=".com/save"]:has(input[name=set_eom][value=true]) button'}]},{name:"google.com",prehideSelectors:[".HTjtHe#xe7COe"],detectCmp:[{exists:".HTjtHe#xe7COe"},{exists:'.HTjtHe#xe7COe a[href^="https://policies.google.com/technologies/cookies"]'}],detectPopup:[{visible:".HTjtHe#xe7COe button#W0wltc"}],optIn:[{waitForThenClick:".HTjtHe#xe7COe button#L2AGLb"}],optOut:[{waitForThenClick:".HTjtHe#xe7COe button#W0wltc"}],test:[{eval:"EVAL_GOOGLE_0"}]},{name:"gov.uk",detectCmp:[{exists:"#global-cookie-message"}],detectPopup:[{exists:"#global-cookie-message"}],optIn:[{click:"button[data-accept-cookies=true]"}],optOut:[{click:"button[data-reject-cookies=true],#reject-cookies"},{click:"button[data-hide-cookie-banner=true],#hide-cookie-decision"}]},{name:"healthline-media",prehideSelectors:["#modal-host > div.no-hash > div.window-wrapper"],detectCmp:[{exists:"#modal-host > div.no-hash > div.window-wrapper, div[data-testid=qualtrics-container]"}],detectPopup:[{exists:"#modal-host > div.no-hash > div.window-wrapper, div[data-testid=qualtrics-container]"}],optIn:[{click:"#modal-host > div.no-hash > div.window-wrapper > div:last-child button"}],optOut:[{if:{exists:'#modal-host > div.no-hash > div.window-wrapper > div:last-child a[href="/privacy-settings"]'},then:[{click:'#modal-host > div.no-hash > div.window-wrapper > div:last-child a[href="/privacy-settings"]'}],else:[{waitForVisible:"div#__next"},{click:"#__next div:nth-child(1) > button:first-child"}]}]},{name:"hl.co.uk",prehideSelectors:[".cookieModalContent","#cookie-banner-overlay"],detectCmp:[{exists:"#cookie-banner-overlay"}],detectPopup:[{exists:"#cookie-banner-overlay"}],optIn:[{click:"#acceptCookieButton"}],optOut:[{click:"#manageCookie"},{hide:[".cookieSettingsModal"]},{waitFor:"#AOCookieToggle"},{click:"#AOCookieToggle[aria-pressed=true]",optional:!0},{waitFor:"#TPCookieToggle"},{click:"#TPCookieToggle[aria-pressed=true]",optional:!0},{click:"#updateCookieButton"}]},{name:"hubspot",detectCmp:[{exists:"#hs-eu-cookie-confirmation"}],detectPopup:[{visible:"#hs-eu-cookie-confirmation"}],optIn:[{click:"#hs-eu-confirmation-button"}],optOut:[{click:"#hs-eu-decline-button"}]},{name:"indeed.com",cosmetic:!0,prehideSelectors:["#CookiePrivacyNotice"],detectCmp:[{exists:"#CookiePrivacyNotice"}],detectPopup:[{visible:"#CookiePrivacyNotice"}],optIn:[{click:"#CookiePrivacyNotice button[data-gnav-element-name=CookiePrivacyNoticeOk]"}],optOut:[{hide:["#CookiePrivacyNotice"]}]},{name:"ionos.de",prehideSelectors:[".privacy-consent--backdrop",".privacy-consent--modal"],detectCmp:[{exists:".privacy-consent--modal"}],detectPopup:[{visible:".privacy-consent--modal"}],optIn:[{click:"#selectAll"}],optOut:[{click:".footer-config-link"},{click:"#confirmSelection"}]},{name:"itopvpn.com",cosmetic:!0,prehideSelectors:[".pop-cookie"],detectCmp:[{exists:".pop-cookie"}],detectPopup:[{exists:".pop-cookie"}],optIn:[{click:"#_pcookie"}],optOut:[{hide:[".pop-cookie"]}]},{name:"iubenda",prehideSelectors:["#iubenda-cs-banner"],detectCmp:[{exists:"#iubenda-cs-banner"}],detectPopup:[{visible:".iubenda-cs-accept-btn"}],optIn:[{click:".iubenda-cs-accept-btn"}],optOut:[{click:".iubenda-cs-customize-btn"},{eval:"EVAL_IUBENDA_0"},{click:"#iubFooterBtn"}],test:[{eval:"EVAL_IUBENDA_1"}]},{name:"johnlewis.com",prehideSelectors:["div[class^=pecr-cookie-banner-]"],detectCmp:[{exists:"div[class^=pecr-cookie-banner-]"}],detectPopup:[{exists:"div[class^=pecr-cookie-banner-]"}],optOut:[{click:"button[data-test^=manage-cookies]"},{wait:"500"},{click:"label[data-test^=toggle][class*=checked]:not([class*=disabled])",all:!0,optional:!0},{click:"button[data-test=save-preferences]"}],optIn:[{click:"button[data-test=allow-all]"}]},{name:"jquery.cookieBar",comment:"https://github.com/kovarp/jquery.cookieBar",prehideSelectors:[".cookie-bar"],cosmetic:!0,detectCmp:[{exists:".cookie-bar .cookie-bar__message,.cookie-bar .cookie-bar__buttons"}],detectPopup:[{visible:".cookie-bar .cookie-bar__message,.cookie-bar .cookie-bar__buttons",check:"any"}],optIn:[{click:".cookie-bar .cookie-bar__btn"}],optOut:[{hide:[".cookie-bar"]}],test:[{visible:".cookie-bar .cookie-bar__message,.cookie-bar .cookie-bar__buttons",check:"none"},{eval:"EVAL_JQUERY_COOKIEBAR_0"}]},{name:"justwatch.com",prehideSelectors:[".consent-banner"],detectCmp:[{exists:".consent-banner .consent-banner__actions"}],detectPopup:[{visible:".consent-banner .consent-banner__actions"}],optIn:[{click:".consent-banner__actions button.basic-button.primary"}],optOut:[{click:".consent-banner__actions button.basic-button.secondary"},{waitForThenClick:".consent-modal__footer button.basic-button.secondary"},{waitForThenClick:".consent-modal ion-content > div > a:nth-child(9)"},{click:"label.consent-switch input[type=checkbox]:checked",all:!0,optional:!0},{waitForVisible:".consent-modal__footer button.basic-button.primary"},{click:".consent-modal__footer button.basic-button.primary"}]},{name:"ketch",runContext:{frame:!1,main:!0},intermediate:!1,prehideSelectors:["#lanyard_root div[role='dialog']"],detectCmp:[{exists:"#lanyard_root div[role='dialog']"}],detectPopup:[{visible:"#lanyard_root div[role='dialog']"}],optIn:[{if:{exists:"#lanyard_root button[class='confirmButton']"},then:[{waitForThenClick:"#lanyard_root div[class^='buttons'] > :nth-child(2)"},{click:"#lanyard_root button[class='confirmButton']"}],else:[{waitForThenClick:"#lanyard_root div[class^='buttons'] > :nth-child(2)"}]}],optOut:[{click:"#lanyard_root button[class^='link']",optional:!0},{if:{exists:"#lanyard_root button[class*='confirmButton']"},then:[{waitForThenClick:"#lanyard_root button[class*='rejectButton']"},{click:"#lanyard_root button[class*='confirmButton']"}],else:[{click:"#lanyard_root div[class^='buttons'] > :nth-child(1)",optional:!0},{waitForThenClick:"#lanyard_root input:checked"},{click:"#consentsTab > div:nth-child(2) > div > div[class^='actions'] > button:nth-child(1)"}]}],test:[]},{name:"kleinanzeigen-de",runContext:{urlPattern:"^https?://(www\\.)?kleinanzeigen\\.de"},prehideSelectors:["#gdpr-banner-container"],detectCmp:[{any:[{exists:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-cmp-button]"},{exists:"#ConsentManagementPage"}]}],detectPopup:[{any:[{visible:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-cmp-button]"},{visible:"#ConsentManagementPage"}]}],optIn:[{if:{exists:"#gdpr-banner-container #gdpr-banner"},then:[{click:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-accept]"}],else:[{click:"#ConsentManagementPage .Button-primary"}]}],optOut:[{if:{exists:"#gdpr-banner-container #gdpr-banner"},then:[{click:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-cmp-button]"}],else:[{click:"#ConsentManagementPage .Button-secondary"}]}]},{name:"linkedin.com",prehideSelectors:[".artdeco-global-alert[type=COOKIE_CONSENT]"],detectCmp:[{exists:".artdeco-global-alert[type=COOKIE_CONSENT]"}],detectPopup:[{visible:".artdeco-global-alert[type=COOKIE_CONSENT]"}],optIn:[{waitForVisible:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=ACCEPT]"},{wait:500},{waitForThenClick:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=ACCEPT]"}],optOut:[{waitForVisible:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=DENY]"},{wait:500},{waitForThenClick:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=DENY]"}],test:[{waitForVisible:".artdeco-global-alert[type=COOKIE_CONSENT]",check:"none"}]},{name:"macpaw.com",cosmetic:!0,prehideSelectors:['div[data-banner="cookies"]'],detectCmp:[{exists:'div[data-banner="cookies"]'}],detectPopup:[{exists:'div[data-banner="cookies"]'}],optIn:[{click:'button[data-banner-close="cookies"]'}],optOut:[{hide:['div[data-banner="cookies"]']}]},{name:"marksandspencer.com",cosmetic:!0,detectCmp:[{exists:".navigation-cookiebbanner"}],detectPopup:[{visible:".navigation-cookiebbanner"}],optOut:[{hide:[".navigation-cookiebbanner"]}],optIn:[{click:".navigation-cookiebbanner__submit"}]},{name:"mediamarkt.de",prehideSelectors:["div[aria-labelledby=pwa-consent-layer-title]","div[class^=StyledConsentLayerWrapper-]"],detectCmp:[{exists:"div[aria-labelledby^=pwa-consent-layer-title]"}],detectPopup:[{exists:"div[aria-labelledby^=pwa-consent-layer-title]"}],optOut:[{click:"button[data-test^=pwa-consent-layer-deny-all]"}],optIn:[{click:"button[data-test^=pwa-consent-layer-accept-all"}]},{name:"Mediavine",prehideSelectors:['[data-name="mediavine-gdpr-cmp"]'],detectCmp:[{exists:'[data-name="mediavine-gdpr-cmp"]'}],detectPopup:[{wait:500},{visible:'[data-name="mediavine-gdpr-cmp"]'}],optIn:[{waitForThenClick:'[data-name="mediavine-gdpr-cmp"] [format="primary"]'}],optOut:[{waitForThenClick:'[data-name="mediavine-gdpr-cmp"] [data-view="manageSettings"]'},{waitFor:'[data-name="mediavine-gdpr-cmp"] input[type=checkbox]'},{eval:"EVAL_MEDIAVINE_0",optional:!0},{click:'[data-name="mediavine-gdpr-cmp"] [format="secondary"]'}]},{name:"microsoft.com",prehideSelectors:["#wcpConsentBannerCtrl"],detectCmp:[{exists:"#wcpConsentBannerCtrl"}],detectPopup:[{exists:"#wcpConsentBannerCtrl"}],optOut:[{eval:"EVAL_MICROSOFT_0"}],optIn:[{eval:"EVAL_MICROSOFT_1"}],test:[{eval:"EVAL_MICROSOFT_2"}]},{name:"midway-usa",runContext:{urlPattern:"^https://www\\.midwayusa\\.com/"},cosmetic:!0,prehideSelectors:["#cookie-container"],detectCmp:[{exists:['div[aria-label="Cookie Policy Banner"]']}],detectPopup:[{visible:"#cookie-container"}],optIn:[{click:"button#cookie-btn"}],optOut:[{hide:['div[aria-label="Cookie Policy Banner"]']}]},{name:"moneysavingexpert.com",detectCmp:[{exists:"dialog[data-testid=accept-our-cookies-dialog]"}],detectPopup:[{visible:"dialog[data-testid=accept-our-cookies-dialog]"}],optIn:[{click:"#banner-accept"}],optOut:[{click:"#banner-manage"},{click:"#pc-confirm"}]},{name:"monzo.com",prehideSelectors:[".cookie-alert, cookie-alert__content"],detectCmp:[{exists:'div.cookie-alert[role="dialog"]'},{exists:'a[href*="monzo"]'}],detectPopup:[{visible:".cookie-alert__content"}],optIn:[{click:".js-accept-cookie-policy"}],optOut:[{click:".js-decline-cookie-policy"}]},{name:"Moove",prehideSelectors:["#moove_gdpr_cookie_info_bar"],detectCmp:[{exists:"#moove_gdpr_cookie_info_bar"}],detectPopup:[{visible:"#moove_gdpr_cookie_info_bar"}],optIn:[{waitForThenClick:".moove-gdpr-infobar-allow-all"}],optOut:[{if:{exists:"#moove_gdpr_cookie_info_bar .change-settings-button"},then:[{click:"#moove_gdpr_cookie_info_bar .change-settings-button"},{waitForVisible:"#moove_gdpr_cookie_modal"},{eval:"EVAL_MOOVE_0"},{click:".moove-gdpr-modal-save-settings"}],else:[{hide:["#moove_gdpr_cookie_info_bar"]}]}],test:[{visible:"#moove_gdpr_cookie_info_bar",check:"none"}]},{name:"national-lottery.co.uk",detectCmp:[{exists:".cuk_cookie_consent"}],detectPopup:[{visible:".cuk_cookie_consent",check:"any"}],optOut:[{click:".cuk_cookie_consent_manage_pref"},{click:".cuk_cookie_consent_save_pref"},{click:".cuk_cookie_consent_close"}],optIn:[{click:".cuk_cookie_consent_accept_all"}]},{name:"nba.com",runContext:{urlPattern:"^https://(www\\.)?nba.com/"},cosmetic:!0,prehideSelectors:["#onetrust-banner-sdk"],detectCmp:[{exists:"#onetrust-banner-sdk"}],detectPopup:[{visible:"#onetrust-banner-sdk"}],optIn:[{click:"#onetrust-accept-btn-handler"}],optOut:[{hide:["#onetrust-banner-sdk"]}]},{name:"netflix.de",detectCmp:[{exists:"#cookie-disclosure"}],detectPopup:[{visible:".cookie-disclosure-message",check:"any"}],optIn:[{click:".btn-accept"}],optOut:[{hide:["#cookie-disclosure"]},{click:".btn-reject"}]},{name:"nhs.uk",prehideSelectors:["#nhsuk-cookie-banner"],detectCmp:[{exists:"#nhsuk-cookie-banner"}],detectPopup:[{exists:"#nhsuk-cookie-banner"}],optOut:[{click:"#nhsuk-cookie-banner__link_accept"}],optIn:[{click:"#nhsuk-cookie-banner__link_accept_analytics"}]},{name:"notice-cookie",prehideSelectors:[".button--notice"],cosmetic:!0,detectCmp:[{exists:".notice--cookie"}],detectPopup:[{visible:".notice--cookie"}],optIn:[{click:".button--notice"}],optOut:[{hide:[".notice--cookie"]}]},{name:"nrk.no",cosmetic:!0,prehideSelectors:[".nrk-masthead__info-banner--cookie"],detectCmp:[{exists:".nrk-masthead__info-banner--cookie"}],detectPopup:[{exists:".nrk-masthead__info-banner--cookie"}],optIn:[{click:"div.nrk-masthead__info-banner--cookie button > span:has(+ svg.nrk-close)"}],optOut:[{hide:[".nrk-masthead__info-banner--cookie"]}]},{name:"obi.de",prehideSelectors:[".disc-cp--active"],detectCmp:[{exists:".disc-cp-modal__modal"}],detectPopup:[{visible:".disc-cp-modal__modal"}],optIn:[{click:".js-disc-cp-accept-all"}],optOut:[{click:".js-disc-cp-deny-all"}]},{name:"onlyFans.com",prehideSelectors:["div.b-cookies-informer"],detectCmp:[{exists:"div.b-cookies-informer"}],detectPopup:[{exists:"div.b-cookies-informer"}],optIn:[{click:"div.b-cookies-informer__nav > button:nth-child(2)"}],optOut:[{click:"div.b-cookies-informer__nav > button:nth-child(1)"},{click:'div.b-cookies-informer__switchers > div:nth-child(2) > div[at-attr="checkbox"] > span.b-input-radio__container > input[type="checkbox"]'},{click:"div.b-cookies-informer__nav > button"}]},{name:"osano",prehideSelectors:[".osano-cm-window"],cosmetic:!0,detectCmp:[{exists:".osano-cm-window"}],detectPopup:[{visible:".osano-cm-dialog"}],optIn:[{click:".osano-cm-accept-all",optional:!0}],optOut:[{hide:[".osano-cm-window"]}]},{name:"otto.de",prehideSelectors:[".cookieBanner--visibility"],detectCmp:[{exists:".cookieBanner--visibility"}],detectPopup:[{visible:".cookieBanner__wrapper"}],optIn:[{click:".js_cookieBannerPermissionButton"}],optOut:[{click:".js_cookieBannerProhibitionButton"}]},{name:"paypal-us",prehideSelectors:["#ccpaCookieContent_wrapper, article.ppvx_modal--overpanel"],detectCmp:[{exists:"#ccpaCookieBanner, .privacy-modal-content"}],detectPopup:[{exists:"#ccpaCookieBanner, .privacy-modal-content"}],optIn:[{click:"#acceptAllButton"}],optOut:[{if:{exists:"a#manageCookiesLink"},then:[{click:"a#manageCookiesLink"}],else:[{waitForVisible:".privacy-modal-content #formContent"},{click:"#formContent .cookiepref-11m2iee-checkbox_base input:checked",all:!0,optional:!0},{click:".confirmCookie #submitCookiesBtn"}]}]},{name:"paypal.com",prehideSelectors:["#gdprCookieBanner"],detectCmp:[{exists:"#gdprCookieBanner"}],detectPopup:[{visible:"#gdprCookieContent_wrapper"}],optIn:[{click:"#acceptAllButton"}],optOut:[{wait:200},{click:".gdprCookieBanner_decline-button"}],test:[{wait:500},{eval:"EVAL_PAYPAL_0"}]},{name:"pinetools.com",cosmetic:!0,prehideSelectors:["#aviso_cookies"],detectCmp:[{exists:"#aviso_cookies"}],detectPopup:[{exists:".lang_en #aviso_cookies"}],optIn:[{click:"#aviso_cookies .a_boton_cerrar"}],optOut:[{hide:["#aviso_cookies"]}]},{name:"pmc",cosmetic:!0,prehideSelectors:["#pmc-pp-tou--notice"],detectCmp:[{exists:"#pmc-pp-tou--notice"}],detectPopup:[{visible:"#pmc-pp-tou--notice"}],optIn:[{click:"span.pmc-pp-tou--notice-close-btn"}],optOut:[{hide:["#pmc-pp-tou--notice"]}]},{name:"pornhub.com",runContext:{urlPattern:"^https://(www\\.)?pornhub\\.com/"},cosmetic:!0,prehideSelectors:[".cookiesBanner"],detectCmp:[{exists:".cookiesBanner"}],detectPopup:[{visible:".cookiesBanner"}],optIn:[{click:".cookiesBanner .okButton"}],optOut:[{hide:[".cookiesBanner"]}]},{name:"pornpics.com",cosmetic:!0,prehideSelectors:["#cookie-contract"],detectCmp:[{exists:"#cookie-contract"}],detectPopup:[{visible:"#cookie-contract"}],optIn:[{click:"#cookie-contract .icon-cross"}],optOut:[{hide:["#cookie-contract"]}]},{name:"PrimeBox CookieBar",prehideSelectors:["#cookie-bar"],detectCmp:[{exists:"#cookie-bar .cb-enable,#cookie-bar .cb-disable,#cookie-bar .cb-policy"}],detectPopup:[{visible:"#cookie-bar .cb-enable,#cookie-bar .cb-disable,#cookie-bar .cb-policy",check:"any"}],optIn:[{waitForThenClick:"#cookie-bar .cb-enable"}],optOut:[{click:"#cookie-bar .cb-disable",optional:!0},{hide:["#cookie-bar"]}],test:[{eval:"EVAL_PRIMEBOX_0"}]},{name:"privacymanager.io",prehideSelectors:["#gdpr-consent-tool-wrapper",'iframe[src^="https://cmp-consent-tool.privacymanager.io"]'],runContext:{urlPattern:"^https://cmp-consent-tool\\.privacymanager\\.io/",main:!1,frame:!0},detectCmp:[{exists:"button#save"}],detectPopup:[{visible:"button#save"}],optIn:[{click:"button#save"}],optOut:[{if:{exists:"#denyAll"},then:[{click:"#denyAll"},{waitForThenClick:".okButton"}],else:[{waitForThenClick:"#manageSettings"},{waitFor:".purposes-overview-list"},{waitFor:"button#saveAndExit"},{click:"span[role=checkbox][aria-checked=true]",all:!0,optional:!0},{click:"button#saveAndExit"}]}]},{name:"pubtech",prehideSelectors:["#pubtech-cmp"],detectCmp:[{exists:"#pubtech-cmp"}],detectPopup:[{visible:"#pubtech-cmp #pt-actions"}],optIn:[{if:{exists:"#pt-accept-all"},then:[{click:"#pubtech-cmp #pt-actions #pt-accept-all"}],else:[{click:"#pubtech-cmp #pt-actions button:nth-of-type(2)"}]}],optOut:[{click:"#pubtech-cmp #pt-close"}],test:[{eval:"EVAL_PUBTECH_0"}]},{name:"quantcast",prehideSelectors:["#qc-cmp2-main,#qc-cmp2-container"],detectCmp:[{exists:"#qc-cmp2-container"}],detectPopup:[{visible:"#qc-cmp2-ui"}],optOut:[{click:'.qc-cmp2-summary-buttons > button[mode="secondary"]'},{waitFor:"#qc-cmp2-ui"},{click:'.qc-cmp2-toggle-switch > button[aria-checked="true"]',all:!0,optional:!0},{click:'.qc-cmp2-main button[aria-label="REJECT ALL"]',optional:!0},{waitForThenClick:'.qc-cmp2-main button[aria-label="SAVE & EXIT"],.qc-cmp2-buttons-desktop > button[mode="primary"]',timeout:5e3}],optIn:[{click:'.qc-cmp2-summary-buttons > button[mode="primary"]'}]},{name:"reddit.com",runContext:{urlPattern:"^https://www\\.reddit\\.com/"},prehideSelectors:['section:has(a[href^="https://www.reddit.com/policies/cookies"])'],detectCmp:[{exists:'section:has(a[href^="https://www.reddit.com/policies/cookies"])'}],detectPopup:[{visible:'section:has(a[href^="https://www.reddit.com/policies/cookies"])'}],optIn:[{waitForThenClick:"section:has(a[href^=\"https://www.reddit.com/policies/cookies\"]) section[class^='_'] > section:first-child form button"}],optOut:[{waitForThenClick:"section:has(a[href^=\"https://www.reddit.com/policies/cookies\"]) section[class^='_'] > section:last-child form button"}],test:[{eval:"EVAL_REDDIT_0"}]},{name:"samsung.com",runContext:{urlPattern:"^https://www\\.samsung\\.com/"},cosmetic:!0,prehideSelectors:["div.cookie-bar"],detectCmp:[{exists:"div.cookie-bar"}],detectPopup:[{visible:"div.cookie-bar"}],optIn:[{click:"div.cookie-bar__manage > a"}],optOut:[{hide:["div.cookie-bar"]}]},{name:"sibbo",prehideSelectors:["sibbo-cmp-layout"],detectCmp:[{exists:"sibbo-cmp-layout"}],detectPopup:[{visible:"sibbo-cmp-layout"}],optIn:[{click:"sibbo-cmp-layout [data-accept-all]"}],optOut:[{click:'.sibbo-panel__aside__buttons a[data-nav="purposes"]'},{click:'.sibbo-panel__main__header__actions a[data-focusable="reject-all"]'},{if:{exists:"[data-view=purposes] .sibbo-panel__main__footer__actions [data-save-and-exit]"},then:[],else:[{waitFor:'.sibbo-panel__main__footer__actions a[data-focusable="next"]:not(.sibbo-cmp-button--disabled)'},{click:'.sibbo-panel__main__footer__actions a[data-focusable="next"]'},{click:'.sibbo-panel__main div[data-view="purposesLegInt"] a[data-focusable="reject-all"]'}]},{waitFor:".sibbo-panel__main__footer__actions [data-save-and-exit]:not(.sibbo-cmp-button--disabled)"},{click:".sibbo-panel__main__footer__actions [data-save-and-exit]:not(.sibbo-cmp-button--disabled)"}],test:[{eval:"EVAL_SIBBO_0"}]},{name:"similarweb.com",cosmetic:!0,prehideSelectors:[".app-cookies-notification"],detectCmp:[{exists:".app-cookies-notification"}],detectPopup:[{exists:".app-layout .app-cookies-notification"}],optIn:[{click:"button.app-cookies-notification__dismiss"}],optOut:[{hide:[".app-layout .app-cookies-notification"]}]},{name:"Sirdata",prehideSelectors:["#sd-cmp"],detectCmp:[{exists:"#sd-cmp"}],detectPopup:[{visible:"#sd-cmp"}],optIn:[{waitForThenClick:"#sd-cmp .sd-cmp-3cRQ2"}],optOut:[{waitForThenClick:"#sd-cmp .sd-cmp-1pO44"}],test:[{eval:"EVAL_SIRDATA_0"}]},{name:"snigel",detectCmp:[{exists:".snigel-cmp-framework"}],detectPopup:[{visible:".snigel-cmp-framework"}],optOut:[{click:"#sn-b-custom"},{click:"#sn-b-save"}],test:[{eval:"EVAL_SNIGEL_0"}],optIn:[{click:".snigel-cmp-framework #accept-choices"}]},{name:"steampowered.com",detectCmp:[{exists:".cookiepreferences_popup"},{visible:".cookiepreferences_popup"}],detectPopup:[{visible:".cookiepreferences_popup"}],optOut:[{click:"#rejectAllButton"}],optIn:[{click:"#acceptAllButton"}],test:[{wait:1e3},{eval:"EVAL_STEAMPOWERED_0"}]},{name:"takealot.com",cosmetic:!0,prehideSelectors:['div[class^="cookies-banner-module_cookie-banner_"]'],detectCmp:[{exists:'div[class^="cookies-banner-module_cookie-banner_"]'}],detectPopup:[{exists:'div[class^="cookies-banner-module_cookie-banner_"]'}],optIn:[{click:'button[class*="cookies-banner-module_dismiss-button_"]'}],optOut:[{hide:['div[class^="cookies-banner-module_cookie-banner_"]']}]},{name:"tarteaucitron.js",prehideSelectors:["#tarteaucitronRoot"],detectCmp:[{exists:"#tarteaucitronRoot"}],detectPopup:[{visible:"#tarteaucitronRoot #tarteaucitronAlertSmall,#tarteaucitronRoot #tarteaucitronAlertBig",check:"any"}],optIn:[{eval:"EVAL_TARTEAUCITRON_1"}],optOut:[{eval:"EVAL_TARTEAUCITRON_0"}],test:[{eval:"EVAL_TARTEAUCITRON_2",comment:"sometimes there are required categories, so we check that at least something is false"}]},{name:"Tealium",prehideSelectors:["#__tealiumGDPRecModal,#__tealiumGDPRcpPrefs,#consent-layer"],detectCmp:[{visible:"#__tealiumGDPRecModal"},{eval:"EVAL_TEALIUM_0"}],detectPopup:[{visible:"#__tealiumGDPRecModal"}],optOut:[{waitForThenClick:"#cm-acceptNone,.js-accept-essential-cookies",timeout:1e3},{eval:"EVAL_TEALIUM_1"}],optIn:[{hide:["#__tealiumGDPRecModal"]},{eval:"EVAL_TEALIUM_2"}],test:[{eval:"EVAL_TEALIUM_3"}]},{name:"Termly",prehideSelectors:["#termly-code-snippet-support"],detectCmp:[{exists:"#termly-code-snippet-support"}],detectPopup:[{visible:"#termly-code-snippet-support div"}],optIn:[{waitForThenClick:'[data-tid="banner-accept"]'}],optOut:[{if:{exists:'[data-tid="banner-decline"]'},then:[{click:'[data-tid="banner-decline"]'}],else:[{click:".t-preference-button"},{wait:500},{if:{exists:".t-declineAllButton"},then:[{click:".t-declineAllButton"}],else:[{waitForThenClick:".t-preference-modal input[type=checkbox][checked]:not([disabled])",all:!0},{waitForThenClick:".t-saveButton"}]}]}]},{name:"Test page cosmetic CMP",cosmetic:!0,prehideSelectors:["#privacy-test-page-cmp-test-prehide"],detectCmp:[{exists:"#privacy-test-page-cmp-test-banner"}],detectPopup:[{visible:"#privacy-test-page-cmp-test-banner"}],optIn:[{waitFor:"#accept-all"},{click:"#accept-all"}],optOut:[{hide:["#privacy-test-page-cmp-test-banner"]}],test:[{wait:500},{eval:"EVAL_TESTCMP_COSMETIC_0"}]},{name:"Test page CMP",prehideSelectors:["#reject-all"],detectCmp:[{exists:"#privacy-test-page-cmp-test"}],detectPopup:[{visible:"#privacy-test-page-cmp-test"}],optIn:[{waitFor:"#accept-all"},{click:"#accept-all"}],optOut:[{waitFor:"#reject-all"},{click:"#reject-all"}],test:[{eval:"EVAL_TESTCMP_0"}]},{name:"thalia.de",prehideSelectors:[".consent-banner-box"],detectCmp:[{exists:"consent-banner[component=consent-banner]"}],detectPopup:[{visible:".consent-banner-box"}],optIn:[{click:".button-zustimmen"}],optOut:[{click:"button[data-consent=disagree]"}]},{name:"thefreedictionary.com",prehideSelectors:["#cmpBanner"],detectCmp:[{exists:"#cmpBanner"}],detectPopup:[{visible:"#cmpBanner"}],optIn:[{eval:"EVAL_THEFREEDICTIONARY_1"}],optOut:[{eval:"EVAL_THEFREEDICTIONARY_0"}]},{name:"theverge",runContext:{frame:!1,main:!0,urlPattern:"^https://(www)?\\.theverge\\.com"},intermediate:!1,prehideSelectors:[".duet--cta--cookie-banner"],detectCmp:[{exists:".duet--cta--cookie-banner"}],detectPopup:[{visible:".duet--cta--cookie-banner"}],optIn:[{click:".duet--cta--cookie-banner button.tracking-12",all:!1}],optOut:[{click:".duet--cta--cookie-banner button.tracking-12 > span"}],test:[{eval:"EVAL_THEVERGE_0"}]},{name:"tidbits-com",cosmetic:!0,prehideSelectors:["#eu_cookie_law_widget-2"],detectCmp:[{exists:"#eu_cookie_law_widget-2"}],detectPopup:[{visible:"#eu_cookie_law_widget-2"}],optIn:[{click:"#eu-cookie-law form > input.accept"}],optOut:[{hide:["#eu_cookie_law_widget-2"]}]},{name:"tractor-supply",runContext:{urlPattern:"^https://www\\.tractorsupply\\.com/"},cosmetic:!0,prehideSelectors:[".tsc-cookie-banner"],detectCmp:[{exists:".tsc-cookie-banner"}],detectPopup:[{visible:".tsc-cookie-banner"}],optIn:[{click:"#cookie-banner-cancel"}],optOut:[{hide:[".tsc-cookie-banner"]}]},{name:"trader-joes-com",cosmetic:!0,prehideSelectors:['div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]'],detectCmp:[{exists:'div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]'}],detectPopup:[{visible:'div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]'}],optIn:[{click:'div[class^="CookiesAlert_cookiesAlert__container__"] button'}],optOut:[{hide:['div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]']}]},{name:"true-car",runContext:{urlPattern:"^https://www\\.truecar\\.com/"},cosmetic:!0,prehideSelectors:[['div[aria-labelledby="cookie-banner-heading"]']],detectCmp:[{exists:'div[aria-labelledby="cookie-banner-heading"]'}],detectPopup:[{visible:'div[aria-labelledby="cookie-banner-heading"]'}],optIn:[{click:'div[aria-labelledby="cookie-banner-heading"] > button[aria-label="Close"]'}],optOut:[{hide:['div[aria-labelledby="cookie-banner-heading"]']}]},{name:"truyo",prehideSelectors:["#truyo-consent-module"],detectCmp:[{exists:"#truyo-cookieBarContent"}],detectPopup:[{visible:"#truyo-consent-module"}],optIn:[{click:"button#acceptAllCookieButton"}],optOut:[{click:"button#declineAllCookieButton"}]},{name:"tumblr-com",cosmetic:!0,prehideSelectors:["#cmp-app-container"],detectCmp:[{exists:"#cmp-app-container"}],detectPopup:[{visible:"#cmp-app-container"}],optIn:[{click:"#tumblr #cmp-app-container div.components-modal__frame > iframe > html body > div > div > div.cmp__dialog-footer > div > button.components-button.white-space-normal.is-primary"}],optOut:[{hide:["#cmp-app-container"]}]},{name:"twitch.tv",runContext:{urlPattern:"^https?://(www\\.)?twitch\\.tv"},prehideSelectors:["div:has(> .consent-banner .consent-banner__content--gdpr-v2),.ReactModalPortal:has([data-a-target=consent-modal-save])"],detectCmp:[{exists:".consent-banner .consent-banner__content--gdpr-v2"}],detectPopup:[{visible:".consent-banner .consent-banner__content--gdpr-v2"}],optIn:[{click:'button[data-a-target="consent-banner-accept"]'}],optOut:[{hide:["div:has(> .consent-banner .consent-banner__content--gdpr-v2)"]},{click:'button[data-a-target="consent-banner-manage-preferences"]'},{waitFor:"input[type=checkbox][data-a-target=tw-checkbox]"},{click:"input[type=checkbox][data-a-target=tw-checkbox][checked]:not([disabled])",all:!0,optional:!0},{waitForThenClick:"[data-a-target=consent-modal-save]"},{waitForVisible:".ReactModalPortal:has([data-a-target=consent-modal-save])",check:"none"}]},{name:"twitter",runContext:{urlPattern:"^https://([a-z0-9-]+\\.)?twitter\\.com/"},prehideSelectors:['[data-testid="BottomBar"]'],detectCmp:[{exists:'[data-testid="BottomBar"] div'}],detectPopup:[{visible:'[data-testid="BottomBar"] div'}],optIn:[{waitForThenClick:'[data-testid="BottomBar"] > div:has(>div:first-child>div:last-child>span[role=button]) > div:last-child > div[role=button]:first-child'}],optOut:[{waitForThenClick:'[data-testid="BottomBar"] > div:has(>div:first-child>div:last-child>span[role=button]) > div:last-child > div[role=button]:last-child'}],TODOtest:[{eval:"EVAL_document.cookie.includes('d_prefs=MjoxLGNvbnNlbnRfdmVyc2lvbjoy')"}]},{name:"ubuntu.com",prehideSelectors:["dialog.cookie-policy"],detectCmp:[{any:[{exists:"dialog.cookie-policy header"},{exists:'xpath///*[@id="modal"]/div/header'}]}],detectPopup:[{any:[{visible:"dialog header"},{visible:'xpath///*[@id="modal"]/div/header'}]}],optIn:[{any:[{waitForThenClick:"#cookie-policy-button-accept"},{waitForThenClick:'xpath///*[@id="cookie-policy-button-accept"]'}]}],optOut:[{any:[{waitForThenClick:"button.p-button"},{waitForThenClick:'xpath///*[@id="cookie-policy-content"]/p[4]/button[2]'}]},{waitForThenClick:".p-switch__input:checked",optional:!0,all:!0},{any:[{waitForThenClick:"div > button"},{waitForThenClick:'xpath///*[@id="modal"]/div/button'}]}],test:[{eval:"EVAL_UBUNTU_COM_0"}]},{name:"UK Cookie Consent",prehideSelectors:["#catapult-cookie-bar"],cosmetic:!0,detectCmp:[{exists:"#catapult-cookie-bar"}],detectPopup:[{exists:".has-cookie-bar #catapult-cookie-bar"}],optIn:[{click:"#catapultCookie"}],optOut:[{hide:["#catapult-cookie-bar"]}],test:[{eval:"EVAL_UK_COOKIE_CONSENT_0"}]},{name:"urbanarmorgear-com",cosmetic:!0,prehideSelectors:['div[class^="Layout__CookieBannerContainer-"]'],detectCmp:[{exists:'div[class^="Layout__CookieBannerContainer-"]'}],detectPopup:[{visible:'div[class^="Layout__CookieBannerContainer-"]'}],optIn:[{click:'button[class^="CookieBanner__AcceptButton"]'}],optOut:[{hide:['div[class^="Layout__CookieBannerContainer-"]']}]},{name:"usercentrics-api",detectCmp:[{exists:"#usercentrics-root"}],detectPopup:[{eval:"EVAL_USERCENTRICS_API_0"},{exists:["#usercentrics-root","[data-testid=uc-container]"]}],optIn:[{eval:"EVAL_USERCENTRICS_API_3"},{eval:"EVAL_USERCENTRICS_API_1"},{eval:"EVAL_USERCENTRICS_API_5"}],optOut:[{eval:"EVAL_USERCENTRICS_API_1"},{eval:"EVAL_USERCENTRICS_API_2"}],test:[{eval:"EVAL_USERCENTRICS_API_6"}]},{name:"usercentrics-button",detectCmp:[{exists:"#usercentrics-button"}],detectPopup:[{visible:"#usercentrics-button #uc-btn-accept-banner"}],optIn:[{click:"#usercentrics-button #uc-btn-accept-banner"}],optOut:[{click:"#usercentrics-button #uc-btn-deny-banner"}],test:[{eval:"EVAL_USERCENTRICS_BUTTON_0"}]},{name:"uswitch.com",prehideSelectors:["#cookie-banner-wrapper"],detectCmp:[{exists:"#cookie-banner-wrapper"}],detectPopup:[{visible:"#cookie-banner-wrapper"}],optIn:[{click:"#cookie_banner_accept_mobile"}],optOut:[{click:"#cookie_banner_save"}]},{name:"vodafone.de",runContext:{urlPattern:"^https://www\\.vodafone\\.de/"},prehideSelectors:[".dip-consent,.dip-consent-container"],detectCmp:[{exists:".dip-consent-container"}],detectPopup:[{visible:".dip-consent-content"}],optOut:[{click:'.dip-consent-btn[tabindex="2"]'}],optIn:[{click:'.dip-consent-btn[tabindex="1"]'}]},{name:"waitrose.com",prehideSelectors:["div[aria-labelledby=CookieAlertModalHeading]","section[data-test=initial-waitrose-cookie-consent-banner]","section[data-test=cookie-consent-modal]"],detectCmp:[{exists:"section[data-test=initial-waitrose-cookie-consent-banner]"}],detectPopup:[{visible:"section[data-test=initial-waitrose-cookie-consent-banner]"}],optIn:[{click:"button[data-test=accept-all]"}],optOut:[{click:"button[data-test=manage-cookies]"},{wait:200},{eval:"EVAL_WAITROSE_0"},{click:"button[data-test=submit]"}],test:[{eval:"EVAL_WAITROSE_1"}]},{name:"wetransfer.com",detectCmp:[{exists:".welcome__cookie-notice"}],detectPopup:[{visible:".welcome__cookie-notice"}],optIn:[{click:".welcome__button--accept"}],optOut:[{click:".welcome__button--decline"}]},{name:"whitepages.com",runContext:{urlPattern:"^https://www\\.whitepages\\.com/"},cosmetic:!0,prehideSelectors:[".cookie-wrapper, .cookie-overlay"],detectCmp:[{exists:".cookie-wrapper"}],detectPopup:[{visible:".cookie-overlay"}],optIn:[{click:'button[aria-label="Got it"]'}],optOut:[{hide:[".cookie-wrapper"]}]},{name:"woo-commerce-com",prehideSelectors:[".wccom-comp-privacy-banner .wccom-privacy-banner"],detectCmp:[{exists:".wccom-comp-privacy-banner .wccom-privacy-banner"}],detectPopup:[{exists:".wccom-comp-privacy-banner .wccom-privacy-banner"}],optIn:[{click:".wccom-privacy-banner__content-buttons button.is-primary"}],optOut:[{click:".wccom-privacy-banner__content-buttons button.is-secondary"},{waitForThenClick:"input[type=checkbox][checked]:not([disabled])",all:!0},{click:"div.wccom-modal__footer > button"}]},{name:"WP Cookie Notice for GDPR",comment:"https://wordpress.org/plugins/gdpr-cookie-consent/",prehideSelectors:["#gdpr-cookie-consent-bar"],detectCmp:[{exists:"#gdpr-cookie-consent-bar"}],detectPopup:[{visible:"#gdpr-cookie-consent-bar"}],optIn:[{waitForThenClick:"#gdpr-cookie-consent-bar #cookie_action_accept"}],optOut:[{waitForThenClick:"#gdpr-cookie-consent-bar #cookie_action_reject"}],test:[{eval:"EVAL_WP_COOKIE_NOTICE_0"}]},{name:"wpcc",cosmetic:!0,prehideSelectors:[".wpcc-container"],detectCmp:[{exists:".wpcc-container"}],detectPopup:[{exists:".wpcc-container .wpcc-message"}],optIn:[{click:".wpcc-compliance .wpcc-btn"}],optOut:[{hide:[".wpcc-container"]}]},{name:"xhamster-eu",prehideSelectors:[".cookies-modal"],detectCmp:[{exists:".cookies-modal"}],detectPopup:[{exists:".cookies-modal"}],optIn:[{click:"button.cmd-button-accept-all"}],optOut:[{click:"button.cmd-button-reject-all"}]},{name:"xhamster-us",runContext:{urlPattern:"^https://(www\\.)?xhamster\\d?\\.com"},cosmetic:!0,prehideSelectors:[".cookie-announce"],detectCmp:[{exists:".cookie-announce"}],detectPopup:[{visible:".cookie-announce .announce-text"}],optIn:[{click:".cookie-announce button.xh-button"}],optOut:[{hide:[".cookie-announce"]}]},{name:"xing.com",detectCmp:[{exists:"div[class^=cookie-consent-CookieConsent]"}],detectPopup:[{exists:"div[class^=cookie-consent-CookieConsent]"}],optIn:[{click:"#consent-accept-button"}],optOut:[{click:"#consent-settings-button"},{click:".consent-banner-button-accept-overlay"}],test:[{eval:"EVAL_XING_0"}]},{name:"xnxx-com",cosmetic:!0,prehideSelectors:["#cookies-use-alert"],detectCmp:[{exists:"#cookies-use-alert"}],detectPopup:[{visible:"#cookies-use-alert"}],optIn:[{click:"#cookies-use-alert .close"}],optOut:[{hide:["#cookies-use-alert"]}]},{name:"youporn.com",cosmetic:!0,prehideSelectors:[".euCookieModal, #js_euCookieModal"],detectCmp:[{exists:".euCookieModal"}],detectPopup:[{exists:".euCookieModal, #js_euCookieModal"}],optIn:[{click:'button[name="user_acceptCookie"]'}],optOut:[{hide:[".euCookieModal"]}]},{name:"youtube-desktop",prehideSelectors:["tp-yt-iron-overlay-backdrop.opened","ytd-consent-bump-v2-lightbox"],detectCmp:[{exists:"ytd-consent-bump-v2-lightbox tp-yt-paper-dialog"},{exists:'ytd-consent-bump-v2-lightbox tp-yt-paper-dialog a[href^="https://consent.youtube.com/"]'}],detectPopup:[{visible:"ytd-consent-bump-v2-lightbox tp-yt-paper-dialog"}],optIn:[{waitForThenClick:"ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:last-child #button,ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:last-child button"},{wait:500}],optOut:[{waitForThenClick:"ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:first-child #button,ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:first-child button"},{wait:500}],test:[{wait:500},{eval:"EVAL_YOUTUBE_DESKTOP_0"}]},{name:"youtube-mobile",prehideSelectors:[".consent-bump-v2-lightbox"],detectCmp:[{exists:"ytm-consent-bump-v2-renderer"}],detectPopup:[{visible:"ytm-consent-bump-v2-renderer"}],optIn:[{waitForThenClick:"ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons c3-material-button:first-child button, ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons ytm-button-renderer:first-child button"},{wait:500}],optOut:[{waitForThenClick:"ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons c3-material-button:nth-child(2) button, ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons ytm-button-renderer:nth-child(2) button"},{wait:500}],test:[{wait:500},{eval:"EVAL_YOUTUBE_MOBILE_0"}]}],P={"didomi.io":{detectors:[{presentMatcher:{target:{selector:"#didomi-host, #didomi-notice"},type:"css"},showingMatcher:{target:{selector:"body.didomi-popup-open, .didomi-notice-banner"},type:"css"}}],methods:[{action:{target:{selector:".didomi-popup-notice-buttons .didomi-button:not(.didomi-button-highlight), .didomi-notice-banner .didomi-learn-more-button"},type:"click"},name:"OPEN_OPTIONS"},{action:{actions:[{retries:50,target:{selector:"#didomi-purpose-cookies"},type:"waitcss",waitTime:50},{consents:[{description:"Share (everything) with others",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-share_whith_others]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-share_whith_others]:last-child"},type:"click"},type:"X"},{description:"Information storage and access",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-cookies]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-cookies]:last-child"},type:"click"},type:"D"},{description:"Content selection, offers and marketing",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-CL-T1Rgm7]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-CL-T1Rgm7]:last-child"},type:"click"},type:"E"},{description:"Analytics",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-analytics]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-analytics]:last-child"},type:"click"},type:"B"},{description:"Analytics",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-M9NRHJe3G]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-M9NRHJe3G]:last-child"},type:"click"},type:"B"},{description:"Ad and content selection",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-advertising_personalization]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-advertising_personalization]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection",falseAction:{parent:{childFilter:{target:{selector:"#didomi-purpose-pub-ciblee"}},selector:".didomi-consent-popup-data-processing, .didomi-components-accordion-label-container"},target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-pub-ciblee]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-pub-ciblee]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - basics",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-q4zlJqdcD]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-q4zlJqdcD]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - partners and subsidiaries",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-partenaire-cAsDe8jC]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-partenaire-cAsDe8jC]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - social networks",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-p4em9a8m]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-p4em9a8m]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - others",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-autres-pub]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-autres-pub]:last-child"},type:"click"},type:"F"},{description:"Social networks",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-reseauxsociaux]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-reseauxsociaux]:last-child"},type:"click"},type:"A"},{description:"Social networks",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-social_media]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-social_media]:last-child"},type:"click"},type:"A"},{description:"Content selection",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-content_personalization]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-content_personalization]:last-child"},type:"click"},type:"E"},{description:"Ad delivery",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-ad_delivery]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-ad_delivery]:last-child"},type:"click"},type:"F"}],type:"consent"},{action:{consents:[{matcher:{childFilter:{target:{selector:":not(.didomi-components-radio__option--selected)"}},type:"css"},trueAction:{target:{selector:":nth-child(2)"},type:"click"},falseAction:{target:{selector:":first-child"},type:"click"},type:"X"}],type:"consent"},target:{selector:".didomi-components-radio"},type:"foreach"}],type:"list"},name:"DO_CONSENT"},{action:{parent:{selector:".didomi-consent-popup-footer .didomi-consent-popup-actions"},target:{selector:".didomi-components-button:first-child"},type:"click"},name:"SAVE_CONSENT"}]},oil:{detectors:[{presentMatcher:{target:{selector:".as-oil-content-overlay"},type:"css"},showingMatcher:{target:{selector:".as-oil-content-overlay"},type:"css"}}],methods:[{action:{actions:[{target:{selector:".as-js-advanced-settings"},type:"click"},{retries:"10",target:{selector:".as-oil-cpc__purpose-container"},type:"waitcss",waitTime:"250"}],type:"list"},name:"OPEN_OPTIONS"},{action:{actions:[{consents:[{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Information storage and access","Opbevaring af og adgang til oplysninger på din enhed"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Information storage and access","Opbevaring af og adgang til oplysninger på din enhed"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"D"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personlige annoncer","Personalisation"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personlige annoncer","Personalisation"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"E"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Annoncevalg, levering og rapportering","Ad selection, delivery, reporting"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Annoncevalg, levering og rapportering","Ad selection, delivery, reporting"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"F"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personalisering af indhold","Content selection, delivery, reporting"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personalisering af indhold","Content selection, delivery, reporting"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"E"},{matcher:{parent:{childFilter:{target:{selector:".as-oil-cpc__purpose-header",textFilter:["Måling","Measurement"]}},selector:".as-oil-cpc__purpose-container"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{childFilter:{target:{selector:".as-oil-cpc__purpose-header",textFilter:["Måling","Measurement"]}},selector:".as-oil-cpc__purpose-container"},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"B"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:"Google"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:"Google"},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"F"}],type:"consent"}],type:"list"},name:"DO_CONSENT"},{action:{target:{selector:".as-oil__btn-optin"},type:"click"},name:"SAVE_CONSENT"},{action:{target:{selector:"div.as-oil"},type:"hide"},name:"HIDE_CMP"}]},optanon:{detectors:[{presentMatcher:{target:{selector:"#optanon-menu, .optanon-alert-box-wrapper"},type:"css"},showingMatcher:{target:{displayFilter:!0,selector:".optanon-alert-box-wrapper"},type:"css"}}],methods:[{action:{actions:[{target:{selector:".optanon-alert-box-wrapper .optanon-toggle-display, a[onclick*='OneTrust.ToggleInfoDisplay()'], a[onclick*='Optanon.ToggleInfoDisplay()']"},type:"click"}],type:"list"},name:"OPEN_OPTIONS"},{action:{actions:[{target:{selector:".preference-menu-item #Your-privacy"},type:"click"},{target:{selector:"#optanon-vendor-consent-text"},type:"click"},{action:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"X"}],type:"consent"},target:{selector:"#optanon-vendor-consent-list .vendor-item"},type:"foreach"},{target:{selector:".vendor-consent-back-link"},type:"click"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-performance"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-performance"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-functional"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-functional"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-advertising"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-advertising"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-social"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-social"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Social Media Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Social Media Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalisation"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalisation"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Site monitoring cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Site monitoring cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Third party privacy-enhanced content"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Third party privacy-enhanced content"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"X"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Performance & Advertising Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Performance & Advertising Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Information storage and access"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Information storage and access"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"D"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad selection, delivery, reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad selection, delivery, reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content selection, delivery, reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content selection, delivery, reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Measurement"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Measurement"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Recommended Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Recommended Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"X"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Unclassified Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Unclassified Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"X"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Analytical Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Analytical Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Marketing Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Marketing Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalization"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalization"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad Selection, Delivery & Reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad Selection, Delivery & Reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content Selection, Delivery & Reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content Selection, Delivery & Reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"}],type:"list"},name:"DO_CONSENT"},{action:{parent:{selector:".optanon-save-settings-button"},target:{selector:".optanon-white-button-middle"},type:"click"},name:"SAVE_CONSENT"},{action:{actions:[{target:{selector:"#optanon-popup-wrapper"},type:"hide"},{target:{selector:"#optanon-popup-bg"},type:"hide"},{target:{selector:".optanon-alert-box-wrapper"},type:"hide"}],type:"list"},name:"HIDE_CMP"}]},quantcast2:{detectors:[{presentMatcher:{target:{selector:"[data-tracking-opt-in-overlay]"},type:"css"},showingMatcher:{target:{selector:"[data-tracking-opt-in-overlay] [data-tracking-opt-in-learn-more]"},type:"css"}}],methods:[{action:{target:{selector:"[data-tracking-opt-in-overlay] [data-tracking-opt-in-learn-more]"},type:"click"},name:"OPEN_OPTIONS"},{action:{actions:[{type:"wait",waitTime:500},{action:{actions:[{target:{selector:"div",textFilter:["Information storage and access"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"D"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Personalization"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"F"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Ad selection, delivery, reporting"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"F"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Content selection, delivery, reporting"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"E"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Measurement"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"B"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Other Partners"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"X"}],type:"consent"},type:"ifcss"}],type:"list"},parent:{childFilter:{target:{selector:"input"}},selector:"[data-tracking-opt-in-overlay] > div > div"},target:{childFilter:{target:{selector:"input"}},selector:":scope > div"},type:"foreach"}],type:"list"},name:"DO_CONSENT"},{action:{target:{selector:"[data-tracking-opt-in-overlay] [data-tracking-opt-in-save]"},type:"click"},name:"SAVE_CONSENT"}]},springer:{detectors:[{presentMatcher:{parent:null,target:{selector:".cmp-app_gdpr"},type:"css"},showingMatcher:{parent:null,target:{displayFilter:!0,selector:".cmp-popup_popup"},type:"css"}}],methods:[{action:{actions:[{target:{selector:".cmp-intro_rejectAll"},type:"click"},{type:"wait",waitTime:250},{target:{selector:".cmp-purposes_purposeItem:not(.cmp-purposes_selectedPurpose)"},type:"click"}],type:"list"},name:"OPEN_OPTIONS"},{action:{consents:[{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Przechowywanie informacji na urządzeniu lub dostęp do nich",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Przechowywanie informacji na urządzeniu lub dostęp do nich",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"D"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór podstawowych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór podstawowych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"F"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"F"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"E"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"E"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"B"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"B"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"B"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Stosowanie badań rynkowych w celu generowania opinii odbiorców",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Stosowanie badań rynkowych w celu generowania opinii odbiorców",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"X"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Opracowywanie i ulepszanie produktów",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Opracowywanie i ulepszanie produktów",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"X"}],type:"consent"},name:"DO_CONSENT"},{action:{target:{selector:".cmp-details_save"},type:"click"},name:"SAVE_CONSENT"}]},wordpressgdpr:{detectors:[{presentMatcher:{parent:null,target:{selector:".wpgdprc-consent-bar"},type:"css"},showingMatcher:{parent:null,target:{displayFilter:!0,selector:".wpgdprc-consent-bar"},type:"css"}}],methods:[{action:{parent:null,target:{selector:".wpgdprc-consent-bar .wpgdprc-consent-bar__settings",textFilter:null},type:"click"},name:"OPEN_OPTIONS"},{action:{actions:[{target:{selector:".wpgdprc-consent-modal .wpgdprc-button",textFilter:"Eyeota"},type:"click"},{consents:[{description:"Eyeota Cookies",matcher:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Eyeota"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Eyeota"},target:{selector:"label"},type:"click"},type:"X"}],type:"consent"},{target:{selector:".wpgdprc-consent-modal .wpgdprc-button",textFilter:"Advertising"},type:"click"},{consents:[{description:"Advertising Cookies",matcher:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Advertising"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Advertising"},target:{selector:"label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},name:"DO_CONSENT"},{action:{parent:null,target:{selector:".wpgdprc-button",textFilter:"Save my settings"},type:"click"},name:"SAVE_CONSENT"}]}},T={autoconsent:I,consentomatic:P},F=Object.freeze({__proto__:null,autoconsent:I,consentomatic:P,default:T});const L=new class{constructor(e,t=null,o=null){if(this.id=g(),this.rules=[],this.foundCmp=null,this.state={lifecycle:"loading",prehideOn:!1,findCmpAttempts:0,detectedCmps:[],detectedPopups:[],selfTest:null},w.sendContentMessage=e,this.sendContentMessage=e,this.rules=[],this.updateState({lifecycle:"loading"}),this.addDynamicRules(),t)this.initialize(t,o);else{o&&this.parseDeclarativeRules(o);e({type:"init",url:window.location.href}),this.updateState({lifecycle:"waitingForInitResponse"})}}initialize(e,t){if(this.config=e,e.enabled){if(t&&this.parseDeclarativeRules(t),this.rules=function(e,t){return e.filter((e=>(!t.disabledCmps||!t.disabledCmps.includes(e.name))&&(t.enableCosmeticRules||!e.isCosmetic)))}(this.rules,e),e.enablePrehide)if(document.documentElement)this.prehideElements();else{const e=()=>{window.removeEventListener("DOMContentLoaded",e),this.prehideElements()};window.addEventListener("DOMContentLoaded",e)}if("loading"===document.readyState){const e=()=>{window.removeEventListener("DOMContentLoaded",e),this.start()};window.addEventListener("DOMContentLoaded",e)}else this.start();this.updateState({lifecycle:"initialized"})}}addDynamicRules(){S.forEach((e=>{this.rules.push(new e(this))}))}parseDeclarativeRules(e){Object.keys(e.consentomatic).forEach((t=>{this.addConsentomaticCMP(t,e.consentomatic[t])})),e.autoconsent.forEach((e=>{this.addDeclarativeCMP(e)}))}addDeclarativeCMP(e){this.rules.push(new A(e,this))}addConsentomaticCMP(e,t){this.rules.push(new E(`com_${e}`,t))}start(){window.requestIdleCallback?window.requestIdleCallback((()=>this._start()),{timeout:500}):this._start()}async _start(){this.updateState({lifecycle:"started"});const e=await this.findCmp(this.config.detectRetries);if(this.updateState({detectedCmps:e.map((e=>e.name))}),0===e.length)return this.config.enablePrehide&&this.undoPrehide(),this.updateState({lifecycle:"nothingDetected"}),!1;this.updateState({lifecycle:"cmpDetected"});let t=await this.detectPopups(e.filter((e=>!e.isCosmetic)));if(0===t.length&&(t=await this.detectPopups(e.filter((e=>e.isCosmetic)))),0===t.length)return this.config.enablePrehide&&this.undoPrehide(),!1;if(this.updateState({lifecycle:"openPopupDetected"}),t.length>1){const e={msg:"Found multiple CMPs, check the detection rules.",cmps:t.map((e=>e.name))};this.sendContentMessage({type:"autoconsentError",details:e})}return this.foundCmp=t[0],"optOut"===this.config.autoAction?await this.doOptOut():"optIn"!==this.config.autoAction||await this.doOptIn()}async findCmp(e){this.updateState({findCmpAttempts:this.state.findCmpAttempts+1});const t=[];for(const e of this.rules)try{if(!e.checkRunContext())continue;await e.detectCmp()&&(this.sendContentMessage({type:"cmpDetected",url:location.href,cmp:e.name}),t.push(e))}catch(e){}return 0===t.length&&e>0?(await k(500),this.findCmp(e-1)):t}async detectPopups(e){const t=[],o=e.map((e=>this.waitForPopup(e).then((o=>{o&&(this.updateState({detectedPopups:this.state.detectedPopups.concat([e.name])}),this.sendContentMessage({type:"popupFound",cmp:e.name,url:location.href}),t.push(e))})).catch((e=>null))));return await Promise.all(o),t}async doOptOut(){let e;return this.updateState({lifecycle:"runningOptOut"}),e=!!this.foundCmp&&await this.foundCmp.optOut(),this.config.enablePrehide&&this.undoPrehide(),this.sendContentMessage({type:"optOutResult",cmp:this.foundCmp?this.foundCmp.name:"none",result:e,scheduleSelfTest:this.foundCmp&&this.foundCmp.hasSelfTest,url:location.href}),e&&!this.foundCmp.isIntermediate?(this.sendContentMessage({type:"autoconsentDone",cmp:this.foundCmp.name,isCosmetic:this.foundCmp.isCosmetic,url:location.href}),this.updateState({lifecycle:"done"})):this.updateState({lifecycle:e?"optOutSucceeded":"optOutFailed"}),e}async doOptIn(){let e;return this.updateState({lifecycle:"runningOptIn"}),e=!!this.foundCmp&&await this.foundCmp.optIn(),this.config.enablePrehide&&this.undoPrehide(),this.sendContentMessage({type:"optInResult",cmp:this.foundCmp?this.foundCmp.name:"none",result:e,scheduleSelfTest:!1,url:location.href}),e&&!this.foundCmp.isIntermediate?(this.sendContentMessage({type:"autoconsentDone",cmp:this.foundCmp.name,isCosmetic:this.foundCmp.isCosmetic,url:location.href}),this.updateState({lifecycle:"done"})):this.updateState({lifecycle:e?"optInSucceeded":"optInFailed"}),e}async doSelfTest(){let e;return e=!!this.foundCmp&&await this.foundCmp.test(),this.sendContentMessage({type:"selfTestResult",cmp:this.foundCmp?this.foundCmp.name:"none",result:e,url:location.href}),this.updateState({selfTest:e}),e}async waitForPopup(e,t=5,o=500){const c=await e.detectPopup().catch((e=>!1));return!c&&t>0?(await k(o),this.waitForPopup(e,t-1,o)):c}prehideElements(){const e=this.rules.reduce(((e,t)=>t.prehideSelectors?[...e,...t.prehideSelectors]:e),["#didomi-popup,.didomi-popup-container,.didomi-popup-notice,.didomi-consent-popup-preferences,#didomi-notice,.didomi-popup-backdrop,.didomi-screen-medium"]);return this.updateState({prehideOn:!0}),setTimeout((()=>{this.config.enablePrehide&&this.state.prehideOn&&!["runningOptOut","runningOptIn"].includes(this.state.lifecycle)&&this.undoPrehide()}),this.config.prehideTimeout||2e3),function(e){return s(a("autoconsent-prehide"),e,"opacity")}(e)}undoPrehide(){return this.updateState({prehideOn:!1}),function(){const e=a("autoconsent-prehide");return e&&e.remove(),!!e}()}updateState(e){Object.assign(this.state,e),this.sendContentMessage({type:"report",instanceId:this.id,url:window.location.href,mainFrame:window.top===window.self,state:this.state})}async receiveMessageCallback(e){switch(e.type){case"initResp":this.initialize(e.config,e.rules);break;case"optIn":await this.doOptIn();break;case"optOut":await this.doOptOut();break;case"selfTest":await this.doSelfTest();break;case"evalResp":!function(e,t){const o=w.pending.get(e);o?(w.pending.delete(e),o.timer&&window.clearTimeout(o.timer),o.resolve(t)):console.warn("no eval #",e)}(e.id,e.result)}}}((e=>{window.webkit.messageHandlers[e.type]&&window.webkit.messageHandlers[e.type].postMessage(e).then((e=>{L.receiveMessageCallback(e)}))}),null,F);window.autoconsentMessageCallback=e=>{L.receiveMessageCallback(e)}}(); +!function(){"use strict";var e=class e{static setBase(t){e.base=t}static findElement(t,o=null,c=!1){let i=null;return i=null!=o?Array.from(o.querySelectorAll(t.selector)):null!=e.base?Array.from(e.base.querySelectorAll(t.selector)):Array.from(document.querySelectorAll(t.selector)),null!=t.textFilter&&(i=i.filter((e=>{const o=e.textContent.toLowerCase();if(Array.isArray(t.textFilter)){let e=!1;for(const c of t.textFilter)if(-1!==o.indexOf(c.toLowerCase())){e=!0;break}return e}if(null!=t.textFilter)return-1!==o.indexOf(t.textFilter.toLowerCase())}))),null!=t.styleFilters&&(i=i.filter((e=>{const o=window.getComputedStyle(e);let c=!0;for(const e of t.styleFilters){const t=o[e.option];c=e.negated?c&&t!==e.value:c&&t===e.value}return c}))),null!=t.displayFilter&&(i=i.filter((e=>t.displayFilter?0!==e.offsetHeight:0===e.offsetHeight))),null!=t.iframeFilter&&(i=i.filter((()=>t.iframeFilter?window.location!==window.parent.location:window.location===window.parent.location))),null!=t.childFilter&&(i=i.filter((o=>{const c=e.base;e.setBase(o);const i=e.find(t.childFilter);return e.setBase(c),null!=i.target}))),c?i:(i.length>1&&console.warn("Multiple possible targets: ",i,t,o),i[0])}static find(t,o=!1){const c=[];if(null!=t.parent){const i=e.findElement(t.parent,null,o);if(null!=i){if(i instanceof Array)return i.forEach((i=>{const n=e.findElement(t.target,i,o);n instanceof Array?n.forEach((e=>{c.push({parent:i,target:e})})):c.push({parent:i,target:n})})),c;{const n=e.findElement(t.target,i,o);n instanceof Array?n.forEach((e=>{c.push({parent:i,target:e})})):c.push({parent:i,target:n})}}}else{const i=e.findElement(t.target,null,o);i instanceof Array?i.forEach((e=>{c.push({parent:null,target:e})})):c.push({parent:null,target:i})}return 0===c.length&&c.push({parent:null,target:null}),o?c:(1!==c.length&&console.warn("Multiple results found, even though multiple false",c),c[0])}};e.base=null;var t=e;function o(e){const o=t.find(e);return"css"===e.type?!!o.target:"checkbox"===e.type?!!o.target&&o.target.checked:void 0}async function c(e,a){switch(e.type){case"click":return async function(e){const o=t.find(e);null!=o.target&&o.target.click();return n(i)}(e);case"list":return async function(e,t){for(const o of e.actions)await c(o,t)}(e,a);case"consent":return async function(e,t){for(const i of e.consents){const e=-1!==t.indexOf(i.type);if(i.matcher&&i.toggleAction){o(i.matcher)!==e&&await c(i.toggleAction)}else e?await c(i.trueAction):await c(i.falseAction)}}(e,a);case"ifcss":return async function(e,o){const i=t.find(e);i.target?e.falseAction&&await c(e.falseAction,o):e.trueAction&&await c(e.trueAction,o)}(e,a);case"waitcss":return async function(e){await new Promise((o=>{let c=e.retries||10;const i=e.waitTime||250,n=()=>{const a=t.find(e);(e.negated&&a.target||!e.negated&&!a.target)&&c>0?(c-=1,setTimeout(n,i)):o()};n()}))}(e);case"foreach":return async function(e,o){const i=t.find(e,!0),n=t.base;for(const n of i)n.target&&(t.setBase(n.target),await c(e.action,o));t.setBase(n)}(e,a);case"hide":return async function(e){const o=t.find(e);o.target&&o.target.classList.add("Autoconsent-Hidden")}(e);case"slide":return async function(e){const o=t.find(e),c=t.find(e.dragTarget);if(o.target){const e=o.target.getBoundingClientRect(),t=c.target.getBoundingClientRect();let i=t.top-e.top,n=t.left-e.left;"y"===this.config.axis.toLowerCase()&&(n=0),"x"===this.config.axis.toLowerCase()&&(i=0);const a=window.screenX+e.left+e.width/2,s=window.screenY+e.top+e.height/2,r=e.left+e.width/2,l=e.top+e.height/2,p=document.createEvent("MouseEvents");p.initMouseEvent("mousedown",!0,!0,window,0,a,s,r,l,!1,!1,!1,!1,0,o.target);const d=document.createEvent("MouseEvents");d.initMouseEvent("mousemove",!0,!0,window,0,a+n,s+i,r+n,l+i,!1,!1,!1,!1,0,o.target);const u=document.createEvent("MouseEvents");u.initMouseEvent("mouseup",!0,!0,window,0,a+n,s+i,r+n,l+i,!1,!1,!1,!1,0,o.target),o.target.dispatchEvent(p),await this.waitTimeout(10),o.target.dispatchEvent(d),await this.waitTimeout(10),o.target.dispatchEvent(u)}}(e);case"close":return async function(){window.close()}();case"wait":return async function(e){await n(e.waitTime)}(e);case"eval":return async function(e){return console.log("eval!",e.code),new Promise((t=>{try{e.async?(window.eval(e.code),setTimeout((()=>{t(window.eval("window.__consentCheckResult"))}),e.timeout||250)):t(window.eval(e.code))}catch(o){console.warn("eval error",o,e.code),t(!1)}}))}(e);default:throw"Unknown action type: "+e.type}}var i=0;function n(e){return new Promise((t=>{setTimeout((()=>{t()}),e)}))}function a(e="autoconsent-css-rules"){const t=`style#${e}`,o=document.querySelector(t);if(o&&o instanceof HTMLStyleElement)return o;{const t=document.head||document.getElementsByTagName("head")[0]||document.documentElement,o=document.createElement("style");return o.id=e,t.appendChild(o),o}}function s(e,t,o="display"){const c="opacity"===o?"opacity: 0":"display: none",i=`${t.join(",")} { ${c} !important; z-index: -1 !important; pointer-events: none !important; } `;return e instanceof HTMLStyleElement&&(e.innerText+=i,t.length>0)}async function r(e,t,o){const c=await e();return!c&&t>0?new Promise((c=>{setTimeout((async()=>{c(r(e,t-1,o))}),o)})):Promise.resolve(c)}function l(e){if(!e)return!1;if(null!==e.offsetParent)return!0;{const t=window.getComputedStyle(e);if("fixed"===t.position&&"none"!==t.display)return!0}return!1}function p(e,t=!1){const o=g(e);return o.length>0&&(t?o.forEach((e=>e.click())):o[0].click()),o.length>0}function d(e){return g(e).length>0}function u(e,t){const o=g(e),c=new Array(o.length);return o.forEach(((e,t)=>{c[t]=l(e)})),"none"===t?c.every((e=>!e)):0!==c.length&&("any"===t?c.some((e=>e)):c.every((e=>e)))}function m(e,t=1e4){return r((()=>g(e).length>0),Math.ceil(t/200),200)}function h(e,t=1e4,o="any"){return r((()=>u(e,o)),Math.ceil(t/200),200)}async function k(e,t=1e4,o=!1){return await m(e,t),p(e,o)}function b(e){return new Promise((t=>{setTimeout((()=>{t(!0)}),e)}))}function _(e,t=document){if(e.startsWith("aria/"))return[];if(e.startsWith("xpath/")){const o=e.slice(6),c=document.evaluate(o,t,null,XPathResult.ANY_TYPE,null);let i=null;const n=[];for(;i=c.iterateNext();)n.push(i);return n}return e.startsWith("text/")||e.startsWith("pierce/")?[]:t.shadowRoot?Array.from(t.shadowRoot.querySelectorAll(e)):Array.from(t.querySelectorAll(e))}function g(e){return"string"==typeof e?_(e):function(e){let t,o=document;for(const c of e){if(t=_(c,o),0===t.length)return[];o=t[0]}return t}(e)}function y(){return crypto&&void 0!==crypto.randomUUID?crypto.randomUUID():Math.random().toString()}var w=class{constructor(e,t=1e3){this.id=e,this.promise=new Promise(((e,t)=>{this.resolve=e,this.reject=t})),this.timer=window.setTimeout((()=>{this.reject(new Error("timeout"))}),t)}},C={pending:new Map,sendContentMessage:null};var v={EVAL_0:()=>console.log(1),EVAL_CONSENTMANAGER_1:()=>window.__cmp&&"object"==typeof __cmp("getCMPData"),EVAL_CONSENTMANAGER_2:()=>!__cmp("consentStatus").userChoiceExists,EVAL_CONSENTMANAGER_3:()=>__cmp("setConsent",0),EVAL_CONSENTMANAGER_4:()=>__cmp("setConsent",1),EVAL_CONSENTMANAGER_5:()=>__cmp("consentStatus").userChoiceExists,EVAL_COOKIEBOT_1:()=>!0!==window.CookieConsent.hasResponse,EVAL_COOKIEBOT_2:()=>window.Cookiebot.dialog.submitConsent(),EVAL_COOKIEBOT_3:()=>endCookieProcess(),EVAL_COOKIEBOT_4:()=>!0===window.CookieConsent.declined,EVAL_KLARO_1:()=>klaro.getManager().config.services.every((e=>e.required||!klaro.getManager().consents[e.name])),EVAL_ONETRUST_1:()=>window.OnetrustActiveGroups.split(",").filter((e=>e.length>0)).length<=1,EVAL_TRUSTARC_TOP:()=>window&&window.truste&&"0"===window.truste.eu.bindMap.prefCookie,EVAL_ADROLL_0:()=>!document.cookie.includes("__adroll_fpc"),EVAL_ALMACMP_0:()=>document.cookie.includes('"name":"Google","consent":false'),EVAL_AFFINITY_SERIF_COM_0:()=>document.cookie.includes("serif_manage_cookies_viewed")&&!document.cookie.includes("serif_allow_analytics"),EVAL_AXEPTIO_0:()=>document.cookie.includes("axeptio_authorized_vendors=%2C%2C"),EVAL_BING_0:()=>document.cookie.includes("AL=0")&&document.cookie.includes("AD=0")&&document.cookie.includes("SM=0"),EVAL_BORLABS_0:()=>!JSON.parse(decodeURIComponent(document.cookie.split(";").find((e=>-1!==e.indexOf("borlabs-cookie"))).split("=",2)[1])).consents.statistics,EVAL_BUNDESREGIERUNG_DE_0:()=>document.cookie.match("cookie-allow-tracking=0"),EVAL_CANVA_0:()=>!document.cookie.includes("gtm_fpc_engagement_event"),EVAL_CC_BANNER2_0:()=>!!document.cookie.match(/sncc=[^;]+D%3Dtrue/),EVAL_CLICKIO_0:()=>document.cookie.includes("__lxG__consent__v2_daisybit="),EVAL_CLINCH_0:()=>document.cookie.includes("ctc_rejected=1"),EVAL_COINBASE_0:()=>JSON.parse(decodeURIComponent(document.cookie.match(/cm_(eu|default)_preferences=([0-9a-zA-Z\\{\\}\\[\\]%:]*);?/)[2])).consent.length<=1,EVAL_COMPLIANZ_BANNER_0:()=>document.cookie.includes("cmplz_banner-status=dismissed"),EVAL_COMPLIANZ_CATEGORIES_0:()=>!!document.cookie.match(/cmplz_[^=]+=deny/),EVAL_COMPLIANZ_OPTIN_0:()=>!!document.cookie.match(/cookieconsent_preferences_disabled=[^;]+/),EVAL_COOKIE_LAW_INFO_0:()=>CLI.disableAllCookies()||CLI.reject_close()||!0,EVAL_COOKIE_LAW_INFO_1:()=>-1===document.cookie.indexOf("cookielawinfo-checkbox-non-necessary=yes"),EVAL_COOKIE_MANAGER_POPUP_0:()=>!1===JSON.parse(document.cookie.split(";").find((e=>e.trim().startsWith("CookieLevel"))).split("=")[1]).social,EVAL_COOKIEALERT_0:()=>document.querySelector("body").removeAttribute("style")||!0,EVAL_COOKIEALERT_1:()=>document.querySelector("body").removeAttribute("style")||!0,EVAL_COOKIEALERT_2:()=>!0===window.CookieConsent.declined,EVAL_COOKIEFIRST_0:()=>{return!1===(e=JSON.parse(decodeURIComponent(document.cookie.split(";").find((e=>-1!==e.indexOf("cookiefirst"))).trim()).split("=")[1])).performance&&!1===e.functional&&!1===e.advertising;var e},EVAL_COOKIEFIRST_1:()=>document.querySelectorAll("button[data-cookiefirst-accent-color=true][role=checkbox]:not([disabled])").forEach((e=>"true"==e.getAttribute("aria-checked")&&e.click()))||!0,EVAL_COOKIEINFORMATION_0:()=>CookieInformation.declineAllCategories()||!0,EVAL_COOKIEINFORMATION_1:()=>CookieInformation.submitAllCategories()||!0,EVAL_COOKIEINFORMATION_2:()=>document.cookie.includes("CookieInformationConsent="),EVAL_DAILYMOTION_0:()=>!!document.cookie.match("dm-euconsent-v2"),EVAL_DSGVO_0:()=>!document.cookie.includes("sp_dsgvo_cookie_settings"),EVAL_DUNELM_0:()=>document.cookie.includes("cc_functional=0")&&document.cookie.includes("cc_targeting=0"),EVAL_ETSY_0:()=>document.querySelectorAll(".gdpr-overlay-body input").forEach((e=>{e.checked=!1}))||!0,EVAL_ETSY_1:()=>document.querySelector(".gdpr-overlay-view button[data-wt-overlay-close]").click()||!0,EVAL_EU_COOKIE_COMPLIANCE_0:()=>-1===document.cookie.indexOf("cookie-agreed=2"),EVAL_EU_COOKIE_LAW_0:()=>!document.cookie.includes("euCookie"),EVAL_EZOIC_0:()=>ezCMP.handleAcceptAllClick(),EVAL_EZOIC_1:()=>!!document.cookie.match(/ezCMPCookieConsent=[^;]+\|2=0\|3=0\|4=0/),EVAL_GOOGLE_0:()=>!!document.cookie.match(/SOCS=CAE/),EVAL_IUBENDA_0:()=>document.querySelectorAll(".purposes-item input[type=checkbox]:not([disabled])").forEach((e=>{e.checked&&e.click()}))||!0,EVAL_IUBENDA_1:()=>!!document.cookie.match(/_iub_cs-\d+=/),EVAL_JQUERY_COOKIEBAR_0:()=>!document.cookie.includes("cookies-state=accepted"),EVAL_MEDIAVINE_0:()=>document.querySelectorAll('[data-name="mediavine-gdpr-cmp"] input[type=checkbox]').forEach((e=>e.checked&&e.click()))||!0,EVAL_MICROSOFT_0:()=>Array.from(document.querySelectorAll("div > button")).filter((e=>e.innerText.match("Reject|Ablehnen")))[0].click()||!0,EVAL_MICROSOFT_1:()=>Array.from(document.querySelectorAll("div > button")).filter((e=>e.innerText.match("Accept|Annehmen")))[0].click()||!0,EVAL_MICROSOFT_2:()=>!!document.cookie.match("MSCC"),EVAL_MOOVE_0:()=>document.querySelectorAll("#moove_gdpr_cookie_modal input").forEach((e=>{e.disabled||"moove_gdpr_strict_cookies"===e.name||(e.checked=!1)}))||!0,EVAL_ONENINETWO_0:()=>document.cookie.includes("CC_ADVERTISING=NO")&&document.cookie.includes("CC_ANALYTICS=NO"),EVAL_PAYPAL_0:()=>!0===document.cookie.includes("cookie_prefs"),EVAL_PRIMEBOX_0:()=>!document.cookie.includes("cb-enabled=accepted"),EVAL_PUBTECH_0:()=>document.cookie.includes("euconsent-v2")&&(document.cookie.match(/.YAAAAAAAAAAA/)||document.cookie.match(/.aAAAAAAAAAAA/)||document.cookie.match(/.YAAACFgAAAAA/)),EVAL_REDDIT_0:()=>document.cookie.includes("eu_cookie={%22opted%22:true%2C%22nonessential%22:false}"),EVAL_SIBBO_0:()=>!!window.localStorage.getItem("euconsent-v2"),EVAL_SIRDATA_0:()=>document.cookie.includes("euconsent-v2"),EVAL_SNIGEL_0:()=>!!document.cookie.match("snconsent"),EVAL_STEAMPOWERED_0:()=>2===JSON.parse(decodeURIComponent(document.cookie.split(";").find((e=>e.trim().startsWith("cookieSettings"))).split("=")[1])).preference_state,EVAL_TAKEALOT_0:()=>document.body.classList.remove("freeze")||(document.body.style="")||!0,EVAL_TARTEAUCITRON_0:()=>tarteaucitron.userInterface.respondAll(!1)||!0,EVAL_TARTEAUCITRON_1:()=>tarteaucitron.userInterface.respondAll(!0)||!0,EVAL_TARTEAUCITRON_2:()=>document.cookie.match(/tarteaucitron=[^;]*/)[0].includes("false"),EVAL_TEALIUM_0:()=>void 0!==window.utag&&"object"==typeof utag.gdpr,EVAL_TEALIUM_1:()=>utag.gdpr.setConsentValue(!1)||!0,EVAL_TEALIUM_2:()=>utag.gdpr.setConsentValue(!0)||!0,EVAL_TEALIUM_3:()=>1!==utag.gdpr.getConsentState(),EVAL_TESTCMP_0:()=>"button_clicked"===window.results.results[0],EVAL_TESTCMP_COSMETIC_0:()=>"banner_hidden"===window.results.results[0],EVAL_THEFREEDICTIONARY_0:()=>cmpUi.showPurposes()||cmpUi.rejectAll()||!0,EVAL_THEFREEDICTIONARY_1:()=>cmpUi.allowAll()||!0,EVAL_THEVERGE_0:()=>document.cookie.includes("_duet_gdpr_acknowledged=1"),EVAL_UBUNTU_COM_0:()=>"_cookies_accepted=essential"===document.cookie,EVAL_UK_COOKIE_CONSENT_0:()=>!document.cookie.includes("catAccCookies"),EVAL_USERCENTRICS_API_0:()=>"object"==typeof UC_UI,EVAL_USERCENTRICS_API_1:()=>!!UC_UI.closeCMP(),EVAL_USERCENTRICS_API_2:()=>!!UC_UI.denyAllConsents(),EVAL_USERCENTRICS_API_3:()=>!!UC_UI.acceptAllConsents(),EVAL_USERCENTRICS_API_4:()=>!!UC_UI.closeCMP(),EVAL_USERCENTRICS_API_5:()=>!0===UC_UI.areAllConsentsAccepted(),EVAL_USERCENTRICS_API_6:()=>!1===UC_UI.areAllConsentsAccepted(),EVAL_USERCENTRICS_BUTTON_0:()=>JSON.parse(localStorage.getItem("usercentrics")).consents.every((e=>e.isEssential||!e.consentStatus)),EVAL_WAITROSE_0:()=>Array.from(document.querySelectorAll("label[id$=cookies-deny-label]")).forEach((e=>e.click()))||!0,EVAL_WAITROSE_1:()=>document.cookie.includes("wtr_cookies_advertising=0")&&document.cookie.includes("wtr_cookies_analytics=0"),EVAL_WP_COOKIE_NOTICE_0:()=>document.cookie.includes("wpl_viewed_cookie=no"),EVAL_XING_0:()=>document.cookie.includes("userConsent=%7B%22marketing%22%3Afalse"),EVAL_YOUTUBE_DESKTOP_0:()=>!!document.cookie.match(/SOCS=CAE/),EVAL_YOUTUBE_MOBILE_0:()=>!!document.cookie.match(/SOCS=CAE/)};var f={main:!0,frame:!1,urlPattern:""},A=class{constructor(e){this.runContext=f,this.autoconsent=e}get hasSelfTest(){throw new Error("Not Implemented")}get isIntermediate(){throw new Error("Not Implemented")}get isCosmetic(){throw new Error("Not Implemented")}mainWorldEval(e){const t=v[e];if(!t)return console.warn("Snippet not found",e),Promise.resolve(!1);if(this.autoconsent.config.isMainWorld){let e=!1;try{e=!!t.call(globalThis)}catch(e){}return Promise.resolve(e)}return function(e){const t=y();C.sendContentMessage({type:"eval",id:t,code:e});const o=new w(t);return C.pending.set(o.id,o),o.promise}(function(e){const t=e.toString();return t.substring(t.indexOf("=>")+2)}(t)).catch((e=>!1))}checkRunContext(){const e={...f,...this.runContext},t=window.top===window;return!(t&&!e.main)&&(!(!t&&!e.frame)&&!(e.urlPattern&&!window.location.href.match(e.urlPattern)))}detectCmp(){throw new Error("Not Implemented")}async detectPopup(){return!1}optOut(){throw new Error("Not Implemented")}optIn(){throw new Error("Not Implemented")}openCmp(){throw new Error("Not Implemented")}async test(){return Promise.resolve(!0)}},E=class extends A{constructor(e,t){super(t),this.config=e,this.name=e.name,this.runContext=e.runContext||f}get hasSelfTest(){return!!this.config.test}get isIntermediate(){return!!this.config.intermediate}get isCosmetic(){return!!this.config.cosmetic}get prehideSelectors(){return this.config.prehideSelectors}async detectCmp(){return!!this.config.detectCmp&&this._runRulesParallel(this.config.detectCmp)}async detectPopup(){return!!this.config.detectPopup&&this._runRulesSequentially(this.config.detectPopup)}async optOut(){return!!this.config.optOut&&this._runRulesSequentially(this.config.optOut)}async optIn(){return!!this.config.optIn&&this._runRulesSequentially(this.config.optIn)}async openCmp(){return!!this.config.openCmp&&this._runRulesSequentially(this.config.openCmp)}async test(){return this.hasSelfTest?this._runRulesSequentially(this.config.test):super.test()}async evaluateRuleStep(e){const t=[];if(e.exists&&t.push(d(e.exists)),e.visible&&t.push(u(e.visible,e.check)),e.eval){const o=this.mainWorldEval(e.eval);t.push(o)}var o,c;if(e.waitFor&&t.push(m(e.waitFor,e.timeout)),e.waitForVisible&&t.push(h(e.waitForVisible,e.timeout,e.check)),e.click&&t.push(p(e.click,e.all)),e.waitForThenClick&&t.push(k(e.waitForThenClick,e.timeout,e.all)),e.wait&&t.push(b(e.wait)),e.hide&&t.push((o=e.hide,c=e.method,s(a(),o,c))),e.if){if(!e.if.exists&&!e.if.visible)return console.error("invalid conditional rule",e.if),!1;await this.evaluateRuleStep(e.if)?t.push(this._runRulesSequentially(e.then)):e.else&&t.push(this._runRulesSequentially(e.else))}if(e.any){for(const t of e.any)if(await this.evaluateRuleStep(t))return!0;return!1}if(0===t.length)return!1;return(await Promise.all(t)).reduce(((e,t)=>e&&t),!0)}async _runRulesParallel(e){const t=e.map((e=>this.evaluateRuleStep(e)));return(await Promise.all(t)).every((e=>!!e))}async _runRulesSequentially(e){for(const t of e){if(!await this.evaluateRuleStep(t)&&!t.optional)return!1}return!0}},O=class{constructor(e,t){this.name=e,this.config=t,this.methods=new Map,this.runContext=f,this.isCosmetic=!1,t.methods.forEach((e=>{e.action&&this.methods.set(e.name,e.action)})),this.hasSelfTest=!1}get isIntermediate(){return!1}checkRunContext(){return!0}async detectCmp(){return this.config.detectors.map((e=>o(e.presentMatcher))).some((e=>!!e))}async detectPopup(){return this.config.detectors.map((e=>o(e.showingMatcher))).some((e=>!!e))}async executeAction(e,t){return!this.methods.has(e)||c(this.methods.get(e),t)}async optOut(){return await this.executeAction("HIDE_CMP"),await this.executeAction("OPEN_OPTIONS"),await this.executeAction("HIDE_CMP"),await this.executeAction("DO_CONSENT",[]),await this.executeAction("SAVE_CONSENT"),!0}async optIn(){return await this.executeAction("HIDE_CMP"),await this.executeAction("OPEN_OPTIONS"),await this.executeAction("HIDE_CMP"),await this.executeAction("DO_CONSENT",["D","A","B","E","F","X"]),await this.executeAction("SAVE_CONSENT"),!0}async openCmp(){return await this.executeAction("HIDE_CMP"),await this.executeAction("OPEN_OPTIONS"),!0}async test(){return!0}},x="#truste-show-consent",S="#truste-consent-track",I=[class extends A{constructor(e){super(e),this.name="TrustArc-top",this.prehideSelectors=[".trustarc-banner-container",`.truste_popframe,.truste_overlay,.truste_box_overlay,${S}`],this.runContext={main:!0,frame:!1},this._shortcutButton=null,this._optInDone=!1}get hasSelfTest(){return!1}get isIntermediate(){return!this._optInDone&&!this._shortcutButton}get isCosmetic(){return!1}async detectCmp(){const e=d(`${x},${S}`);return e&&(this._shortcutButton=document.querySelector("#truste-consent-required")),e}async detectPopup(){return u(`#truste-consent-content,#trustarc-banner-overlay,${S}`,"all")}openFrame(){p(x)}async optOut(){return this._shortcutButton?(this._shortcutButton.click(),!0):(s(a(),[".truste_popframe",".truste_overlay",".truste_box_overlay",S]),p(x),setTimeout((()=>{a().remove()}),1e4),!0)}async optIn(){return this._optInDone=!0,p("#truste-consent-button")}async openCmp(){return!0}async test(){return await this.mainWorldEval("EVAL_TRUSTARC_TOP")}},class extends A{constructor(){super(...arguments),this.name="TrustArc-frame",this.runContext={main:!1,frame:!0,urlPattern:"^https://consent-pref\\.trustarc\\.com/\\?"}}get hasSelfTest(){return!1}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return!0}async detectPopup(){return u("#defaultpreferencemanager","any")&&u(".mainContent","any")}async navigateToSettings(){return await r((async()=>d(".shp")||u(".advance","any")||d(".switch span:first-child")),10,500),d(".shp")&&p(".shp"),await m(".prefPanel",5e3),u(".advance","any")&&p(".advance"),await r((()=>u(".switch span:first-child","any")),5,1e3)}async optOut(){return await r((()=>"complete"===document.readyState),20,100),await m(".mainContent[aria-hidden=false]",5e3),!!p(".rejectAll")||(d(".prefPanel")&&await m('.prefPanel[style="visibility: visible;"]',3e3),p("#catDetails0")?(p(".submit"),waitForThenClick("#gwt-debug-close_id",5e3),!0):p(".required")?(waitForThenClick("#gwt-debug-close_id",5e3),!0):(await this.navigateToSettings(),p(".switch span:nth-child(1):not(.active)",!0),p(".submit"),waitForThenClick("#gwt-debug-close_id",3e5),!0))}async optIn(){return p(".call")||(await this.navigateToSettings(),p(".switch span:nth-child(2)",!0),p(".submit"),m("#gwt-debug-close_id",3e5).then((()=>{p("#gwt-debug-close_id")}))),!0}},class extends A{constructor(){super(...arguments),this.name="Cybotcookiebot",this.prehideSelectors=["#CybotCookiebotDialog,#dtcookie-container,#cookiebanner,#cb-cookieoverlay"]}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return d("#CybotCookiebotDialogBodyLevelButtonPreferences")}async detectPopup(){return d("#CybotCookiebotDialog,#dtcookie-container,#cookiebanner,#cb-cookiebanner")}async optOut(){return p(".cookie-alert-extended-detail-link")?(await m(".cookie-alert-configuration",2e3),p(".cookie-alert-configuration-input:checked",!0),p(".cookie-alert-extended-button-secondary"),!0):d("#dtcookie-container")?p(".h-dtcookie-decline"):(p(".cookiebot__button--settings")||p("#CybotCookiebotDialogBodyButtonDecline")||(p(".cookiebanner__link--details"),p('.CybotCookiebotDialogBodyLevelButton:checked:enabled,input[id*="CybotCookiebotDialogBodyLevelButton"]:checked:enabled',!0),p("#CybotCookiebotDialogBodyButtonDecline"),p("input[id^=CybotCookiebotDialogBodyLevelButton]:checked",!0),d("#CybotCookiebotDialogBodyButtonAcceptSelected")?p("#CybotCookiebotDialogBodyButtonAcceptSelected"):p("#CybotCookiebotDialogBodyLevelButtonAccept,#CybotCookiebotDialogBodyButtonAccept,#CybotCookiebotDialogBodyLevelButtonLevelOptinAllowallSelection",!0),await this.mainWorldEval("EVAL_COOKIEBOT_1")&&(await this.mainWorldEval("EVAL_COOKIEBOT_2"),await b(500)),d("#cb-confirmedSettings")&&await this.mainWorldEval("EVAL_COOKIEBOT_3")),!0)}async optIn(){return d("#dtcookie-container")?p(".h-dtcookie-accept"):(p(".CybotCookiebotDialogBodyLevelButton:not(:checked):enabled",!0),p("#CybotCookiebotDialogBodyLevelButtonAccept"),p("#CybotCookiebotDialogBodyButtonAccept"),!0)}async test(){return this.mainWorldEval("EVAL_COOKIEBOT_4")}},class extends A{constructor(){super(...arguments),this.name="Sourcepoint-frame",this.prehideSelectors=["div[id^='sp_message_container_'],.message-overlay","#sp_privacy_manager_container"],this.ccpaNotice=!1,this.ccpaPopup=!1,this.runContext={main:!1,frame:!0}}get hasSelfTest(){return!1}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){const e=new URL(location.href);return e.searchParams.has("message_id")&&"ccpa-notice.sp-prod.net"===e.hostname?(this.ccpaNotice=!0,!0):"ccpa-pm.sp-prod.net"===e.hostname?(this.ccpaPopup=!0,!0):("/index.html"===e.pathname||"/privacy-manager/index.html"===e.pathname)&&(e.searchParams.has("message_id")||e.searchParams.has("requestUUID")||e.searchParams.has("consentUUID"))}async detectPopup(){return!!this.ccpaNotice||(this.ccpaPopup?await m(".priv-save-btn",2e3):(await m(".sp_choice_type_11,.sp_choice_type_12,.sp_choice_type_13,.sp_choice_type_ACCEPT_ALL",2e3),!d(".sp_choice_type_9")))}async optIn(){return await m(".sp_choice_type_11,.sp_choice_type_ACCEPT_ALL",2e3),!!p(".sp_choice_type_11")||!!p(".sp_choice_type_ACCEPT_ALL")}isManagerOpen(){return"/privacy-manager/index.html"===location.pathname}async optOut(){if(this.ccpaPopup){const e=document.querySelectorAll(".priv-purpose-container .sp-switch-arrow-block a.neutral.on .right");for(const t of e)t.click();const t=document.querySelectorAll(".priv-purpose-container .sp-switch-arrow-block a.switch-bg.on");for(const e of t)e.click();return p(".priv-save-btn")}if(!this.isManagerOpen()){if(!await m(".sp_choice_type_12,.sp_choice_type_13"))return!1;if(!d(".sp_choice_type_12"))return p(".sp_choice_type_13");p(".sp_choice_type_12"),await r((()=>this.isManagerOpen()),200,100)}await m(".type-modal",2e4);try{const e=".sp_choice_type_REJECT_ALL",t=".reject-toggle",o=await Promise.race([m(e,2e3).then((e=>e?0:-1)),m(t,2e3).then((e=>e?1:-1)),m(".pm-features",2e3).then((e=>e?2:-1))]);if(0===o)return await b(1e3),p(e);1===o?p(t):2===o&&(await m(".pm-features",1e4),p(".checked > span",!0),p(".chevron"))}catch(e){}return p(".sp_choice_type_SAVE_AND_EXIT")}},class extends A{constructor(){super(...arguments),this.name="consentmanager.net",this.prehideSelectors=["#cmpbox,#cmpbox2"],this.apiAvailable=!1}get hasSelfTest(){return this.apiAvailable}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return this.apiAvailable=await this.mainWorldEval("EVAL_CONSENTMANAGER_1"),!!this.apiAvailable||d("#cmpbox")}async detectPopup(){return this.apiAvailable?(await b(500),await this.mainWorldEval("EVAL_CONSENTMANAGER_2")):u("#cmpbox .cmpmore","any")}async optOut(){return await b(500),this.apiAvailable?await this.mainWorldEval("EVAL_CONSENTMANAGER_3"):!!p(".cmpboxbtnno")||(d(".cmpwelcomeprpsbtn")?(p(".cmpwelcomeprpsbtn > a[aria-checked=true]",!0),p(".cmpboxbtnsave"),!0):(p(".cmpboxbtncustom"),await m(".cmptblbox",2e3),p(".cmptdchoice > a[aria-checked=true]",!0),p(".cmpboxbtnyescustomchoices"),!0))}async optIn(){return this.apiAvailable?await this.mainWorldEval("EVAL_CONSENTMANAGER_4"):p(".cmpboxbtnyes")}async test(){if(this.apiAvailable)return await this.mainWorldEval("EVAL_CONSENTMANAGER_5")}},class extends A{constructor(){super(...arguments),this.name="Evidon"}get hasSelfTest(){return!1}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return d("#_evidon_banner")}async detectPopup(){return u("#_evidon_banner","any")}async optOut(){return p("#_evidon-decline-button")||(s(a(),["#evidon-prefdiag-overlay","#evidon-prefdiag-background"]),p("#_evidon-option-button"),await m("#evidon-prefdiag-overlay",5e3),p("#evidon-prefdiag-decline")),!0}async optIn(){return p("#_evidon-accept-button")}},class extends A{constructor(){super(...arguments),this.name="Onetrust",this.prehideSelectors=["#onetrust-banner-sdk,#onetrust-consent-sdk,.onetrust-pc-dark-filter,.js-consent-banner"],this.runContext={urlPattern:"^(?!.*https://www\\.nba\\.com/)"}}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return d("#onetrust-banner-sdk")}async detectPopup(){return u("#onetrust-banner-sdk","all")}async optOut(){return u("#onetrust-reject-all-handler,.js-reject-cookies","any")?p("#onetrust-reject-all-handler,.js-reject-cookies"):(d("#onetrust-pc-btn-handler")?p("#onetrust-pc-btn-handler"):p(".ot-sdk-show-settings,button.js-cookie-settings"),await m("#onetrust-consent-sdk",2e3),await b(1e3),p("#onetrust-consent-sdk input.category-switch-handler:checked,.js-editor-toggle-state:checked",!0),await b(1e3),await m(".save-preference-btn-handler,.js-consent-save",2e3),p(".save-preference-btn-handler,.js-consent-save"),await h("#onetrust-banner-sdk",5e3,"none"),!0)}async optIn(){return p("#onetrust-accept-btn-handler,.js-accept-cookies")}async test(){return await r((()=>this.mainWorldEval("EVAL_ONETRUST_1")),10,500)}},class extends A{constructor(){super(...arguments),this.name="Klaro",this.prehideSelectors=[".klaro"],this.settingsOpen=!1}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return d(".klaro > .cookie-modal")?(this.settingsOpen=!0,!0):d(".klaro > .cookie-notice")}async detectPopup(){return u(".klaro > .cookie-notice,.klaro > .cookie-modal","any")}async optOut(){return!!p(".klaro .cn-decline")||(this.settingsOpen||(p(".klaro .cn-learn-more"),await m(".klaro > .cookie-modal",2e3),this.settingsOpen=!0),!!p(".klaro .cn-decline")||(p(".cm-purpose:not(.cm-toggle-all) > input:not(.half-checked)",!0),p(".cm-btn-accept")))}async optIn(){return!!p(".klaro .cm-btn-accept-all")||(this.settingsOpen?(p(".cm-purpose:not(.cm-toggle-all) > input.half-checked",!0),p(".cm-btn-accept")):p(".klaro .cookie-notice .cm-btn-success"))}async test(){return await this.mainWorldEval("EVAL_KLARO_1")}},class extends A{constructor(){super(...arguments),this.name="Uniconsent"}get prehideSelectors(){return[".unic",".modal:has(.unic)"]}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return d(".unic .unic-box,.unic .unic-bar")}async detectPopup(){return u(".unic .unic-box,.unic .unic-bar","any")}async optOut(){if(await m(".unic button",1e3),document.querySelectorAll(".unic button").forEach((e=>{const t=e.textContent;(t.includes("Manage Options")||t.includes("Optionen verwalten"))&&e.click()})),await m(".unic input[type=checkbox]",1e3)){await m(".unic button",1e3),document.querySelectorAll(".unic input[type=checkbox]").forEach((e=>{e.checked&&e.click()}));for(const e of document.querySelectorAll(".unic button")){const t=e.textContent;for(const o of["Confirm Choices","Save Choices","Auswahl speichern"])if(t.includes(o))return e.click(),await b(500),!0}}return!1}async optIn(){return k(".unic #unic-agree")}async test(){await b(1e3);return!d(".unic .unic-box,.unic .unic-bar")}},class extends A{constructor(){super(...arguments),this.prehideSelectors=[".cmp-root"],this.name="Conversant"}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return d(".cmp-root .cmp-receptacle")}async detectPopup(){return u(".cmp-root .cmp-receptacle","any")}async optOut(){if(!await k(".cmp-main-button:not(.cmp-main-button--primary)"))return!1;if(!await m(".cmp-view-tab-tabs"))return!1;await k(".cmp-view-tab-tabs > :first-child"),await k(".cmp-view-tab-tabs > .cmp-view-tab--active:first-child");for(const e of Array.from(document.querySelectorAll(".cmp-accordion-item"))){e.querySelector(".cmp-accordion-item-title").click(),await r((()=>!!e.querySelector(".cmp-accordion-item-content.cmp-active")),10,50);const t=e.querySelector(".cmp-accordion-item-content.cmp-active");t.querySelectorAll(".cmp-toggle-actions .cmp-toggle-deny:not(.cmp-toggle-deny--active)").forEach((e=>e.click())),t.querySelectorAll(".cmp-toggle-actions .cmp-toggle-checkbox:not(.cmp-toggle-checkbox--active)").forEach((e=>e.click()))}return await p(".cmp-main-button:not(.cmp-main-button--primary)"),!0}async optIn(){return k(".cmp-main-button.cmp-main-button--primary")}async test(){return document.cookie.includes("cmp-data=0")}},class extends A{constructor(){super(...arguments),this.name="tiktok.com",this.runContext={urlPattern:"tiktok"}}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}getShadowRoot(){const e=document.querySelector("tiktok-cookie-banner");return e?e.shadowRoot:null}async detectCmp(){return d("tiktok-cookie-banner")}async detectPopup(){return l(this.getShadowRoot().querySelector(".tiktok-cookie-banner"))}async optOut(){const e=this.getShadowRoot().querySelector(".button-wrapper button:first-child");return!!e&&(e.click(),!0)}async optIn(){const e=this.getShadowRoot().querySelector(".button-wrapper button:last-child");return!!e&&(e.click(),!0)}async test(){const e=document.cookie.match(/cookie-consent=([^;]+)/);if(!e)return!1;const t=JSON.parse(decodeURIComponent(e[1]));return Object.values(t).every((e=>"boolean"!=typeof e||!1===e))}},class extends A{constructor(){super(...arguments),this.runContext={urlPattern:"^https://(www\\.)?airbnb\\.[^/]+/"},this.prehideSelectors=["div[data-testid=main-cookies-banner-container]",'div:has(> div:first-child):has(> div:last-child):has(> section [data-testid="strictly-necessary-cookies"])']}get hasSelfTest(){return!0}get isIntermediate(){return!1}get isCosmetic(){return!1}async detectCmp(){return d("div[data-testid=main-cookies-banner-container]")}async detectPopup(){return u("div[data-testid=main-cookies-banner-container","any")}async optOut(){let e;for(await k("div[data-testid=main-cookies-banner-container] button._snbhip0");e=document.querySelector("[data-testid=modal-container] button[aria-checked=true]:not([disabled])");)e.click();return k("button[data-testid=save-btn]")}async optIn(){return k("div[data-testid=main-cookies-banner-container] button._148dgdpk")}async test(){return await r((()=>!!document.cookie.match("OptanonAlertBoxClosed")),20,200)}}];var P=[{name:"192.com",detectCmp:[{exists:".ont-cookies"}],detectPopup:[{visible:".ont-cookies"}],optIn:[{click:".ont-btn-main.ont-cookies-btn.js-ont-btn-ok2"}],optOut:[{click:".ont-cookes-btn-manage"},{click:".ont-btn-main.ont-cookies-btn.js-ont-btn-choose"}],test:[{eval:"EVAL_ONENINETWO_0"}]},{name:"1password-com",cosmetic:!0,prehideSelectors:['footer #footer-root [aria-label="Cookie Consent"]'],detectCmp:[{exists:'footer #footer-root [aria-label="Cookie Consent"]'}],detectPopup:[{visible:'footer #footer-root [aria-label="Cookie Consent"]'}],optIn:[{click:'footer #footer-root [aria-label="Cookie Consent"] button'}],optOut:[{hide:['footer #footer-root [aria-label="Cookie Consent"]']}]},{name:"activobank.pt",runContext:{urlPattern:"^https://(www\\.)?activobank\\.pt"},prehideSelectors:["aside#cookies,.overlay-cookies"],detectCmp:[{exists:"#cookies .cookies-btn"}],detectPopup:[{visible:"#cookies #submitCookies"}],optIn:[{waitForThenClick:"#cookies #submitCookies"}],optOut:[{waitForThenClick:"#cookies #rejectCookies"}]},{name:"Adroll",prehideSelectors:["#adroll_consent_container"],detectCmp:[{exists:"#adroll_consent_container"}],detectPopup:[{visible:"#adroll_consent_container"}],optIn:[{waitForThenClick:"#adroll_consent_accept"}],optOut:[{waitForThenClick:"#adroll_consent_reject"}],test:[{eval:"EVAL_ADROLL_0"}]},{name:"affinity.serif.com",detectCmp:[{exists:".c-cookie-banner button[data-qa='allow-all-cookies']"}],detectPopup:[{visible:".c-cookie-banner"}],optIn:[{click:'button[data-qa="allow-all-cookies"]'}],optOut:[{click:'button[data-qa="manage-cookies"]'},{waitFor:'.c-cookie-banner ~ [role="dialog"]'},{waitForThenClick:'.c-cookie-banner ~ [role="dialog"] input[type="checkbox"][value="true"]',all:!0},{click:'.c-cookie-banner ~ [role="dialog"] .c-modal__action button'}],test:[{wait:500},{eval:"EVAL_AFFINITY_SERIF_COM_0"}]},{name:"agolde.com",cosmetic:!0,prehideSelectors:["#modal-1 div[data-micromodal-close]"],detectCmp:[{exists:"#modal-1 div[aria-labelledby=modal-1-title]"}],detectPopup:[{exists:"#modal-1 div[data-micromodal-close]"}],optIn:[{click:'button[aria-label="Close modal"]'}],optOut:[{hide:["#modal-1 div[data-micromodal-close]"]}]},{name:"almacmp",prehideSelectors:["#alma-cmpv2-container"],detectCmp:[{exists:"#alma-cmpv2-container"}],detectPopup:[{visible:"#alma-cmpv2-container #almacmp-modal-layer1"}],optIn:[{waitForThenClick:"#alma-cmpv2-container #almacmp-modal-layer1 #almacmp-modalConfirmBtn"}],optOut:[{waitForThenClick:"#alma-cmpv2-container #almacmp-modal-layer1 #almacmp-modalSettingBtn"},{waitFor:"#alma-cmpv2-container #almacmp-modal-layer2"},{waitForThenClick:"#alma-cmpv2-container #almacmp-modal-layer2 #almacmp-reject-all-layer2"}],test:[{eval:"EVAL_ALMACMP_0"}]},{name:"altium.com",cosmetic:!0,prehideSelectors:[".altium-privacy-bar"],detectCmp:[{exists:".altium-privacy-bar"}],detectPopup:[{exists:".altium-privacy-bar"}],optIn:[{click:"a.altium-privacy-bar__btn"}],optOut:[{hide:[".altium-privacy-bar"]}]},{name:"amazon.com",prehideSelectors:['span[data-action="sp-cc"][data-sp-cc*="rejectAllAction"]'],detectCmp:[{exists:'span[data-action="sp-cc"][data-sp-cc*="rejectAllAction"]'}],detectPopup:[{visible:'span[data-action="sp-cc"][data-sp-cc*="rejectAllAction"]'}],optIn:[{waitForVisible:"#sp-cc-accept"},{wait:500},{click:"#sp-cc-accept"}],optOut:[{waitForVisible:"#sp-cc-rejectall-link"},{wait:500},{click:"#sp-cc-rejectall-link"}]},{name:"aquasana.com",cosmetic:!0,prehideSelectors:["#consent-tracking"],detectCmp:[{exists:"#consent-tracking"}],detectPopup:[{exists:"#consent-tracking"}],optIn:[{click:"#accept_consent"}],optOut:[{hide:["#consent-tracking"]}]},{name:"arzt-auskunft.de",prehideSelectors:["#cookiescript_injected"],detectCmp:[{exists:"#cookiescript_injected"}],detectPopup:[{visible:"#cookiescript_injected"}],optOut:[{click:"#cookiescript_reject"}],optIn:[{click:"#cookiescript_accept"}]},{name:"athlinks-com",runContext:{urlPattern:"^https://(www\\.)?athlinks\\.com/"},cosmetic:!0,prehideSelectors:["#footer-container ~ div"],detectCmp:[{exists:"#footer-container ~ div"}],detectPopup:[{visible:"#footer-container > div"}],optIn:[{click:"#footer-container ~ div button"}],optOut:[{hide:["#footer-container ~ div"]}]},{name:"ausopen.com",cosmetic:!0,detectCmp:[{exists:".gdpr-popup__message"}],detectPopup:[{visible:".gdpr-popup__message"}],optOut:[{hide:[".gdpr-popup__message"]}],optIn:[{click:".gdpr-popup__message button"}]},{name:"automattic-cmp-optout",prehideSelectors:['form[class*="cookie-banner"][method="post"]'],detectCmp:[{exists:'form[class*="cookie-banner"][method="post"]'}],detectPopup:[{visible:'form[class*="cookie-banner"][method="post"]'}],optIn:[{click:'a[class*="accept-all-button"]'}],optOut:[{click:'form[class*="cookie-banner"] div[class*="simple-options"] a[class*="customize-button"]'},{waitForThenClick:"input[type=checkbox][checked]:not([disabled])",all:!0},{click:'a[class*="accept-selection-button"]'}]},{name:"aws.amazon.com",prehideSelectors:["#awsccc-cb-content","#awsccc-cs-container","#awsccc-cs-modalOverlay","#awsccc-cs-container-inner"],detectCmp:[{exists:"#awsccc-cb-content"}],detectPopup:[{visible:"#awsccc-cb-content"}],optIn:[{click:"button[data-id=awsccc-cb-btn-accept"}],optOut:[{click:"button[data-id=awsccc-cb-btn-customize]"},{waitFor:"input[aria-checked]"},{click:"input[aria-checked=true]",all:!0,optional:!0},{click:"button[data-id=awsccc-cs-btn-save]"}]},{name:"axeptio",prehideSelectors:[".axeptio_widget"],detectCmp:[{exists:".axeptio_widget"}],detectPopup:[{visible:".axeptio_widget"}],optIn:[{waitFor:".axeptio-widget--open"},{click:"button#axeptio_btn_acceptAll"}],optOut:[{waitFor:".axeptio-widget--open"},{click:"button#axeptio_btn_dismiss"}],test:[{eval:"EVAL_AXEPTIO_0"}]},{name:"baden-wuerttemberg.de",prehideSelectors:[".cookie-alert.t-dark"],cosmetic:!0,detectCmp:[{exists:".cookie-alert.t-dark"}],detectPopup:[{visible:".cookie-alert.t-dark"}],optIn:[{click:".cookie-alert__form input:not([disabled]):not([checked])"},{click:".cookie-alert__button button"}],optOut:[{hide:[".cookie-alert.t-dark"]}]},{name:"bbb.org",runContext:{urlPattern:"^https://www\\.bbb\\.org/"},cosmetic:!0,prehideSelectors:['div[aria-label="use of cookies on bbb.org"]'],detectCmp:[{exists:'div[aria-label="use of cookies on bbb.org"]'}],detectPopup:[{visible:'div[aria-label="use of cookies on bbb.org"]'}],optIn:[{click:'div[aria-label="use of cookies on bbb.org"] button.bds-button-unstyled span.visually-hidden'}],optOut:[{hide:['div[aria-label="use of cookies on bbb.org"]']}]},{name:"bing.com",prehideSelectors:["#bnp_container"],detectCmp:[{exists:"#bnp_cookie_banner"}],detectPopup:[{visible:"#bnp_cookie_banner"}],optIn:[{click:"#bnp_btn_accept"}],optOut:[{click:"#bnp_btn_preference"},{click:"#mcp_savesettings"}],test:[{eval:"EVAL_BING_0"}]},{name:"borlabs",detectCmp:[{exists:"._brlbs-block-content"}],detectPopup:[{visible:"._brlbs-bar-wrap,._brlbs-box-wrap"}],optIn:[{click:"a[data-cookie-accept-all]"}],optOut:[{click:"a[data-cookie-individual]"},{waitForVisible:".cookie-preference"},{click:"input[data-borlabs-cookie-checkbox]:checked",all:!0,optional:!0},{click:"#CookiePrefSave"},{wait:500}],prehideSelectors:["#BorlabsCookieBox"],test:[{eval:"EVAL_BORLABS_0"}]},{name:"bundesregierung.de",prehideSelectors:[".bpa-cookie-banner"],detectCmp:[{exists:".bpa-cookie-banner"}],detectPopup:[{visible:".bpa-cookie-banner .bpa-module-full-hero"}],optIn:[{click:".bpa-accept-all-button"}],optOut:[{wait:500,comment:"click is not immediately recognized"},{waitForThenClick:".bpa-close-button"}],test:[{eval:"EVAL_BUNDESREGIERUNG_DE_0"}]},{name:"burpee.com",cosmetic:!0,prehideSelectors:["#notice-cookie-block"],detectCmp:[{exists:"#notice-cookie-block"}],detectPopup:[{exists:"#html-body #notice-cookie-block"}],optIn:[{click:"#btn-cookie-allow"}],optOut:[{hide:["#html-body #notice-cookie-block","#notice-cookie"]}]},{name:"canva.com",prehideSelectors:['div[role="dialog"] a[data-anchor-id="cookie-policy"]'],detectCmp:[{exists:'div[role="dialog"] a[data-anchor-id="cookie-policy"]'}],detectPopup:[{exists:'div[role="dialog"] a[data-anchor-id="cookie-policy"]'}],optIn:[{click:'div[role="dialog"] button:nth-child(1)'}],optOut:[{if:{exists:'div[role="dialog"] button:nth-child(3)'},then:[{click:'div[role="dialog"] button:nth-child(2)'}],else:[{click:'div[role="dialog"] button:nth-child(2)'},{waitFor:'div[role="dialog"] a[data-anchor-id="privacy-policy"]'},{click:'div[role="dialog"] button:nth-child(2)'},{click:'div[role="dialog"] div:last-child button:only-child'}]}],test:[{eval:"EVAL_CANVA_0"}]},{name:"cc-banner-springer",prehideSelectors:[".cc-banner[data-cc-banner]"],detectCmp:[{exists:".cc-banner[data-cc-banner]"}],detectPopup:[{visible:".cc-banner[data-cc-banner]"}],optIn:[{waitForThenClick:".cc-banner[data-cc-banner] button[data-cc-action=accept]"}],optOut:[{if:{exists:".cc-banner[data-cc-banner] button[data-cc-action=reject]"},then:[{click:".cc-banner[data-cc-banner] button[data-cc-action=reject]"}],else:[{waitForThenClick:".cc-banner[data-cc-banner] button[data-cc-action=preferences]"},{waitFor:".cc-preferences[data-cc-preferences]"},{click:".cc-preferences[data-cc-preferences] input[type=radio][data-cc-action=toggle-category][value=off]",all:!0},{if:{exists:".cc-preferences[data-cc-preferences] button[data-cc-action=reject]"},then:[{click:".cc-preferences[data-cc-preferences] button[data-cc-action=reject]"}],else:[{click:".cc-preferences[data-cc-preferences] button[data-cc-action=save]"}]}]}],test:[{eval:"EVAL_CC_BANNER2_0"}]},{name:"cc_banner",cosmetic:!0,prehideSelectors:[".cc_banner-wrapper"],detectCmp:[{exists:".cc_banner-wrapper"}],detectPopup:[{visible:".cc_banner"}],optIn:[{click:".cc_btn_accept_all"}],optOut:[{hide:[".cc_banner-wrapper"]}]},{comment:"https://www.civicuk.com/cookie-control/",name:"civic-cookie-control",prehideSelectors:["#ccc-module,#ccc-overlay"],detectCmp:[{exists:"#ccc-module"}],detectPopup:[{visible:"#ccc"},{visible:"#ccc-module"}],optOut:[{click:"#ccc-reject-settings"}],optIn:[{click:"#ccc-recommended-settings"}]},{name:"click.io",prehideSelectors:["#cl-consent"],detectCmp:[{exists:"#cl-consent"}],detectPopup:[{visible:"#cl-consent"}],optIn:[{waitForThenClick:'#cl-consent [data-role="b_agree"]'}],optOut:[{waitFor:'#cl-consent [data-role="b_options"]'},{wait:500},{click:'#cl-consent [data-role="b_options"]'},{waitFor:'.cl-consent-popup.cl-consent-visible [data-role="alloff"]'},{click:'.cl-consent-popup.cl-consent-visible [data-role="alloff"]',all:!0},{click:'[data-role="b_save"]'}],test:[{eval:"EVAL_CLICKIO_0",comment:"TODO: this only checks if we interacted at all"}]},{name:"clinch",intermediate:!1,runContext:{frame:!1,main:!0},prehideSelectors:[".consent-modal[role=dialog]"],detectCmp:[{exists:".consent-modal[role=dialog]"}],detectPopup:[{visible:".consent-modal[role=dialog]"}],optIn:[{click:"#consent_agree"}],optOut:[{click:"#manage_cookie_preferences"},{click:"#cookie_consent_preferences input:checked",all:!0,optional:!0},{click:"#consent_save"}],test:[{eval:"EVAL_CLINCH_0"}]},{name:"clustrmaps.com",runContext:{urlPattern:"^https://(www\\.)?clustrmaps\\.com/"},cosmetic:!0,prehideSelectors:["#gdpr-cookie-message"],detectCmp:[{exists:"#gdpr-cookie-message"}],detectPopup:[{visible:"#gdpr-cookie-message"}],optIn:[{click:"button#gdpr-cookie-accept"}],optOut:[{hide:["#gdpr-cookie-message"]}]},{name:"coinbase",intermediate:!1,runContext:{frame:!0,main:!0,urlPattern:"^https://(www|help)\\.coinbase\\.com"},prehideSelectors:[],detectCmp:[{exists:"div[class^=CookieBannerContent__Container]"}],detectPopup:[{visible:"div[class^=CookieBannerContent__Container]"}],optIn:[{click:"div[class^=CookieBannerContent__CTA] :nth-last-child(1)"}],optOut:[{click:"button[class^=CookieBannerContent__Settings]"},{click:"div[class^=CookiePreferencesModal__CategoryContainer] input:checked",all:!0,optional:!0},{click:"div[class^=CookiePreferencesModal__ButtonContainer] > button"}],test:[{eval:"EVAL_COINBASE_0"}]},{name:"Complianz banner",prehideSelectors:["#cmplz-cookiebanner-container"],detectCmp:[{exists:"#cmplz-cookiebanner-container .cmplz-cookiebanner"}],detectPopup:[{visible:"#cmplz-cookiebanner-container .cmplz-cookiebanner",check:"any"}],optIn:[{waitForThenClick:".cmplz-cookiebanner .cmplz-accept"}],optOut:[{waitForThenClick:".cmplz-cookiebanner .cmplz-deny"}],test:[{eval:"EVAL_COMPLIANZ_BANNER_0"}]},{name:"Complianz categories",prehideSelectors:['.cc-type-categories[aria-describedby="cookieconsent:desc"]'],detectCmp:[{exists:'.cc-type-categories[aria-describedby="cookieconsent:desc"]'}],detectPopup:[{visible:'.cc-type-categories[aria-describedby="cookieconsent:desc"]'}],optIn:[{click:".cc-accept-all",optional:!0},{click:".cc-allow",optional:!0},{click:".cc-dismiss",optional:!0}],optOut:[{click:".cc-dismiss"}],test:[{eval:"EVAL_COMPLIANZ_CATEGORIES_0"}]},{name:"Complianz notice",prehideSelectors:['.cc-type-info[aria-describedby="cookieconsent:desc"]'],cosmetic:!0,detectCmp:[{exists:'.cc-type-info[aria-describedby="cookieconsent:desc"]'}],detectPopup:[{visible:'.cc-type-info[aria-describedby="cookieconsent:desc"]'}],optIn:[{click:".cc-accept-all",optional:!0},{click:".cc-allow",optional:!0},{click:".cc-dismiss",optional:!0}],optOut:[{hide:['[aria-describedby="cookieconsent:desc"]']}]},{name:"Complianz optin",prehideSelectors:['.cc-type-opt-in[aria-describedby="cookieconsent:desc"]'],detectCmp:[{exists:'.cc-type-opt-in[aria-describedby="cookieconsent:desc"]'}],detectPopup:[{visible:'.cc-type-opt-in[aria-describedby="cookieconsent:desc"]'}],optIn:[{click:".cc-accept-all",optional:!0},{click:".cc-allow",optional:!0},{click:".cc-dismiss",optional:!0}],optOut:[{click:".cc-settings"},{waitForVisible:'[aria-label="cookies preferences popup"]'},{click:'[aria-label="cookies preferences popup"] input[type=checkbox]:not([disabled]):checked',all:!0,optional:!0},{click:'[aria-label="cookies preferences popup"] [aria-label="Accept Selected"], [aria-label="cookies preferences popup"] [aria-label="Save my choice"], .cc-btn-accept-selected, .cc-deny',optional:!0}],test:[{eval:"EVAL_COMPLIANZ_OPTIN_0"}]},{name:"cookie-law-info",prehideSelectors:["#cookie-law-info-bar"],detectCmp:[{exists:"#cookie-law-info-bar"}],detectPopup:[{visible:"#cookie-law-info-bar"}],optIn:[{click:'[data-cli_action="accept_all"]'}],optOut:[{hide:["#cookie-law-info-bar"]},{eval:"EVAL_COOKIE_LAW_INFO_0"}],test:[{eval:"EVAL_COOKIE_LAW_INFO_1"}]},{name:"cookie-manager-popup",cosmetic:!1,runContext:{main:!0,frame:!1},intermediate:!1,detectCmp:[{exists:"#notice-cookie-block #allow-functional-cookies, #notice-cookie-block #btn-cookie-settings"}],detectPopup:[{visible:"#notice-cookie-block"}],optIn:[{click:"#btn-cookie-allow"}],optOut:[{if:{exists:"#allow-functional-cookies"},then:[{click:"#allow-functional-cookies"}],else:[{waitForThenClick:"#btn-cookie-settings"},{waitForVisible:".modal-body"},{click:'.modal-body input:checked, .switch[data-switch="on"]',all:!0,optional:!0},{click:'[role="dialog"] .modal-footer button'}]}],prehideSelectors:["#btn-cookie-settings"],test:[{eval:"EVAL_COOKIE_MANAGER_POPUP_0"}]},{name:"cookie-notice",prehideSelectors:["#cookie-notice"],cosmetic:!0,detectCmp:[{visible:"#cookie-notice .cookie-notice-container"}],detectPopup:[{visible:"#cookie-notice"}],optIn:[{click:"#cn-accept-cookie"}],optOut:[{hide:["#cookie-notice"]}]},{name:"cookiealert",intermediate:!1,prehideSelectors:[],runContext:{frame:!0,main:!0},detectCmp:[{exists:".cookie-alert-extended"}],detectPopup:[{visible:".cookie-alert-extended-modal"}],optIn:[{click:"button[data-controller='cookie-alert/extended/button/accept']"},{eval:"EVAL_COOKIEALERT_0"}],optOut:[{click:"a[data-controller='cookie-alert/extended/detail-link']"},{click:".cookie-alert-configuration-input:checked",all:!0,optional:!0},{click:"button[data-controller='cookie-alert/extended/button/configuration']"},{eval:"EVAL_COOKIEALERT_0"}],test:[{eval:"EVAL_COOKIEALERT_2"}]},{name:"cookiefirst.com",prehideSelectors:["#cookiefirst-root,.cookiefirst-root,[aria-labelledby=cookie-preference-panel-title]"],detectCmp:[{exists:"#cookiefirst-root,.cookiefirst-root"}],detectPopup:[{visible:"#cookiefirst-root,.cookiefirst-root"}],optIn:[{click:"button[data-cookiefirst-action=accept]"}],optOut:[{if:{exists:"button[data-cookiefirst-action=adjust]"},then:[{click:"button[data-cookiefirst-action=adjust]"},{waitForVisible:"[data-cookiefirst-widget=modal]",timeout:1e3},{eval:"EVAL_COOKIEFIRST_1"},{wait:1e3},{click:"button[data-cookiefirst-action=save]"}],else:[{click:"button[data-cookiefirst-action=reject]"}]}],test:[{eval:"EVAL_COOKIEFIRST_0"}]},{name:"Cookie Information Banner",prehideSelectors:["#cookie-information-template-wrapper"],detectCmp:[{exists:"#cookie-information-template-wrapper"}],detectPopup:[{visible:"#cookie-information-template-wrapper"}],optIn:[{eval:"EVAL_COOKIEINFORMATION_1"}],optOut:[{hide:["#cookie-information-template-wrapper"],comment:"some templates don't hide the banner automatically"},{eval:"EVAL_COOKIEINFORMATION_0"}],test:[{eval:"EVAL_COOKIEINFORMATION_2"}]},{name:"corona-in-zahlen.de",prehideSelectors:[".cookiealert"],detectCmp:[{exists:".cookiealert"}],detectPopup:[{visible:".cookiealert"}],optOut:[{click:".configurecookies"},{click:".confirmcookies"}],optIn:[{click:".acceptcookies"}]},{name:"crossfit-com",cosmetic:!0,prehideSelectors:['body #modal > div > div[class^="_wrapper_"]'],detectCmp:[{exists:'body #modal > div > div[class^="_wrapper_"]'}],detectPopup:[{visible:'body #modal > div > div[class^="_wrapper_"]'}],optIn:[{click:'button[aria-label="accept cookie policy"]'}],optOut:[{hide:['body #modal > div > div[class^="_wrapper_"]']}]},{name:"dailymotion-us",cosmetic:!0,prehideSelectors:['div[class*="CookiePopup__desktopContainer"]:has(div[class*="CookiePopup"])'],detectCmp:[{exists:'div[class*="CookiePopup__desktopContainer"]'}],detectPopup:[{visible:'div[class*="CookiePopup__desktopContainer"]'}],optIn:[{click:'div[class*="CookiePopup__desktopContainer"] > button > span'}],optOut:[{hide:['div[class*="CookiePopup__desktopContainer"]']}]},{name:"dailymotion.com",runContext:{urlPattern:"^https://(www\\.)?dailymotion\\.com/"},prehideSelectors:['div[class*="Overlay__container"]:has(div[class*="TCF2Popup"])'],detectCmp:[{exists:'div[class*="TCF2Popup"]'}],detectPopup:[{visible:'[class*="TCF2Popup"] a[href^="https://www.dailymotion.com/legal/cookiemanagement"]'}],optIn:[{waitForThenClick:'button[class*="TCF2Popup__button"]:not([class*="TCF2Popup__personalize"])'}],optOut:[{waitForThenClick:'button[class*="TCF2ContinueWithoutAcceptingButton"]'}],test:[{eval:"EVAL_DAILYMOTION_0"}]},{name:"deepl.com",prehideSelectors:[".dl_cookieBanner_container"],detectCmp:[{exists:".dl_cookieBanner_container"}],detectPopup:[{visible:".dl_cookieBanner_container"}],optOut:[{click:".dl_cookieBanner--buttonSelected"}],optIn:[{click:".dl_cookieBanner--buttonAll"}]},{name:"delta.com",runContext:{urlPattern:"^https://www\\.delta\\.com/"},cosmetic:!0,prehideSelectors:["ngc-cookie-banner"],detectCmp:[{exists:"div.cookie-footer-container"}],detectPopup:[{visible:"div.cookie-footer-container"}],optIn:[{click:" button.cookie-close-icon"}],optOut:[{hide:["div.cookie-footer-container"]}]},{name:"dmgmedia-us",prehideSelectors:["#mol-ads-cmp-iframe, div.mol-ads-cmp > form > div"],detectCmp:[{exists:"div.mol-ads-cmp > form > div"}],detectPopup:[{waitForVisible:"div.mol-ads-cmp > form > div"}],optIn:[{waitForThenClick:"button.mol-ads-cmp--btn-primary"}],optOut:[{waitForThenClick:"div.mol-ads-ccpa--message > u > a"},{waitForVisible:".mol-ads-cmp--modal-dialog"},{waitForThenClick:"a.mol-ads-cmp-footer-privacy"},{waitForThenClick:"button.mol-ads-cmp--btn-secondary"}]},{name:"dmgmedia",prehideSelectors:['[data-project="mol-fe-cmp"]'],detectCmp:[{exists:'[data-project="mol-fe-cmp"]'}],detectPopup:[{visible:'[data-project="mol-fe-cmp"]'}],optIn:[{waitForThenClick:'[data-project="mol-fe-cmp"] button[class*=primary]'}],optOut:[{waitForThenClick:'[data-project="mol-fe-cmp"] button[class*=basic]'},{waitForVisible:'[data-project="mol-fe-cmp"] div[class*="tabContent"]'},{waitForThenClick:'[data-project="mol-fe-cmp"] div[class*="toggle"][class*="enabled"]',all:!0},{waitForThenClick:'[data-project="mol-fe-cmp"] button[class*=white]'}]},{name:"Drupal",detectCmp:[{exists:"#drupalorg-crosssite-gdpr"}],detectPopup:[{visible:"#drupalorg-crosssite-gdpr"}],optOut:[{click:".no"}],optIn:[{click:".yes"}]},{name:"WP DSGVO Tools",link:"https://wordpress.org/plugins/shapepress-dsgvo/",prehideSelectors:[".sp-dsgvo"],cosmetic:!0,detectCmp:[{exists:".sp-dsgvo.sp-dsgvo-popup-overlay"}],detectPopup:[{visible:".sp-dsgvo.sp-dsgvo-popup-overlay",check:"any"}],optIn:[{click:".sp-dsgvo-privacy-btn-accept-all",all:!0}],optOut:[{hide:[".sp-dsgvo.sp-dsgvo-popup-overlay"]}],test:[{eval:"EVAL_DSGVO_0"}]},{name:"dunelm.com",prehideSelectors:["div[data-testid=cookie-consent-modal-backdrop]"],detectCmp:[{exists:"div[data-testid=cookie-consent-message-contents]"}],detectPopup:[{visible:"div[data-testid=cookie-consent-message-contents]"}],optIn:[{click:'[data-testid="cookie-consent-allow-all"]'}],optOut:[{click:"button[data-testid=cookie-consent-adjust-settings]"},{click:"button[data-testid=cookie-consent-preferences-save]"}],test:[{eval:"EVAL_DUNELM_0"}]},{name:"etsy",prehideSelectors:["#gdpr-single-choice-overlay","#gdpr-privacy-settings"],detectCmp:[{exists:"#gdpr-single-choice-overlay"}],detectPopup:[{visible:"#gdpr-single-choice-overlay"}],optOut:[{click:"button[data-gdpr-open-full-settings]"},{waitForVisible:".gdpr-overlay-body input",timeout:3e3},{wait:1e3},{eval:"EVAL_ETSY_0"},{eval:"EVAL_ETSY_1"}],optIn:[{click:"button[data-gdpr-single-choice-accept]"}]},{name:"eu-cookie-compliance-banner",detectCmp:[{exists:".eu-cookie-compliance-banner-info"}],detectPopup:[{exists:".eu-cookie-compliance-popup-open"}],optIn:[{click:".agree-button"}],optOut:[{click:".decline-button,.eu-cookie-compliance-save-preferences-button",optional:!0},{hide:[".eu-cookie-compliance-banner-info","#sliding-popup"]}],test:[{eval:"EVAL_EU_COOKIE_COMPLIANCE_0"}]},{name:"EU Cookie Law",prehideSelectors:[".pea_cook_wrapper,.pea_cook_more_info_popover"],cosmetic:!0,detectCmp:[{exists:".pea_cook_wrapper"}],detectPopup:[{wait:500},{visible:".pea_cook_wrapper"}],optIn:[{click:"#pea_cook_btn"}],optOut:[{hide:[".pea_cook_wrapper"]}],test:[{eval:"EVAL_EU_COOKIE_LAW_0"}]},{name:"EZoic",prehideSelectors:["#ez-cookie-dialog-wrapper"],detectCmp:[{exists:"#ez-cookie-dialog-wrapper"}],detectPopup:[{visible:"#ez-cookie-dialog-wrapper"}],optIn:[{click:"#ez-accept-all",optional:!0},{eval:"EVAL_EZOIC_0",optional:!0}],optOut:[{wait:500},{click:"#ez-manage-settings"},{waitFor:"#ez-cookie-dialog input[type=checkbox]"},{click:"#ez-cookie-dialog input[type=checkbox][checked]",all:!0},{click:"#ez-save-settings"}],test:[{eval:"EVAL_EZOIC_1"}]},{name:"facebook",runContext:{urlPattern:"^https://([a-z0-9-]+\\.)?facebook\\.com/"},prehideSelectors:['div[data-testid="cookie-policy-manage-dialog"]'],detectCmp:[{exists:'div[data-testid="cookie-policy-manage-dialog"]'}],detectPopup:[{visible:'div[data-testid="cookie-policy-manage-dialog"]'}],optIn:[{waitForThenClick:'button[data-cookiebanner="accept_button"]'},{waitForVisible:'div[data-testid="cookie-policy-manage-dialog"]',check:"none"}],optOut:[{waitForThenClick:'button[data-cookiebanner="accept_only_essential_button"]'},{waitForVisible:'div[data-testid="cookie-policy-manage-dialog"]',check:"none"}]},{name:"funding-choices",prehideSelectors:[".fc-consent-root,.fc-dialog-container,.fc-dialog-overlay,.fc-dialog-content"],detectCmp:[{exists:".fc-consent-root"}],detectPopup:[{exists:".fc-dialog-container"}],optOut:[{click:".fc-cta-do-not-consent,.fc-cta-manage-options"},{click:".fc-preference-consent:checked,.fc-preference-legitimate-interest:checked",all:!0,optional:!0},{click:".fc-confirm-choices",optional:!0}],optIn:[{click:".fc-cta-consent"}]},{name:"geeks-for-geeks",runContext:{urlPattern:"^https://www\\.geeksforgeeks\\.org/"},cosmetic:!0,prehideSelectors:[".cookie-consent"],detectCmp:[{exists:".cookie-consent"}],detectPopup:[{visible:".cookie-consent"}],optIn:[{click:".cookie-consent button.consent-btn"}],optOut:[{hide:[".cookie-consent"]}]},{name:"generic-cosmetic",cosmetic:!0,prehideSelectors:["#js-cookie-banner,.js-cookie-banner,.cookie-banner,#cookie-banner"],detectCmp:[{exists:"#js-cookie-banner,.js-cookie-banner,.cookie-banner,#cookie-banner"}],detectPopup:[{visible:"#js-cookie-banner,.js-cookie-banner,.cookie-banner,#cookie-banner"}],optIn:[],optOut:[{hide:["#js-cookie-banner,.js-cookie-banner,.cookie-banner,#cookie-banner"]}]},{name:"google-consent-standalone",prehideSelectors:[],detectCmp:[{exists:'a[href^="https://policies.google.com/technologies/cookies"'},{exists:'form[action^="https://consent."][action$=".com/save"]'}],detectPopup:[{visible:'a[href^="https://policies.google.com/technologies/cookies"'}],optIn:[{waitForThenClick:'form[action^="https://consent."][action$=".com/save"]:has(input[name=set_eom][value=false]) button'}],optOut:[{waitForThenClick:'form[action^="https://consent."][action$=".com/save"]:has(input[name=set_eom][value=true]) button'}]},{name:"google.com",prehideSelectors:[".HTjtHe#xe7COe"],detectCmp:[{exists:".HTjtHe#xe7COe"},{exists:'.HTjtHe#xe7COe a[href^="https://policies.google.com/technologies/cookies"]'}],detectPopup:[{visible:".HTjtHe#xe7COe button#W0wltc"}],optIn:[{waitForThenClick:".HTjtHe#xe7COe button#L2AGLb"}],optOut:[{waitForThenClick:".HTjtHe#xe7COe button#W0wltc"}],test:[{eval:"EVAL_GOOGLE_0"}]},{name:"gov.uk",detectCmp:[{exists:"#global-cookie-message"}],detectPopup:[{exists:"#global-cookie-message"}],optIn:[{click:"button[data-accept-cookies=true]"}],optOut:[{click:"button[data-reject-cookies=true],#reject-cookies"},{click:"button[data-hide-cookie-banner=true],#hide-cookie-decision"}]},{name:"healthline-media",prehideSelectors:["#modal-host > div.no-hash > div.window-wrapper"],detectCmp:[{exists:"#modal-host > div.no-hash > div.window-wrapper, div[data-testid=qualtrics-container]"}],detectPopup:[{exists:"#modal-host > div.no-hash > div.window-wrapper, div[data-testid=qualtrics-container]"}],optIn:[{click:"#modal-host > div.no-hash > div.window-wrapper > div:last-child button"}],optOut:[{if:{exists:'#modal-host > div.no-hash > div.window-wrapper > div:last-child a[href="/privacy-settings"]'},then:[{click:'#modal-host > div.no-hash > div.window-wrapper > div:last-child a[href="/privacy-settings"]'}],else:[{waitForVisible:"div#__next"},{click:"#__next div:nth-child(1) > button:first-child"}]}]},{name:"hl.co.uk",prehideSelectors:[".cookieModalContent","#cookie-banner-overlay"],detectCmp:[{exists:"#cookie-banner-overlay"}],detectPopup:[{exists:"#cookie-banner-overlay"}],optIn:[{click:"#acceptCookieButton"}],optOut:[{click:"#manageCookie"},{hide:[".cookieSettingsModal"]},{waitFor:"#AOCookieToggle"},{click:"#AOCookieToggle[aria-pressed=true]",optional:!0},{waitFor:"#TPCookieToggle"},{click:"#TPCookieToggle[aria-pressed=true]",optional:!0},{click:"#updateCookieButton"}]},{name:"hubspot",detectCmp:[{exists:"#hs-eu-cookie-confirmation"}],detectPopup:[{visible:"#hs-eu-cookie-confirmation"}],optIn:[{click:"#hs-eu-confirmation-button"}],optOut:[{click:"#hs-eu-decline-button"}]},{name:"indeed.com",cosmetic:!0,prehideSelectors:["#CookiePrivacyNotice"],detectCmp:[{exists:"#CookiePrivacyNotice"}],detectPopup:[{visible:"#CookiePrivacyNotice"}],optIn:[{click:"#CookiePrivacyNotice button[data-gnav-element-name=CookiePrivacyNoticeOk]"}],optOut:[{hide:["#CookiePrivacyNotice"]}]},{name:"ionos.de",prehideSelectors:[".privacy-consent--backdrop",".privacy-consent--modal"],detectCmp:[{exists:".privacy-consent--modal"}],detectPopup:[{visible:".privacy-consent--modal"}],optIn:[{click:"#selectAll"}],optOut:[{click:".footer-config-link"},{click:"#confirmSelection"}]},{name:"itopvpn.com",cosmetic:!0,prehideSelectors:[".pop-cookie"],detectCmp:[{exists:".pop-cookie"}],detectPopup:[{exists:".pop-cookie"}],optIn:[{click:"#_pcookie"}],optOut:[{hide:[".pop-cookie"]}]},{name:"iubenda",prehideSelectors:["#iubenda-cs-banner"],detectCmp:[{exists:"#iubenda-cs-banner"}],detectPopup:[{visible:".iubenda-cs-accept-btn"}],optIn:[{click:".iubenda-cs-accept-btn"}],optOut:[{click:".iubenda-cs-customize-btn"},{eval:"EVAL_IUBENDA_0"},{click:"#iubFooterBtn"}],test:[{eval:"EVAL_IUBENDA_1"}]},{name:"johnlewis.com",prehideSelectors:["div[class^=pecr-cookie-banner-]"],detectCmp:[{exists:"div[class^=pecr-cookie-banner-]"}],detectPopup:[{exists:"div[class^=pecr-cookie-banner-]"}],optOut:[{click:"button[data-test^=manage-cookies]"},{wait:"500"},{click:"label[data-test^=toggle][class*=checked]:not([class*=disabled])",all:!0,optional:!0},{click:"button[data-test=save-preferences]"}],optIn:[{click:"button[data-test=allow-all]"}]},{name:"jquery.cookieBar",comment:"https://github.com/kovarp/jquery.cookieBar",prehideSelectors:[".cookie-bar"],cosmetic:!0,detectCmp:[{exists:".cookie-bar .cookie-bar__message,.cookie-bar .cookie-bar__buttons"}],detectPopup:[{visible:".cookie-bar .cookie-bar__message,.cookie-bar .cookie-bar__buttons",check:"any"}],optIn:[{click:".cookie-bar .cookie-bar__btn"}],optOut:[{hide:[".cookie-bar"]}],test:[{visible:".cookie-bar .cookie-bar__message,.cookie-bar .cookie-bar__buttons",check:"none"},{eval:"EVAL_JQUERY_COOKIEBAR_0"}]},{name:"justwatch.com",prehideSelectors:[".consent-banner"],detectCmp:[{exists:".consent-banner .consent-banner__actions"}],detectPopup:[{visible:".consent-banner .consent-banner__actions"}],optIn:[{click:".consent-banner__actions button.basic-button.primary"}],optOut:[{click:".consent-banner__actions button.basic-button.secondary"},{waitForThenClick:".consent-modal__footer button.basic-button.secondary"},{waitForThenClick:".consent-modal ion-content > div > a:nth-child(9)"},{click:"label.consent-switch input[type=checkbox]:checked",all:!0,optional:!0},{waitForVisible:".consent-modal__footer button.basic-button.primary"},{click:".consent-modal__footer button.basic-button.primary"}]},{name:"ketch",runContext:{frame:!1,main:!0},intermediate:!1,prehideSelectors:["#lanyard_root div[role='dialog']"],detectCmp:[{exists:"#lanyard_root div[role='dialog']"}],detectPopup:[{visible:"#lanyard_root div[role='dialog']"}],optIn:[{if:{exists:"#lanyard_root button[class='confirmButton']"},then:[{waitForThenClick:"#lanyard_root div[class^='buttons'] > :nth-child(2)"},{click:"#lanyard_root button[class='confirmButton']"}],else:[{waitForThenClick:"#lanyard_root div[class^='buttons'] > :nth-child(2)"}]}],optOut:[{click:"#lanyard_root button[class^='link']",optional:!0},{if:{exists:"#lanyard_root button[class*='confirmButton']"},then:[{waitForThenClick:"#lanyard_root button[class*='rejectButton']"},{click:"#lanyard_root button[class*='confirmButton']"}],else:[{click:"#lanyard_root div[class^='buttons'] > :nth-child(1)",optional:!0},{waitForThenClick:"#lanyard_root input:checked"},{click:"#consentsTab > div:nth-child(2) > div > div[class^='actions'] > button:nth-child(1)"}]}],test:[]},{name:"kleinanzeigen-de",runContext:{urlPattern:"^https?://(www\\.)?kleinanzeigen\\.de"},prehideSelectors:["#gdpr-banner-container"],detectCmp:[{any:[{exists:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-cmp-button]"},{exists:"#ConsentManagementPage"}]}],detectPopup:[{any:[{visible:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-cmp-button]"},{visible:"#ConsentManagementPage"}]}],optIn:[{if:{exists:"#gdpr-banner-container #gdpr-banner"},then:[{click:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-accept]"}],else:[{click:"#ConsentManagementPage .Button-primary"}]}],optOut:[{if:{exists:"#gdpr-banner-container #gdpr-banner"},then:[{click:"#gdpr-banner-container #gdpr-banner [data-testid=gdpr-banner-cmp-button]"}],else:[{click:"#ConsentManagementPage .Button-secondary"}]}]},{name:"linkedin.com",prehideSelectors:[".artdeco-global-alert[type=COOKIE_CONSENT]"],detectCmp:[{exists:".artdeco-global-alert[type=COOKIE_CONSENT]"}],detectPopup:[{visible:".artdeco-global-alert[type=COOKIE_CONSENT]"}],optIn:[{waitForVisible:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=ACCEPT]"},{wait:500},{waitForThenClick:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=ACCEPT]"}],optOut:[{waitForVisible:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=DENY]"},{wait:500},{waitForThenClick:".artdeco-global-alert[type=COOKIE_CONSENT] button[action-type=DENY]"}],test:[{waitForVisible:".artdeco-global-alert[type=COOKIE_CONSENT]",check:"none"}]},{name:"macpaw.com",cosmetic:!0,prehideSelectors:['div[data-banner="cookies"]'],detectCmp:[{exists:'div[data-banner="cookies"]'}],detectPopup:[{exists:'div[data-banner="cookies"]'}],optIn:[{click:'button[data-banner-close="cookies"]'}],optOut:[{hide:['div[data-banner="cookies"]']}]},{name:"marksandspencer.com",cosmetic:!0,detectCmp:[{exists:".navigation-cookiebbanner"}],detectPopup:[{visible:".navigation-cookiebbanner"}],optOut:[{hide:[".navigation-cookiebbanner"]}],optIn:[{click:".navigation-cookiebbanner__submit"}]},{name:"mediamarkt.de",prehideSelectors:["div[aria-labelledby=pwa-consent-layer-title]","div[class^=StyledConsentLayerWrapper-]"],detectCmp:[{exists:"div[aria-labelledby^=pwa-consent-layer-title]"}],detectPopup:[{exists:"div[aria-labelledby^=pwa-consent-layer-title]"}],optOut:[{click:"button[data-test^=pwa-consent-layer-deny-all]"}],optIn:[{click:"button[data-test^=pwa-consent-layer-accept-all"}]},{name:"Mediavine",prehideSelectors:['[data-name="mediavine-gdpr-cmp"]'],detectCmp:[{exists:'[data-name="mediavine-gdpr-cmp"]'}],detectPopup:[{wait:500},{visible:'[data-name="mediavine-gdpr-cmp"]'}],optIn:[{waitForThenClick:'[data-name="mediavine-gdpr-cmp"] [format="primary"]'}],optOut:[{waitForThenClick:'[data-name="mediavine-gdpr-cmp"] [data-view="manageSettings"]'},{waitFor:'[data-name="mediavine-gdpr-cmp"] input[type=checkbox]'},{eval:"EVAL_MEDIAVINE_0",optional:!0},{click:'[data-name="mediavine-gdpr-cmp"] [format="secondary"]'}]},{name:"microsoft.com",prehideSelectors:["#wcpConsentBannerCtrl"],detectCmp:[{exists:"#wcpConsentBannerCtrl"}],detectPopup:[{exists:"#wcpConsentBannerCtrl"}],optOut:[{eval:"EVAL_MICROSOFT_0"}],optIn:[{eval:"EVAL_MICROSOFT_1"}],test:[{eval:"EVAL_MICROSOFT_2"}]},{name:"midway-usa",runContext:{urlPattern:"^https://www\\.midwayusa\\.com/"},cosmetic:!0,prehideSelectors:["#cookie-container"],detectCmp:[{exists:['div[aria-label="Cookie Policy Banner"]']}],detectPopup:[{visible:"#cookie-container"}],optIn:[{click:"button#cookie-btn"}],optOut:[{hide:['div[aria-label="Cookie Policy Banner"]']}]},{name:"moneysavingexpert.com",detectCmp:[{exists:"dialog[data-testid=accept-our-cookies-dialog]"}],detectPopup:[{visible:"dialog[data-testid=accept-our-cookies-dialog]"}],optIn:[{click:"#banner-accept"}],optOut:[{click:"#banner-manage"},{click:"#pc-confirm"}]},{name:"monzo.com",prehideSelectors:[".cookie-alert, cookie-alert__content"],detectCmp:[{exists:'div.cookie-alert[role="dialog"]'},{exists:'a[href*="monzo"]'}],detectPopup:[{visible:".cookie-alert__content"}],optIn:[{click:".js-accept-cookie-policy"}],optOut:[{click:".js-decline-cookie-policy"}]},{name:"Moove",prehideSelectors:["#moove_gdpr_cookie_info_bar"],detectCmp:[{exists:"#moove_gdpr_cookie_info_bar"}],detectPopup:[{visible:"#moove_gdpr_cookie_info_bar"}],optIn:[{waitForThenClick:".moove-gdpr-infobar-allow-all"}],optOut:[{if:{exists:"#moove_gdpr_cookie_info_bar .change-settings-button"},then:[{click:"#moove_gdpr_cookie_info_bar .change-settings-button"},{waitForVisible:"#moove_gdpr_cookie_modal"},{eval:"EVAL_MOOVE_0"},{click:".moove-gdpr-modal-save-settings"}],else:[{hide:["#moove_gdpr_cookie_info_bar"]}]}],test:[{visible:"#moove_gdpr_cookie_info_bar",check:"none"}]},{name:"national-lottery.co.uk",detectCmp:[{exists:".cuk_cookie_consent"}],detectPopup:[{visible:".cuk_cookie_consent",check:"any"}],optOut:[{click:".cuk_cookie_consent_manage_pref"},{click:".cuk_cookie_consent_save_pref"},{click:".cuk_cookie_consent_close"}],optIn:[{click:".cuk_cookie_consent_accept_all"}]},{name:"nba.com",runContext:{urlPattern:"^https://(www\\.)?nba.com/"},cosmetic:!0,prehideSelectors:["#onetrust-banner-sdk"],detectCmp:[{exists:"#onetrust-banner-sdk"}],detectPopup:[{visible:"#onetrust-banner-sdk"}],optIn:[{click:"#onetrust-accept-btn-handler"}],optOut:[{hide:["#onetrust-banner-sdk"]}]},{name:"netflix.de",detectCmp:[{exists:"#cookie-disclosure"}],detectPopup:[{visible:".cookie-disclosure-message",check:"any"}],optIn:[{click:".btn-accept"}],optOut:[{hide:["#cookie-disclosure"]},{click:".btn-reject"}]},{name:"nhs.uk",prehideSelectors:["#nhsuk-cookie-banner"],detectCmp:[{exists:"#nhsuk-cookie-banner"}],detectPopup:[{exists:"#nhsuk-cookie-banner"}],optOut:[{click:"#nhsuk-cookie-banner__link_accept"}],optIn:[{click:"#nhsuk-cookie-banner__link_accept_analytics"}]},{name:"notice-cookie",prehideSelectors:[".button--notice"],cosmetic:!0,detectCmp:[{exists:".notice--cookie"}],detectPopup:[{visible:".notice--cookie"}],optIn:[{click:".button--notice"}],optOut:[{hide:[".notice--cookie"]}]},{name:"nrk.no",cosmetic:!0,prehideSelectors:[".nrk-masthead__info-banner--cookie"],detectCmp:[{exists:".nrk-masthead__info-banner--cookie"}],detectPopup:[{exists:".nrk-masthead__info-banner--cookie"}],optIn:[{click:"div.nrk-masthead__info-banner--cookie button > span:has(+ svg.nrk-close)"}],optOut:[{hide:[".nrk-masthead__info-banner--cookie"]}]},{name:"obi.de",prehideSelectors:[".disc-cp--active"],detectCmp:[{exists:".disc-cp-modal__modal"}],detectPopup:[{visible:".disc-cp-modal__modal"}],optIn:[{click:".js-disc-cp-accept-all"}],optOut:[{click:".js-disc-cp-deny-all"}]},{name:"onlyFans.com",prehideSelectors:["div.b-cookies-informer"],detectCmp:[{exists:"div.b-cookies-informer"}],detectPopup:[{exists:"div.b-cookies-informer"}],optIn:[{click:"div.b-cookies-informer__nav > button:nth-child(2)"}],optOut:[{click:"div.b-cookies-informer__nav > button:nth-child(1)"},{click:'div.b-cookies-informer__switchers > div:nth-child(2) > div[at-attr="checkbox"] > span.b-input-radio__container > input[type="checkbox"]'},{click:"div.b-cookies-informer__nav > button"}]},{name:"osano",prehideSelectors:[".osano-cm-window"],cosmetic:!0,detectCmp:[{exists:".osano-cm-window"}],detectPopup:[{visible:".osano-cm-dialog"}],optIn:[{click:".osano-cm-accept-all",optional:!0}],optOut:[{hide:[".osano-cm-window"]}]},{name:"otto.de",prehideSelectors:[".cookieBanner--visibility"],detectCmp:[{exists:".cookieBanner--visibility"}],detectPopup:[{visible:".cookieBanner__wrapper"}],optIn:[{click:".js_cookieBannerPermissionButton"}],optOut:[{click:".js_cookieBannerProhibitionButton"}]},{name:"paypal-us",prehideSelectors:["#ccpaCookieContent_wrapper, article.ppvx_modal--overpanel"],detectCmp:[{exists:"#ccpaCookieBanner, .privacy-modal-content"}],detectPopup:[{exists:"#ccpaCookieBanner, .privacy-modal-content"}],optIn:[{click:"#acceptAllButton"}],optOut:[{if:{exists:"a#manageCookiesLink"},then:[{click:"a#manageCookiesLink"}],else:[{waitForVisible:".privacy-modal-content #formContent"},{click:"#formContent .cookiepref-11m2iee-checkbox_base input:checked",all:!0,optional:!0},{click:".confirmCookie #submitCookiesBtn"}]}]},{name:"paypal.com",prehideSelectors:["#gdprCookieBanner"],detectCmp:[{exists:"#gdprCookieBanner"}],detectPopup:[{visible:"#gdprCookieContent_wrapper"}],optIn:[{click:"#acceptAllButton"}],optOut:[{wait:200},{click:".gdprCookieBanner_decline-button"}],test:[{wait:500},{eval:"EVAL_PAYPAL_0"}]},{name:"pinetools.com",cosmetic:!0,prehideSelectors:["#aviso_cookies"],detectCmp:[{exists:"#aviso_cookies"}],detectPopup:[{exists:".lang_en #aviso_cookies"}],optIn:[{click:"#aviso_cookies .a_boton_cerrar"}],optOut:[{hide:["#aviso_cookies"]}]},{name:"pmc",cosmetic:!0,prehideSelectors:["#pmc-pp-tou--notice"],detectCmp:[{exists:"#pmc-pp-tou--notice"}],detectPopup:[{visible:"#pmc-pp-tou--notice"}],optIn:[{click:"span.pmc-pp-tou--notice-close-btn"}],optOut:[{hide:["#pmc-pp-tou--notice"]}]},{name:"pornhub.com",runContext:{urlPattern:"^https://(www\\.)?pornhub\\.com/"},cosmetic:!0,prehideSelectors:[".cookiesBanner"],detectCmp:[{exists:".cookiesBanner"}],detectPopup:[{visible:".cookiesBanner"}],optIn:[{click:".cookiesBanner .okButton"}],optOut:[{hide:[".cookiesBanner"]}]},{name:"pornpics.com",cosmetic:!0,prehideSelectors:["#cookie-contract"],detectCmp:[{exists:"#cookie-contract"}],detectPopup:[{visible:"#cookie-contract"}],optIn:[{click:"#cookie-contract .icon-cross"}],optOut:[{hide:["#cookie-contract"]}]},{name:"PrimeBox CookieBar",prehideSelectors:["#cookie-bar"],detectCmp:[{exists:"#cookie-bar .cb-enable,#cookie-bar .cb-disable,#cookie-bar .cb-policy"}],detectPopup:[{visible:"#cookie-bar .cb-enable,#cookie-bar .cb-disable,#cookie-bar .cb-policy",check:"any"}],optIn:[{waitForThenClick:"#cookie-bar .cb-enable"}],optOut:[{click:"#cookie-bar .cb-disable",optional:!0},{hide:["#cookie-bar"]}],test:[{eval:"EVAL_PRIMEBOX_0"}]},{name:"privacymanager.io",prehideSelectors:["#gdpr-consent-tool-wrapper",'iframe[src^="https://cmp-consent-tool.privacymanager.io"]'],runContext:{urlPattern:"^https://cmp-consent-tool\\.privacymanager\\.io/",main:!1,frame:!0},detectCmp:[{exists:"button#save"}],detectPopup:[{visible:"button#save"}],optIn:[{click:"button#save"}],optOut:[{if:{exists:"#denyAll"},then:[{click:"#denyAll"},{waitForThenClick:".okButton"}],else:[{waitForThenClick:"#manageSettings"},{waitFor:".purposes-overview-list"},{waitFor:"button#saveAndExit"},{click:"span[role=checkbox][aria-checked=true]",all:!0,optional:!0},{click:"button#saveAndExit"}]}]},{name:"pubtech",prehideSelectors:["#pubtech-cmp"],detectCmp:[{exists:"#pubtech-cmp"}],detectPopup:[{visible:"#pubtech-cmp #pt-actions"}],optIn:[{if:{exists:"#pt-accept-all"},then:[{click:"#pubtech-cmp #pt-actions #pt-accept-all"}],else:[{click:"#pubtech-cmp #pt-actions button:nth-of-type(2)"}]}],optOut:[{click:"#pubtech-cmp #pt-close"}],test:[{eval:"EVAL_PUBTECH_0"}]},{name:"quantcast",prehideSelectors:["#qc-cmp2-main,#qc-cmp2-container"],detectCmp:[{exists:"#qc-cmp2-container"}],detectPopup:[{visible:"#qc-cmp2-ui"}],optOut:[{click:'.qc-cmp2-summary-buttons > button[mode="secondary"]'},{waitFor:"#qc-cmp2-ui"},{click:'.qc-cmp2-toggle-switch > button[aria-checked="true"]',all:!0,optional:!0},{click:'.qc-cmp2-main button[aria-label="REJECT ALL"]',optional:!0},{waitForThenClick:'.qc-cmp2-main button[aria-label="SAVE & EXIT"],.qc-cmp2-buttons-desktop > button[mode="primary"]',timeout:5e3}],optIn:[{click:'.qc-cmp2-summary-buttons > button[mode="primary"]'}]},{name:"reddit.com",runContext:{urlPattern:"^https://www\\.reddit\\.com/"},prehideSelectors:['section:has(a[href^="https://www.reddit.com/policies/cookies"])'],detectCmp:[{exists:'section:has(a[href^="https://www.reddit.com/policies/cookies"])'}],detectPopup:[{visible:'section:has(a[href^="https://www.reddit.com/policies/cookies"])'}],optIn:[{waitForThenClick:"section:has(a[href^=\"https://www.reddit.com/policies/cookies\"]) section[class^='_'] > section:first-child form button"}],optOut:[{waitForThenClick:"section:has(a[href^=\"https://www.reddit.com/policies/cookies\"]) section[class^='_'] > section:last-child form button"}],test:[{eval:"EVAL_REDDIT_0"}]},{name:"samsung.com",runContext:{urlPattern:"^https://www\\.samsung\\.com/"},cosmetic:!0,prehideSelectors:["div.cookie-bar"],detectCmp:[{exists:"div.cookie-bar"}],detectPopup:[{visible:"div.cookie-bar"}],optIn:[{click:"div.cookie-bar__manage > a"}],optOut:[{hide:["div.cookie-bar"]}]},{name:"sibbo",prehideSelectors:["sibbo-cmp-layout"],detectCmp:[{exists:"sibbo-cmp-layout"}],detectPopup:[{visible:"sibbo-cmp-layout"}],optIn:[{click:"sibbo-cmp-layout [data-accept-all]"}],optOut:[{click:'.sibbo-panel__aside__buttons a[data-nav="purposes"]'},{click:'.sibbo-panel__main__header__actions a[data-focusable="reject-all"]'},{if:{exists:"[data-view=purposes] .sibbo-panel__main__footer__actions [data-save-and-exit]"},then:[],else:[{waitFor:'.sibbo-panel__main__footer__actions a[data-focusable="next"]:not(.sibbo-cmp-button--disabled)'},{click:'.sibbo-panel__main__footer__actions a[data-focusable="next"]'},{click:'.sibbo-panel__main div[data-view="purposesLegInt"] a[data-focusable="reject-all"]'}]},{waitFor:".sibbo-panel__main__footer__actions [data-save-and-exit]:not(.sibbo-cmp-button--disabled)"},{click:".sibbo-panel__main__footer__actions [data-save-and-exit]:not(.sibbo-cmp-button--disabled)"}],test:[{eval:"EVAL_SIBBO_0"}]},{name:"similarweb.com",cosmetic:!0,prehideSelectors:[".app-cookies-notification"],detectCmp:[{exists:".app-cookies-notification"}],detectPopup:[{exists:".app-layout .app-cookies-notification"}],optIn:[{click:"button.app-cookies-notification__dismiss"}],optOut:[{hide:[".app-layout .app-cookies-notification"]}]},{name:"Sirdata",prehideSelectors:["#sd-cmp"],detectCmp:[{exists:"#sd-cmp"}],detectPopup:[{visible:"#sd-cmp"}],optIn:[{waitForThenClick:"#sd-cmp .sd-cmp-3cRQ2"}],optOut:[{waitForThenClick:"#sd-cmp .sd-cmp-1pO44"}],test:[{eval:"EVAL_SIRDATA_0"}]},{name:"snigel",detectCmp:[{exists:".snigel-cmp-framework"}],detectPopup:[{visible:".snigel-cmp-framework"}],optOut:[{click:"#sn-b-custom"},{click:"#sn-b-save"}],test:[{eval:"EVAL_SNIGEL_0"}],optIn:[{click:".snigel-cmp-framework #accept-choices"}]},{name:"steampowered.com",detectCmp:[{exists:".cookiepreferences_popup"},{visible:".cookiepreferences_popup"}],detectPopup:[{visible:".cookiepreferences_popup"}],optOut:[{click:"#rejectAllButton"}],optIn:[{click:"#acceptAllButton"}],test:[{wait:1e3},{eval:"EVAL_STEAMPOWERED_0"}]},{name:"takealot.com",cosmetic:!0,prehideSelectors:['div[class^="cookies-banner-module_"]'],detectCmp:[{exists:'div[class^="cookies-banner-module_cookie-banner_"]'}],detectPopup:[{exists:'div[class^="cookies-banner-module_cookie-banner_"]'}],optIn:[{click:'button[class*="cookies-banner-module_dismiss-button_"]'}],optOut:[{hide:['div[class^="cookies-banner-module_"]']},{if:{exists:'div[class^="cookies-banner-module_small-cookie-banner_"]'},then:[{eval:"EVAL_TAKEALOT_0"}],else:[]}]},{name:"tarteaucitron.js",prehideSelectors:["#tarteaucitronRoot"],detectCmp:[{exists:"#tarteaucitronRoot"}],detectPopup:[{visible:"#tarteaucitronRoot #tarteaucitronAlertSmall,#tarteaucitronRoot #tarteaucitronAlertBig",check:"any"}],optIn:[{eval:"EVAL_TARTEAUCITRON_1"}],optOut:[{eval:"EVAL_TARTEAUCITRON_0"}],test:[{eval:"EVAL_TARTEAUCITRON_2",comment:"sometimes there are required categories, so we check that at least something is false"}]},{name:"Tealium",prehideSelectors:["#__tealiumGDPRecModal,#__tealiumGDPRcpPrefs,#consent-layer"],detectCmp:[{visible:"#__tealiumGDPRecModal"},{eval:"EVAL_TEALIUM_0"}],detectPopup:[{visible:"#__tealiumGDPRecModal"}],optOut:[{waitForThenClick:"#cm-acceptNone,.js-accept-essential-cookies",timeout:1e3},{eval:"EVAL_TEALIUM_1"}],optIn:[{hide:["#__tealiumGDPRecModal"]},{eval:"EVAL_TEALIUM_2"}],test:[{eval:"EVAL_TEALIUM_3"}]},{name:"Termly",prehideSelectors:["#termly-code-snippet-support"],detectCmp:[{exists:"#termly-code-snippet-support"}],detectPopup:[{visible:"#termly-code-snippet-support div"}],optIn:[{waitForThenClick:'[data-tid="banner-accept"]'}],optOut:[{if:{exists:'[data-tid="banner-decline"]'},then:[{click:'[data-tid="banner-decline"]'}],else:[{click:".t-preference-button"},{wait:500},{if:{exists:".t-declineAllButton"},then:[{click:".t-declineAllButton"}],else:[{waitForThenClick:".t-preference-modal input[type=checkbox][checked]:not([disabled])",all:!0},{waitForThenClick:".t-saveButton"}]}]}]},{name:"Test page cosmetic CMP",cosmetic:!0,prehideSelectors:["#privacy-test-page-cmp-test-prehide"],detectCmp:[{exists:"#privacy-test-page-cmp-test-banner"}],detectPopup:[{visible:"#privacy-test-page-cmp-test-banner"}],optIn:[{waitFor:"#accept-all"},{click:"#accept-all"}],optOut:[{hide:["#privacy-test-page-cmp-test-banner"]}],test:[{wait:500},{eval:"EVAL_TESTCMP_COSMETIC_0"}]},{name:"Test page CMP",prehideSelectors:["#reject-all"],detectCmp:[{exists:"#privacy-test-page-cmp-test"}],detectPopup:[{visible:"#privacy-test-page-cmp-test"}],optIn:[{waitFor:"#accept-all"},{click:"#accept-all"}],optOut:[{waitFor:"#reject-all"},{click:"#reject-all"}],test:[{eval:"EVAL_TESTCMP_0"}]},{name:"thalia.de",prehideSelectors:[".consent-banner-box"],detectCmp:[{exists:"consent-banner[component=consent-banner]"}],detectPopup:[{visible:".consent-banner-box"}],optIn:[{click:".button-zustimmen"}],optOut:[{click:"button[data-consent=disagree]"}]},{name:"thefreedictionary.com",prehideSelectors:["#cmpBanner"],detectCmp:[{exists:"#cmpBanner"}],detectPopup:[{visible:"#cmpBanner"}],optIn:[{eval:"EVAL_THEFREEDICTIONARY_1"}],optOut:[{eval:"EVAL_THEFREEDICTIONARY_0"}]},{name:"theverge",runContext:{frame:!1,main:!0,urlPattern:"^https://(www)?\\.theverge\\.com"},intermediate:!1,prehideSelectors:[".duet--cta--cookie-banner"],detectCmp:[{exists:".duet--cta--cookie-banner"}],detectPopup:[{visible:".duet--cta--cookie-banner"}],optIn:[{click:".duet--cta--cookie-banner button.tracking-12",all:!1}],optOut:[{click:".duet--cta--cookie-banner button.tracking-12 > span"}],test:[{eval:"EVAL_THEVERGE_0"}]},{name:"tidbits-com",cosmetic:!0,prehideSelectors:["#eu_cookie_law_widget-2"],detectCmp:[{exists:"#eu_cookie_law_widget-2"}],detectPopup:[{visible:"#eu_cookie_law_widget-2"}],optIn:[{click:"#eu-cookie-law form > input.accept"}],optOut:[{hide:["#eu_cookie_law_widget-2"]}]},{name:"tractor-supply",runContext:{urlPattern:"^https://www\\.tractorsupply\\.com/"},cosmetic:!0,prehideSelectors:[".tsc-cookie-banner"],detectCmp:[{exists:".tsc-cookie-banner"}],detectPopup:[{visible:".tsc-cookie-banner"}],optIn:[{click:"#cookie-banner-cancel"}],optOut:[{hide:[".tsc-cookie-banner"]}]},{name:"trader-joes-com",cosmetic:!0,prehideSelectors:['div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]'],detectCmp:[{exists:'div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]'}],detectPopup:[{visible:'div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]'}],optIn:[{click:'div[class^="CookiesAlert_cookiesAlert__container__"] button'}],optOut:[{hide:['div.aem-page > div[class^="CookiesAlert_cookiesAlert__"]']}]},{name:"tropicfeel-com",prehideSelectors:["#shopify-section-cookies-controller"],detectCmp:[{exists:"#shopify-section-cookies-controller"}],detectPopup:[{visible:"#shopify-section-cookies-controller #cookies-controller-main-pane",check:"any"}],optIn:[{waitForThenClick:"#cookies-controller-main-pane form[data-form-allow-all] button"}],optOut:[{click:"#cookies-controller-main-pane a[data-tab-target=manage-cookies]"},{waitFor:"#manage-cookies-pane.active"},{click:"#manage-cookies-pane.active input[type=checkbox][checked]:not([disabled])",all:!0},{click:"#manage-cookies-pane.active button[type=submit]"}],test:[]},{name:"true-car",runContext:{urlPattern:"^https://www\\.truecar\\.com/"},cosmetic:!0,prehideSelectors:[['div[aria-labelledby="cookie-banner-heading"]']],detectCmp:[{exists:'div[aria-labelledby="cookie-banner-heading"]'}],detectPopup:[{visible:'div[aria-labelledby="cookie-banner-heading"]'}],optIn:[{click:'div[aria-labelledby="cookie-banner-heading"] > button[aria-label="Close"]'}],optOut:[{hide:['div[aria-labelledby="cookie-banner-heading"]']}]},{name:"truyo",prehideSelectors:["#truyo-consent-module"],detectCmp:[{exists:"#truyo-cookieBarContent"}],detectPopup:[{visible:"#truyo-consent-module"}],optIn:[{click:"button#acceptAllCookieButton"}],optOut:[{click:"button#declineAllCookieButton"}]},{name:"tumblr-com",cosmetic:!0,prehideSelectors:["#cmp-app-container"],detectCmp:[{exists:"#cmp-app-container"}],detectPopup:[{visible:"#cmp-app-container"}],optIn:[{click:"#tumblr #cmp-app-container div.components-modal__frame > iframe > html body > div > div > div.cmp__dialog-footer > div > button.components-button.white-space-normal.is-primary"}],optOut:[{hide:["#cmp-app-container"]}]},{name:"twitch.tv",runContext:{urlPattern:"^https?://(www\\.)?twitch\\.tv"},prehideSelectors:["div:has(> .consent-banner .consent-banner__content--gdpr-v2),.ReactModalPortal:has([data-a-target=consent-modal-save])"],detectCmp:[{exists:".consent-banner .consent-banner__content--gdpr-v2"}],detectPopup:[{visible:".consent-banner .consent-banner__content--gdpr-v2"}],optIn:[{click:'button[data-a-target="consent-banner-accept"]'}],optOut:[{hide:["div:has(> .consent-banner .consent-banner__content--gdpr-v2)"]},{click:'button[data-a-target="consent-banner-manage-preferences"]'},{waitFor:"input[type=checkbox][data-a-target=tw-checkbox]"},{click:"input[type=checkbox][data-a-target=tw-checkbox][checked]:not([disabled])",all:!0,optional:!0},{waitForThenClick:"[data-a-target=consent-modal-save]"},{waitForVisible:".ReactModalPortal:has([data-a-target=consent-modal-save])",check:"none"}]},{name:"twitter",runContext:{urlPattern:"^https://([a-z0-9-]+\\.)?twitter\\.com/"},prehideSelectors:['[data-testid="BottomBar"]'],detectCmp:[{exists:'[data-testid="BottomBar"] div'}],detectPopup:[{visible:'[data-testid="BottomBar"] div'}],optIn:[{waitForThenClick:'[data-testid="BottomBar"] > div:has(>div:first-child>div:last-child>span[role=button]) > div:last-child > div[role=button]:first-child'}],optOut:[{waitForThenClick:'[data-testid="BottomBar"] > div:has(>div:first-child>div:last-child>span[role=button]) > div:last-child > div[role=button]:last-child'}],TODOtest:[{eval:"EVAL_document.cookie.includes('d_prefs=MjoxLGNvbnNlbnRfdmVyc2lvbjoy')"}]},{name:"ubuntu.com",prehideSelectors:["dialog.cookie-policy"],detectCmp:[{any:[{exists:"dialog.cookie-policy header"},{exists:'xpath///*[@id="modal"]/div/header'}]}],detectPopup:[{any:[{visible:"dialog header"},{visible:'xpath///*[@id="modal"]/div/header'}]}],optIn:[{any:[{waitForThenClick:"#cookie-policy-button-accept"},{waitForThenClick:'xpath///*[@id="cookie-policy-button-accept"]'}]}],optOut:[{any:[{waitForThenClick:"button.p-button"},{waitForThenClick:'xpath///*[@id="cookie-policy-content"]/p[4]/button[2]'}]},{waitForThenClick:".p-switch__input:checked",optional:!0,all:!0},{any:[{waitForThenClick:"div > button"},{waitForThenClick:'xpath///*[@id="modal"]/div/button'}]}],test:[{eval:"EVAL_UBUNTU_COM_0"}]},{name:"UK Cookie Consent",prehideSelectors:["#catapult-cookie-bar"],cosmetic:!0,detectCmp:[{exists:"#catapult-cookie-bar"}],detectPopup:[{exists:".has-cookie-bar #catapult-cookie-bar"}],optIn:[{click:"#catapultCookie"}],optOut:[{hide:["#catapult-cookie-bar"]}],test:[{eval:"EVAL_UK_COOKIE_CONSENT_0"}]},{name:"urbanarmorgear-com",cosmetic:!0,prehideSelectors:['div[class^="Layout__CookieBannerContainer-"]'],detectCmp:[{exists:'div[class^="Layout__CookieBannerContainer-"]'}],detectPopup:[{visible:'div[class^="Layout__CookieBannerContainer-"]'}],optIn:[{click:'button[class^="CookieBanner__AcceptButton"]'}],optOut:[{hide:['div[class^="Layout__CookieBannerContainer-"]']}]},{name:"usercentrics-api",detectCmp:[{exists:"#usercentrics-root"}],detectPopup:[{eval:"EVAL_USERCENTRICS_API_0"},{exists:["#usercentrics-root","[data-testid=uc-container]"]}],optIn:[{eval:"EVAL_USERCENTRICS_API_3"},{eval:"EVAL_USERCENTRICS_API_1"},{eval:"EVAL_USERCENTRICS_API_5"}],optOut:[{eval:"EVAL_USERCENTRICS_API_1"},{eval:"EVAL_USERCENTRICS_API_2"}],test:[{eval:"EVAL_USERCENTRICS_API_6"}]},{name:"usercentrics-button",detectCmp:[{exists:"#usercentrics-button"}],detectPopup:[{visible:"#usercentrics-button #uc-btn-accept-banner"}],optIn:[{click:"#usercentrics-button #uc-btn-accept-banner"}],optOut:[{click:"#usercentrics-button #uc-btn-deny-banner"}],test:[{eval:"EVAL_USERCENTRICS_BUTTON_0"}]},{name:"uswitch.com",prehideSelectors:["#cookie-banner-wrapper"],detectCmp:[{exists:"#cookie-banner-wrapper"}],detectPopup:[{visible:"#cookie-banner-wrapper"}],optIn:[{click:"#cookie_banner_accept_mobile"}],optOut:[{click:"#cookie_banner_save"}]},{name:"vodafone.de",runContext:{urlPattern:"^https://www\\.vodafone\\.de/"},prehideSelectors:[".dip-consent,.dip-consent-container"],detectCmp:[{exists:".dip-consent-container"}],detectPopup:[{visible:".dip-consent-content"}],optOut:[{click:'.dip-consent-btn[tabindex="2"]'}],optIn:[{click:'.dip-consent-btn[tabindex="1"]'}]},{name:"waitrose.com",prehideSelectors:["div[aria-labelledby=CookieAlertModalHeading]","section[data-test=initial-waitrose-cookie-consent-banner]","section[data-test=cookie-consent-modal]"],detectCmp:[{exists:"section[data-test=initial-waitrose-cookie-consent-banner]"}],detectPopup:[{visible:"section[data-test=initial-waitrose-cookie-consent-banner]"}],optIn:[{click:"button[data-test=accept-all]"}],optOut:[{click:"button[data-test=manage-cookies]"},{wait:200},{eval:"EVAL_WAITROSE_0"},{click:"button[data-test=submit]"}],test:[{eval:"EVAL_WAITROSE_1"}]},{name:"wetransfer.com",detectCmp:[{exists:".welcome__cookie-notice"}],detectPopup:[{visible:".welcome__cookie-notice"}],optIn:[{click:".welcome__button--accept"}],optOut:[{click:".welcome__button--decline"}]},{name:"whitepages.com",runContext:{urlPattern:"^https://www\\.whitepages\\.com/"},cosmetic:!0,prehideSelectors:[".cookie-wrapper, .cookie-overlay"],detectCmp:[{exists:".cookie-wrapper"}],detectPopup:[{visible:".cookie-overlay"}],optIn:[{click:'button[aria-label="Got it"]'}],optOut:[{hide:[".cookie-wrapper"]}]},{name:"woo-commerce-com",prehideSelectors:[".wccom-comp-privacy-banner .wccom-privacy-banner"],detectCmp:[{exists:".wccom-comp-privacy-banner .wccom-privacy-banner"}],detectPopup:[{exists:".wccom-comp-privacy-banner .wccom-privacy-banner"}],optIn:[{click:".wccom-privacy-banner__content-buttons button.is-primary"}],optOut:[{click:".wccom-privacy-banner__content-buttons button.is-secondary"},{waitForThenClick:"input[type=checkbox][checked]:not([disabled])",all:!0},{click:"div.wccom-modal__footer > button"}]},{name:"WP Cookie Notice for GDPR",comment:"https://wordpress.org/plugins/gdpr-cookie-consent/",prehideSelectors:["#gdpr-cookie-consent-bar"],detectCmp:[{exists:"#gdpr-cookie-consent-bar"}],detectPopup:[{visible:"#gdpr-cookie-consent-bar"}],optIn:[{waitForThenClick:"#gdpr-cookie-consent-bar #cookie_action_accept"}],optOut:[{waitForThenClick:"#gdpr-cookie-consent-bar #cookie_action_reject"}],test:[{eval:"EVAL_WP_COOKIE_NOTICE_0"}]},{name:"wpcc",cosmetic:!0,prehideSelectors:[".wpcc-container"],detectCmp:[{exists:".wpcc-container"}],detectPopup:[{exists:".wpcc-container .wpcc-message"}],optIn:[{click:".wpcc-compliance .wpcc-btn"}],optOut:[{hide:[".wpcc-container"]}]},{name:"xhamster-eu",prehideSelectors:[".cookies-modal"],detectCmp:[{exists:".cookies-modal"}],detectPopup:[{exists:".cookies-modal"}],optIn:[{click:"button.cmd-button-accept-all"}],optOut:[{click:"button.cmd-button-reject-all"}]},{name:"xhamster-us",runContext:{urlPattern:"^https://(www\\.)?xhamster\\d?\\.com"},cosmetic:!0,prehideSelectors:[".cookie-announce"],detectCmp:[{exists:".cookie-announce"}],detectPopup:[{visible:".cookie-announce .announce-text"}],optIn:[{click:".cookie-announce button.xh-button"}],optOut:[{hide:[".cookie-announce"]}]},{name:"xing.com",detectCmp:[{exists:"div[class^=cookie-consent-CookieConsent]"}],detectPopup:[{exists:"div[class^=cookie-consent-CookieConsent]"}],optIn:[{click:"#consent-accept-button"}],optOut:[{click:"#consent-settings-button"},{click:".consent-banner-button-accept-overlay"}],test:[{eval:"EVAL_XING_0"}]},{name:"xnxx-com",cosmetic:!0,prehideSelectors:["#cookies-use-alert"],detectCmp:[{exists:"#cookies-use-alert"}],detectPopup:[{visible:"#cookies-use-alert"}],optIn:[{click:"#cookies-use-alert .close"}],optOut:[{hide:["#cookies-use-alert"]}]},{name:"youporn.com",cosmetic:!0,prehideSelectors:[".euCookieModal, #js_euCookieModal"],detectCmp:[{exists:".euCookieModal"}],detectPopup:[{exists:".euCookieModal, #js_euCookieModal"}],optIn:[{click:'button[name="user_acceptCookie"]'}],optOut:[{hide:[".euCookieModal"]}]},{name:"youtube-desktop",prehideSelectors:["tp-yt-iron-overlay-backdrop.opened","ytd-consent-bump-v2-lightbox"],detectCmp:[{exists:"ytd-consent-bump-v2-lightbox tp-yt-paper-dialog"},{exists:'ytd-consent-bump-v2-lightbox tp-yt-paper-dialog a[href^="https://consent.youtube.com/"]'}],detectPopup:[{visible:"ytd-consent-bump-v2-lightbox tp-yt-paper-dialog"}],optIn:[{waitForThenClick:"ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:last-child #button,ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:last-child button"},{wait:500}],optOut:[{waitForThenClick:"ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:first-child #button,ytd-consent-bump-v2-lightbox .eom-buttons .eom-button-row:first-child ytd-button-renderer:first-child button"},{wait:500}],test:[{wait:500},{eval:"EVAL_YOUTUBE_DESKTOP_0"}]},{name:"youtube-mobile",prehideSelectors:[".consent-bump-v2-lightbox"],detectCmp:[{exists:"ytm-consent-bump-v2-renderer"}],detectPopup:[{visible:"ytm-consent-bump-v2-renderer"}],optIn:[{waitForThenClick:"ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons c3-material-button:first-child button, ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons ytm-button-renderer:first-child button"},{wait:500}],optOut:[{waitForThenClick:"ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons c3-material-button:nth-child(2) button, ytm-consent-bump-v2-renderer .privacy-terms + .one-col-dialog-buttons ytm-button-renderer:nth-child(2) button"},{wait:500}],test:[{wait:500},{eval:"EVAL_YOUTUBE_MOBILE_0"}]},{name:"zdf",prehideSelectors:["#zdf-cmp-banner-sdk"],detectCmp:[{exists:"#zdf-cmp-banner-sdk"}],detectPopup:[{visible:"#zdf-cmp-main.zdf-cmp-show"}],optIn:[{waitForThenClick:"#zdf-cmp-main #zdf-cmp-accept-btn"}],optOut:[{waitForThenClick:"#zdf-cmp-main #zdf-cmp-deny-btn"}],test:[]}],T={"didomi.io":{detectors:[{presentMatcher:{target:{selector:"#didomi-host, #didomi-notice"},type:"css"},showingMatcher:{target:{selector:"body.didomi-popup-open, .didomi-notice-banner"},type:"css"}}],methods:[{action:{target:{selector:".didomi-popup-notice-buttons .didomi-button:not(.didomi-button-highlight), .didomi-notice-banner .didomi-learn-more-button"},type:"click"},name:"OPEN_OPTIONS"},{action:{actions:[{retries:50,target:{selector:"#didomi-purpose-cookies"},type:"waitcss",waitTime:50},{consents:[{description:"Share (everything) with others",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-share_whith_others]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-share_whith_others]:last-child"},type:"click"},type:"X"},{description:"Information storage and access",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-cookies]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-cookies]:last-child"},type:"click"},type:"D"},{description:"Content selection, offers and marketing",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-CL-T1Rgm7]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-CL-T1Rgm7]:last-child"},type:"click"},type:"E"},{description:"Analytics",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-analytics]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-analytics]:last-child"},type:"click"},type:"B"},{description:"Analytics",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-M9NRHJe3G]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-M9NRHJe3G]:last-child"},type:"click"},type:"B"},{description:"Ad and content selection",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-advertising_personalization]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-advertising_personalization]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection",falseAction:{parent:{childFilter:{target:{selector:"#didomi-purpose-pub-ciblee"}},selector:".didomi-consent-popup-data-processing, .didomi-components-accordion-label-container"},target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-pub-ciblee]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-pub-ciblee]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - basics",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-q4zlJqdcD]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-q4zlJqdcD]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - partners and subsidiaries",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-partenaire-cAsDe8jC]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-partenaire-cAsDe8jC]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - social networks",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-p4em9a8m]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-p4em9a8m]:last-child"},type:"click"},type:"F"},{description:"Ad and content selection - others",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-autres-pub]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-autres-pub]:last-child"},type:"click"},type:"F"},{description:"Social networks",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-reseauxsociaux]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-reseauxsociaux]:last-child"},type:"click"},type:"A"},{description:"Social networks",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-social_media]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-social_media]:last-child"},type:"click"},type:"A"},{description:"Content selection",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-content_personalization]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-content_personalization]:last-child"},type:"click"},type:"E"},{description:"Ad delivery",falseAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-ad_delivery]:first-child"},type:"click"},trueAction:{target:{selector:".didomi-components-radio__option[aria-describedby=didomi-purpose-ad_delivery]:last-child"},type:"click"},type:"F"}],type:"consent"},{action:{consents:[{matcher:{childFilter:{target:{selector:":not(.didomi-components-radio__option--selected)"}},type:"css"},trueAction:{target:{selector:":nth-child(2)"},type:"click"},falseAction:{target:{selector:":first-child"},type:"click"},type:"X"}],type:"consent"},target:{selector:".didomi-components-radio"},type:"foreach"}],type:"list"},name:"DO_CONSENT"},{action:{parent:{selector:".didomi-consent-popup-footer .didomi-consent-popup-actions"},target:{selector:".didomi-components-button:first-child"},type:"click"},name:"SAVE_CONSENT"}]},oil:{detectors:[{presentMatcher:{target:{selector:".as-oil-content-overlay"},type:"css"},showingMatcher:{target:{selector:".as-oil-content-overlay"},type:"css"}}],methods:[{action:{actions:[{target:{selector:".as-js-advanced-settings"},type:"click"},{retries:"10",target:{selector:".as-oil-cpc__purpose-container"},type:"waitcss",waitTime:"250"}],type:"list"},name:"OPEN_OPTIONS"},{action:{actions:[{consents:[{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Information storage and access","Opbevaring af og adgang til oplysninger på din enhed"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Information storage and access","Opbevaring af og adgang til oplysninger på din enhed"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"D"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personlige annoncer","Personalisation"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personlige annoncer","Personalisation"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"E"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Annoncevalg, levering og rapportering","Ad selection, delivery, reporting"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Annoncevalg, levering og rapportering","Ad selection, delivery, reporting"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"F"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personalisering af indhold","Content selection, delivery, reporting"]},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:["Personalisering af indhold","Content selection, delivery, reporting"]},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"E"},{matcher:{parent:{childFilter:{target:{selector:".as-oil-cpc__purpose-header",textFilter:["Måling","Measurement"]}},selector:".as-oil-cpc__purpose-container"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{childFilter:{target:{selector:".as-oil-cpc__purpose-header",textFilter:["Måling","Measurement"]}},selector:".as-oil-cpc__purpose-container"},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"B"},{matcher:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:"Google"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".as-oil-cpc__purpose-container",textFilter:"Google"},target:{selector:".as-oil-cpc__switch"},type:"click"},type:"F"}],type:"consent"}],type:"list"},name:"DO_CONSENT"},{action:{target:{selector:".as-oil__btn-optin"},type:"click"},name:"SAVE_CONSENT"},{action:{target:{selector:"div.as-oil"},type:"hide"},name:"HIDE_CMP"}]},optanon:{detectors:[{presentMatcher:{target:{selector:"#optanon-menu, .optanon-alert-box-wrapper"},type:"css"},showingMatcher:{target:{displayFilter:!0,selector:".optanon-alert-box-wrapper"},type:"css"}}],methods:[{action:{actions:[{target:{selector:".optanon-alert-box-wrapper .optanon-toggle-display, a[onclick*='OneTrust.ToggleInfoDisplay()'], a[onclick*='Optanon.ToggleInfoDisplay()']"},type:"click"}],type:"list"},name:"OPEN_OPTIONS"},{action:{actions:[{target:{selector:".preference-menu-item #Your-privacy"},type:"click"},{target:{selector:"#optanon-vendor-consent-text"},type:"click"},{action:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"X"}],type:"consent"},target:{selector:"#optanon-vendor-consent-list .vendor-item"},type:"foreach"},{target:{selector:".vendor-consent-back-link"},type:"click"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-performance"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-performance"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-functional"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-functional"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-advertising"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-advertising"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-social"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-social"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Social Media Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Social Media Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalisation"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalisation"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Site monitoring cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Site monitoring cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Third party privacy-enhanced content"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Third party privacy-enhanced content"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"X"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Performance & Advertising Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Performance & Advertising Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Information storage and access"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Information storage and access"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"D"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad selection, delivery, reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad selection, delivery, reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content selection, delivery, reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content selection, delivery, reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Measurement"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Measurement"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Recommended Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Recommended Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"X"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Unclassified Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Unclassified Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"X"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Analytical Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Analytical Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"B"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Marketing Cookies"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Marketing Cookies"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalization"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Personalization"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad Selection, Delivery & Reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Ad Selection, Delivery & Reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},type:"ifcss"},{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content Selection, Delivery & Reporting"},trueAction:{actions:[{parent:{selector:"#optanon-menu, .optanon-menu"},target:{selector:".menu-item-necessary",textFilter:"Content Selection, Delivery & Reporting"},type:"click"},{consents:[{matcher:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status input"},type:"checkbox"},toggleAction:{parent:{selector:"#optanon-popup-body-right"},target:{selector:".optanon-status label"},type:"click"},type:"E"}],type:"consent"}],type:"list"},type:"ifcss"}],type:"list"},name:"DO_CONSENT"},{action:{parent:{selector:".optanon-save-settings-button"},target:{selector:".optanon-white-button-middle"},type:"click"},name:"SAVE_CONSENT"},{action:{actions:[{target:{selector:"#optanon-popup-wrapper"},type:"hide"},{target:{selector:"#optanon-popup-bg"},type:"hide"},{target:{selector:".optanon-alert-box-wrapper"},type:"hide"}],type:"list"},name:"HIDE_CMP"}]},quantcast2:{detectors:[{presentMatcher:{target:{selector:"[data-tracking-opt-in-overlay]"},type:"css"},showingMatcher:{target:{selector:"[data-tracking-opt-in-overlay] [data-tracking-opt-in-learn-more]"},type:"css"}}],methods:[{action:{target:{selector:"[data-tracking-opt-in-overlay] [data-tracking-opt-in-learn-more]"},type:"click"},name:"OPEN_OPTIONS"},{action:{actions:[{type:"wait",waitTime:500},{action:{actions:[{target:{selector:"div",textFilter:["Information storage and access"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"D"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Personalization"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"F"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Ad selection, delivery, reporting"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"F"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Content selection, delivery, reporting"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"E"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Measurement"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"B"}],type:"consent"},type:"ifcss"},{target:{selector:"div",textFilter:["Other Partners"]},trueAction:{consents:[{matcher:{target:{selector:"input"},type:"checkbox"},toggleAction:{target:{selector:"label"},type:"click"},type:"X"}],type:"consent"},type:"ifcss"}],type:"list"},parent:{childFilter:{target:{selector:"input"}},selector:"[data-tracking-opt-in-overlay] > div > div"},target:{childFilter:{target:{selector:"input"}},selector:":scope > div"},type:"foreach"}],type:"list"},name:"DO_CONSENT"},{action:{target:{selector:"[data-tracking-opt-in-overlay] [data-tracking-opt-in-save]"},type:"click"},name:"SAVE_CONSENT"}]},springer:{detectors:[{presentMatcher:{parent:null,target:{selector:".cmp-app_gdpr"},type:"css"},showingMatcher:{parent:null,target:{displayFilter:!0,selector:".cmp-popup_popup"},type:"css"}}],methods:[{action:{actions:[{target:{selector:".cmp-intro_rejectAll"},type:"click"},{type:"wait",waitTime:250},{target:{selector:".cmp-purposes_purposeItem:not(.cmp-purposes_selectedPurpose)"},type:"click"}],type:"list"},name:"OPEN_OPTIONS"},{action:{consents:[{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Przechowywanie informacji na urządzeniu lub dostęp do nich",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Przechowywanie informacji na urządzeniu lub dostęp do nich",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"D"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór podstawowych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór podstawowych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"F"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"F"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"E"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Tworzenie profilu spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"E"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Wybór spersonalizowanych treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"B"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności reklam",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"B"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Pomiar wydajności treści",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"B"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Stosowanie badań rynkowych w celu generowania opinii odbiorców",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Stosowanie badań rynkowych w celu generowania opinii odbiorców",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"X"},{matcher:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Opracowywanie i ulepszanie produktów",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch .cmp-switch_isSelected"},type:"css"},toggleAction:{parent:{selector:".cmp-purposes_detailHeader",textFilter:"Opracowywanie i ulepszanie produktów",childFilter:{target:{selector:".cmp-switch_switch"}}},target:{selector:".cmp-switch_switch:not(.cmp-switch_isSelected)"},type:"click"},type:"X"}],type:"consent"},name:"DO_CONSENT"},{action:{target:{selector:".cmp-details_save"},type:"click"},name:"SAVE_CONSENT"}]},wordpressgdpr:{detectors:[{presentMatcher:{parent:null,target:{selector:".wpgdprc-consent-bar"},type:"css"},showingMatcher:{parent:null,target:{displayFilter:!0,selector:".wpgdprc-consent-bar"},type:"css"}}],methods:[{action:{parent:null,target:{selector:".wpgdprc-consent-bar .wpgdprc-consent-bar__settings",textFilter:null},type:"click"},name:"OPEN_OPTIONS"},{action:{actions:[{target:{selector:".wpgdprc-consent-modal .wpgdprc-button",textFilter:"Eyeota"},type:"click"},{consents:[{description:"Eyeota Cookies",matcher:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Eyeota"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Eyeota"},target:{selector:"label"},type:"click"},type:"X"}],type:"consent"},{target:{selector:".wpgdprc-consent-modal .wpgdprc-button",textFilter:"Advertising"},type:"click"},{consents:[{description:"Advertising Cookies",matcher:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Advertising"},target:{selector:"input"},type:"checkbox"},toggleAction:{parent:{selector:".wpgdprc-consent-modal__description",textFilter:"Advertising"},target:{selector:"label"},type:"click"},type:"F"}],type:"consent"}],type:"list"},name:"DO_CONSENT"},{action:{parent:null,target:{selector:".wpgdprc-button",textFilter:"Save my settings"},type:"click"},name:"SAVE_CONSENT"}]}},F={autoconsent:P,consentomatic:T},L=Object.freeze({__proto__:null,autoconsent:P,consentomatic:T,default:F});const N=new class{constructor(e,t=null,o=null){if(this.id=y(),this.rules=[],this.foundCmp=null,this.state={lifecycle:"loading",prehideOn:!1,findCmpAttempts:0,detectedCmps:[],detectedPopups:[],selfTest:null},C.sendContentMessage=e,this.sendContentMessage=e,this.rules=[],this.updateState({lifecycle:"loading"}),this.addDynamicRules(),t)this.initialize(t,o);else{o&&this.parseDeclarativeRules(o);e({type:"init",url:window.location.href}),this.updateState({lifecycle:"waitingForInitResponse"})}}initialize(e,t){if(this.config=e,e.enabled){if(t&&this.parseDeclarativeRules(t),this.rules=function(e,t){return e.filter((e=>(!t.disabledCmps||!t.disabledCmps.includes(e.name))&&(t.enableCosmeticRules||!e.isCosmetic)))}(this.rules,e),e.enablePrehide)if(document.documentElement)this.prehideElements();else{const e=()=>{window.removeEventListener("DOMContentLoaded",e),this.prehideElements()};window.addEventListener("DOMContentLoaded",e)}if("loading"===document.readyState){const e=()=>{window.removeEventListener("DOMContentLoaded",e),this.start()};window.addEventListener("DOMContentLoaded",e)}else this.start();this.updateState({lifecycle:"initialized"})}}addDynamicRules(){I.forEach((e=>{this.rules.push(new e(this))}))}parseDeclarativeRules(e){Object.keys(e.consentomatic).forEach((t=>{this.addConsentomaticCMP(t,e.consentomatic[t])})),e.autoconsent.forEach((e=>{this.addDeclarativeCMP(e)}))}addDeclarativeCMP(e){this.rules.push(new E(e,this))}addConsentomaticCMP(e,t){this.rules.push(new O(`com_${e}`,t))}start(){window.requestIdleCallback?window.requestIdleCallback((()=>this._start()),{timeout:500}):this._start()}async _start(){this.updateState({lifecycle:"started"});const e=await this.findCmp(this.config.detectRetries);if(this.updateState({detectedCmps:e.map((e=>e.name))}),0===e.length)return this.config.enablePrehide&&this.undoPrehide(),this.updateState({lifecycle:"nothingDetected"}),!1;this.updateState({lifecycle:"cmpDetected"});let t=await this.detectPopups(e.filter((e=>!e.isCosmetic)));if(0===t.length&&(t=await this.detectPopups(e.filter((e=>e.isCosmetic)))),0===t.length)return this.config.enablePrehide&&this.undoPrehide(),!1;if(this.updateState({lifecycle:"openPopupDetected"}),this.config.enablePrehide&&!this.state.prehideOn&&this.prehideElements(),t.length>1){const e={msg:"Found multiple CMPs, check the detection rules.",cmps:t.map((e=>e.name))};this.sendContentMessage({type:"autoconsentError",details:e})}return this.foundCmp=t[0],"optOut"===this.config.autoAction?await this.doOptOut():"optIn"!==this.config.autoAction||await this.doOptIn()}async findCmp(e){this.updateState({findCmpAttempts:this.state.findCmpAttempts+1});const t=[];for(const e of this.rules)try{if(!e.checkRunContext())continue;await e.detectCmp()&&(this.sendContentMessage({type:"cmpDetected",url:location.href,cmp:e.name}),t.push(e))}catch(e){}return 0===t.length&&e>0?(await b(500),this.findCmp(e-1)):t}async detectPopups(e){const t=[],o=e.map((e=>this.waitForPopup(e).then((o=>{o&&(this.updateState({detectedPopups:this.state.detectedPopups.concat([e.name])}),this.sendContentMessage({type:"popupFound",cmp:e.name,url:location.href}),t.push(e))})).catch((e=>null))));return await Promise.all(o),t}async doOptOut(){let e;return this.updateState({lifecycle:"runningOptOut"}),e=!!this.foundCmp&&await this.foundCmp.optOut(),this.config.enablePrehide&&this.undoPrehide(),this.sendContentMessage({type:"optOutResult",cmp:this.foundCmp?this.foundCmp.name:"none",result:e,scheduleSelfTest:this.foundCmp&&this.foundCmp.hasSelfTest,url:location.href}),e&&!this.foundCmp.isIntermediate?(this.sendContentMessage({type:"autoconsentDone",cmp:this.foundCmp.name,isCosmetic:this.foundCmp.isCosmetic,url:location.href}),this.updateState({lifecycle:"done"})):this.updateState({lifecycle:e?"optOutSucceeded":"optOutFailed"}),e}async doOptIn(){let e;return this.updateState({lifecycle:"runningOptIn"}),e=!!this.foundCmp&&await this.foundCmp.optIn(),this.config.enablePrehide&&this.undoPrehide(),this.sendContentMessage({type:"optInResult",cmp:this.foundCmp?this.foundCmp.name:"none",result:e,scheduleSelfTest:!1,url:location.href}),e&&!this.foundCmp.isIntermediate?(this.sendContentMessage({type:"autoconsentDone",cmp:this.foundCmp.name,isCosmetic:this.foundCmp.isCosmetic,url:location.href}),this.updateState({lifecycle:"done"})):this.updateState({lifecycle:e?"optInSucceeded":"optInFailed"}),e}async doSelfTest(){let e;return e=!!this.foundCmp&&await this.foundCmp.test(),this.sendContentMessage({type:"selfTestResult",cmp:this.foundCmp?this.foundCmp.name:"none",result:e,url:location.href}),this.updateState({selfTest:e}),e}async waitForPopup(e,t=5,o=500){const c=await e.detectPopup().catch((e=>!1));return!c&&t>0?(await b(o),this.waitForPopup(e,t-1,o)):c}prehideElements(){const e=this.rules.reduce(((e,t)=>t.prehideSelectors?[...e,...t.prehideSelectors]:e),["#didomi-popup,.didomi-popup-container,.didomi-popup-notice,.didomi-consent-popup-preferences,#didomi-notice,.didomi-popup-backdrop,.didomi-screen-medium"]);return this.updateState({prehideOn:!0}),setTimeout((()=>{this.config.enablePrehide&&this.state.prehideOn&&!["runningOptOut","runningOptIn"].includes(this.state.lifecycle)&&this.undoPrehide()}),this.config.prehideTimeout||2e3),function(e){return s(a("autoconsent-prehide"),e,"opacity")}(e)}undoPrehide(){return this.updateState({prehideOn:!1}),function(){const e=a("autoconsent-prehide");return e&&e.remove(),!!e}()}updateState(e){Object.assign(this.state,e),this.sendContentMessage({type:"report",instanceId:this.id,url:window.location.href,mainFrame:window.top===window.self,state:this.state})}async receiveMessageCallback(e){switch(e.type){case"initResp":this.initialize(e.config,e.rules);break;case"optIn":await this.doOptIn();break;case"optOut":await this.doOptOut();break;case"selfTest":await this.doSelfTest();break;case"evalResp":!function(e,t){const o=C.pending.get(e);o?(C.pending.delete(e),o.timer&&window.clearTimeout(o.timer),o.resolve(t)):console.warn("no eval #",e)}(e.id,e.result)}}}((e=>{window.webkit.messageHandlers[e.type]&&window.webkit.messageHandlers[e.type].postMessage(e).then((e=>{N.receiveMessageCallback(e)}))}),null,L);window.autoconsentMessageCallback=e=>{N.receiveMessageCallback(e)}}(); diff --git a/DuckDuckGo/AutofillLoginListViewModel.swift b/DuckDuckGo/AutofillLoginListViewModel.swift index ae9642b488..2295c54873 100644 --- a/DuckDuckGo/AutofillLoginListViewModel.swift +++ b/DuckDuckGo/AutofillLoginListViewModel.swift @@ -45,6 +45,11 @@ internal enum AutofillLoginListSectionType: Comparable { static let miscSectionHeading = "#" } +internal enum EnableAutofillRows: Int, CaseIterable { + case toggleAutofill + case resetNeverPromptWebsites +} + final class AutofillLoginListViewModel: ObservableObject { enum ViewState { @@ -66,10 +71,12 @@ final class AutofillLoginListViewModel: ObservableObject { private let tld: TLD private var currentTabUrl: URL? private let secureVault: (any AutofillSecureVault)? + private let autofillNeverPromptWebsitesManager: AutofillNeverPromptWebsitesManager private var cachedDeletedCredentials: SecureVaultModels.WebsiteCredentials? private let autofillDomainNameUrlMatcher = AutofillDomainNameUrlMatcher() private let autofillDomainNameUrlSort = AutofillDomainNameUrlSort() + @Published private (set) var viewState: AutofillLoginListViewModel.ViewState = .authLocked @Published private(set) var sections = [AutofillLoginListSectionType]() { didSet { @@ -89,11 +96,13 @@ final class AutofillLoginListViewModel: ObservableObject { } } - init(appSettings: AppSettings, tld: TLD, secureVault: (any AutofillSecureVault)?, currentTabUrl: URL? = nil) { + init(appSettings: AppSettings, tld: TLD, secureVault: (any AutofillSecureVault)?, currentTabUrl: URL? = nil, autofillNeverPromptWebsitesManager: AutofillNeverPromptWebsitesManager = AppDependencyProvider.shared.autofillNeverPromptWebsitesManager) { self.appSettings = appSettings self.tld = tld self.secureVault = secureVault self.currentTabUrl = currentTabUrl + self.autofillNeverPromptWebsitesManager = autofillNeverPromptWebsitesManager + updateData() authenticationNotRequired = !hasAccountsSaved || AppDependencyProvider.shared.autofillLoginSession.isValidSession setupCancellables() @@ -153,7 +162,7 @@ final class AutofillLoginListViewModel: ObservableObject { func rowsInSection(_ section: Int) -> Int { switch self.sections[section] { case .enableAutofill: - return 1 + return autofillNeverPromptWebsitesManager.neverPromptWebsites.isEmpty ? 1 : 2 case .credentials(_, let items): return items.count } @@ -180,8 +189,11 @@ final class AutofillLoginListViewModel: ObservableObject { } self.sections = makeSections(with: filteredAccounts) } - - + + func resetNeverPromptWebsites() { + _ = autofillNeverPromptWebsitesManager.deleteAllNeverPromptWebsites() + } + // MARK: Private Methods private func fetchAccounts() -> [SecureVaultModels.WebsiteAccount] { diff --git a/DuckDuckGo/AutofillLoginPromptView.swift b/DuckDuckGo/AutofillLoginPromptView.swift index 0a75d988ee..4570c243f8 100644 --- a/DuckDuckGo/AutofillLoginPromptView.swift +++ b/DuckDuckGo/AutofillLoginPromptView.swift @@ -44,7 +44,7 @@ struct AutofillLoginPromptView: View { VStack { Spacer() .frame(height: Const.Size.topPadding) - AutofillViews.WebsiteWithFavicon(accountDomain: viewModel.domain) + AutofillViews.AppIconHeader() Spacer() .frame(height: Const.Size.headlineTopPadding) AutofillViews.Headline(title: viewModel.message) diff --git a/DuckDuckGo/AutofillLoginSettingsListViewController.swift b/DuckDuckGo/AutofillLoginSettingsListViewController.swift index 2055e7b03f..89fa680bc1 100644 --- a/DuckDuckGo/AutofillLoginSettingsListViewController.swift +++ b/DuckDuckGo/AutofillLoginSettingsListViewController.swift @@ -77,6 +77,7 @@ final class AutofillLoginSettingsListViewController: UIViewController { tableView.estimatedSectionFooterHeight = 40 tableView.registerCell(ofType: AutofillListItemTableViewCell.self) tableView.registerCell(ofType: EnableAutofillSettingsTableViewCell.self) + tableView.registerCell(ofType: AutofillNeverSavedTableViewCell.self) // Have to set tableHeaderView height otherwise tableView content will jump when adding / removing searchController due to tableView insetGrouped style tableView.tableHeaderView = UIView(frame: CGRect(x: 0, y: 0, width: 0, height: 24)) return tableView @@ -217,7 +218,23 @@ final class AutofillLoginSettingsListViewController: UIViewController { navigationController?.pushViewController(detailsController, animated: animated) detailsViewController = detailsController } - + + private func presentNeverPromptResetPromptAtIndexPath(_ indexPath: IndexPath) { + let controller = UIAlertController(title: "", + message: UserText.autofillResetNeverSavedActionTitle, + preferredStyle: .actionSheet) + controller.addAction(UIAlertAction(title: UserText.autofillResetNeverSavedActionConfirmButton, style: .destructive) { [weak self] _ in + self?.viewModel.resetNeverPromptWebsites() + self?.tableView.reloadData() + Pixel.fire(pixel: .autofillLoginsSettingsResetExcludedConfirmed) + }) + controller.addAction(UIAlertAction(title: UserText.autofillResetNeverSavedActionCancelButton, style: .cancel) { _ in + Pixel.fire(pixel: .autofillLoginsSettingsResetExcludedDismissed) + }) + present(controller: controller, fromView: tableView.cellForRow(at: indexPath) ?? tableView) + Pixel.fire(pixel: .autofillLoginsSettingsResetExcludedDisplayed) + } + private func setupCancellables() { viewModel.$viewState .receive(on: DispatchQueue.main) @@ -470,6 +487,12 @@ final class AutofillLoginSettingsListViewController: UIViewController { cell.theme = ThemeManager.shared.currentTheme return cell } + + private func neverSavedCell(for tableView: UITableView, indexPath: IndexPath) -> AutofillNeverSavedTableViewCell { + let cell = tableView.dequeueCell(ofType: AutofillNeverSavedTableViewCell.self, for: indexPath) + cell.theme = ThemeManager.shared.currentTheme + return cell + } } // MARK: UITableViewDelegate @@ -489,11 +512,16 @@ extension AutofillLoginSettingsListViewController: UITableViewDelegate { tableView.deselectRow(at: indexPath, animated: true) switch viewModel.sections[indexPath.section] { + case .enableAutofill: + switch EnableAutofillRows(rawValue: indexPath.row) { + case .resetNeverPromptWebsites: + presentNeverPromptResetPromptAtIndexPath(indexPath) + default: + break + } case .credentials(_, let items): let item = items[indexPath.row] showAccountDetails(item.account) - default: - break } } @@ -541,7 +569,14 @@ extension AutofillLoginSettingsListViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { switch viewModel.sections[indexPath.section] { case .enableAutofill: - return enableAutofillCell(for: tableView, indexPath: indexPath) + switch EnableAutofillRows(rawValue: indexPath.row) { + case .toggleAutofill: + return enableAutofillCell(for: tableView, indexPath: indexPath) + case .resetNeverPromptWebsites: + return neverSavedCell(for: tableView, indexPath: indexPath) + default: + fatalError("No cell for row at index \(indexPath.row)") + } case .credentials(_, let items): return credentialCell(for: tableView, item: items[indexPath.row], indexPath: indexPath) } diff --git a/DuckDuckGo/AutofillNeverPromptWebsitesManager.swift b/DuckDuckGo/AutofillNeverPromptWebsitesManager.swift new file mode 100644 index 0000000000..1671e0b5fb --- /dev/null +++ b/DuckDuckGo/AutofillNeverPromptWebsitesManager.swift @@ -0,0 +1,84 @@ +// +// AutofillNeverPromptWebsitesManager.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import BrowserServicesKit +import Core + +class AutofillNeverPromptWebsitesManager { + + public private(set) var neverPromptWebsites: [SecureVaultModels.NeverPromptWebsites] = [] + + private let secureVault: (any AutofillSecureVault)? + + public init(secureVault: (any AutofillSecureVault)? = try? AutofillSecureVaultFactory.makeVault(errorReporter: SecureVaultErrorReporter.shared)) { + self.secureVault = secureVault + + fetchNeverPromptWebsites() + } + + public func hasNeverPromptWebsitesFor(domain: String) -> Bool { + return neverPromptWebsites.contains { $0.domain == domain } + } + + public func saveNeverPromptWebsite(_ domain: String) throws -> Int64? { + guard let secureVault = secureVault else { + return nil + } + + do { + let id = try secureVault.storeNeverPromptWebsites(SecureVaultModels.NeverPromptWebsites(domain: domain)) + + fetchNeverPromptWebsites() + return id + } catch { + Pixel.fire(pixel: .secureVaultError, error: error) + throw error + } + } + + public func deleteAllNeverPromptWebsites() -> Bool { + guard let secureVault = secureVault else { + return false + } + + do { + try secureVault.deleteAllNeverPromptWebsites() + + fetchNeverPromptWebsites() + return true + } catch { + Pixel.fire(pixel: .secureVaultError, error: error) + return false + } + } + + private func fetchNeverPromptWebsites() { + guard let secureVault = secureVault else { + return + } + + do { + neverPromptWebsites = try secureVault.neverPromptWebsites() + } catch { + Pixel.fire(pixel: .secureVaultError, error: error) + neverPromptWebsites = [] + } + } +} diff --git a/DuckDuckGo/AutofillNeverSavedTableViewCell.swift b/DuckDuckGo/AutofillNeverSavedTableViewCell.swift new file mode 100644 index 0000000000..adfc4c5e53 --- /dev/null +++ b/DuckDuckGo/AutofillNeverSavedTableViewCell.swift @@ -0,0 +1,78 @@ +// +// AutofillNeverSavedTableViewCell.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import DesignResourcesKit + +class AutofillNeverSavedTableViewCell: UITableViewCell { + + var theme: Theme? { + didSet { + updateTheme() + } + } + + private lazy var titleLabel: UILabel = { + let label = UILabel(frame: CGRect.zero) + label.font = .preferredFont(forTextStyle: .callout) + label.text = UserText.autofillNeverSavedSettings + label.numberOfLines = 0 + label.lineBreakMode = .byWordWrapping + return label + }() + + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + setupSubviews() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupSubviews() { + installSubviews() + installConstraints() + } + + private func installSubviews() { + contentView.addSubview(titleLabel) + } + + private func installConstraints() { + titleLabel.translatesAutoresizingMaskIntoConstraints = false + let margins = contentView.layoutMarginsGuide + + NSLayoutConstraint.activate([ + titleLabel.topAnchor.constraint(equalTo: margins.topAnchor), + titleLabel.bottomAnchor.constraint(equalTo: margins.bottomAnchor), + titleLabel.leadingAnchor.constraint(equalTo: margins.leadingAnchor), + titleLabel.trailingAnchor.constraint(equalTo: margins.trailingAnchor) + ]) + } + + private func updateTheme() { + guard let theme = theme else { + return + } + + titleLabel.textColor = theme.autofillDefaultTitleTextColor + contentView.backgroundColor = UIColor(designSystemColor: .surface) + } +} diff --git a/DuckDuckGo/AutofillViews.swift b/DuckDuckGo/AutofillViews.swift index 8dab1a504d..aae551c98f 100644 --- a/DuckDuckGo/AutofillViews.swift +++ b/DuckDuckGo/AutofillViews.swift @@ -55,18 +55,10 @@ struct AutofillViews { } } - struct WebsiteWithFavicon: View { - let accountDomain: String - + struct AppIconHeader: View { var body: some View { - HStack { - FaviconView(viewModel: FaviconViewModel(domain: accountDomain)) - .scaledToFit() - .frame(width: Const.Size.logoImage, height: Const.Size.logoImage) - Text(accountDomain) - .daxFootnoteRegular() - .foregroundColor(Color(designSystemColor: .textSecondary)) - } + Image.appIcon + .scaledToFit() } } @@ -241,4 +233,5 @@ private enum Const { private extension Image { static let close = Image("Close-24") + static let appIcon = Image("WaitlistShareSheetLogo") } diff --git a/DuckDuckGo/Base.lproj/DaxOnboarding.storyboard b/DuckDuckGo/Base.lproj/DaxOnboarding.storyboard index f9ac8b1264..f95f44d472 100644 --- a/DuckDuckGo/Base.lproj/DaxOnboarding.storyboard +++ b/DuckDuckGo/Base.lproj/DaxOnboarding.storyboard @@ -1,9 +1,9 @@ - + - + @@ -149,7 +149,7 @@ DuckDuckGo! - + @@ -161,7 +161,7 @@ DuckDuckGo! - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -2387,20 +2287,63 @@ After all, the internet shouldn’t feel so creepy, and getting the privacy you - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + diff --git a/DuckDuckGo/BlankSnapshot.storyboard b/DuckDuckGo/BlankSnapshot.storyboard deleted file mode 100644 index 2f3d93adf5..0000000000 --- a/DuckDuckGo/BlankSnapshot.storyboard +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/DuckDuckGo/BlankSnapshotViewController.swift b/DuckDuckGo/BlankSnapshotViewController.swift index a8aea0fd07..dd70e0ac01 100644 --- a/DuckDuckGo/BlankSnapshotViewController.swift +++ b/DuckDuckGo/BlankSnapshotViewController.swift @@ -25,50 +25,54 @@ protocol BlankSnapshotViewRecoveringDelegate: AnyObject { func recoverFromPresenting(controller: BlankSnapshotViewController) } +// Still some logic here that should be de-duplicated from MainViewController class BlankSnapshotViewController: UIViewController { override var preferredStatusBarStyle: UIStatusBarStyle { return ThemeManager.shared.currentTheme.statusBarStyle } - @IBOutlet weak var customNavigationBar: UIView! - @IBOutlet weak var tabsButton: UIBarButtonItem! - @IBOutlet weak var fireButton: UIBarButtonItem! let menuButton = MenuButton() - @IBOutlet weak var lastButton: UIBarButtonItem! - @IBOutlet weak var toolbar: UIToolbar! - - @IBOutlet weak var statusBarBackground: UIView! - @IBOutlet weak var navigationBarTop: NSLayoutConstraint! - - var omniBar: OmniBar! + let tabSwitcherButton = TabSwitcherButton() - + let appSettings: AppSettings + + var viewCoordinator: MainViewCoordinator! + weak var delegate: BlankSnapshotViewRecoveringDelegate? + + init(appSettings: AppSettings) { + self.appSettings = appSettings + super.init(nibName: nil, bundle: nil) + } - static func loadFromStoryboard() -> BlankSnapshotViewController { - let storyboard = UIStoryboard(name: "BlankSnapshot", bundle: nil) - guard let controller = storyboard.instantiateInitialViewController() as? BlankSnapshotViewController else { - fatalError("Failed to instantiate correct Blank Snapshot view controller") - } - return controller + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") } - + override func viewDidLoad() { super.viewDidLoad() + viewCoordinator = MainViewFactory.createViewHierarchy(view) + if appSettings.currentAddressBarPosition.isBottom { + viewCoordinator.moveAddressBarToPosition(.bottom) + viewCoordinator.hideToolbarSeparator() + } + configureOmniBar() + configureToolbarButtons() if AppWidthObserver.shared.isLargeWidth { - toolbar.isHidden = true - navigationBarTop.constant = 40 + viewCoordinator.toolbar.isHidden = true + viewCoordinator.constraints.navigationBarContainerTop.constant = 40 configureTabBar() } else { - tabsButton.customView = tabSwitcherButton + viewCoordinator.toolbarTabSwitcherButton.customView = tabSwitcherButton tabSwitcherButton.delegate = self + viewCoordinator.lastToolbarButton.customView = menuButton menuButton.setState(.menuImage, animated: false) - lastButton.customView = menuButton + viewCoordinator.lastToolbarButton.customView = menuButton } applyTheme(ThemeManager.shared.currentTheme) @@ -77,7 +81,13 @@ class BlankSnapshotViewController: UIViewController { // Need to do this at this phase to support split screen on iPad override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) - toolbar.isHidden = AppWidthObserver.shared.isLargeWidth + viewCoordinator.toolbar.isHidden = AppWidthObserver.shared.isLargeWidth + } + + private func configureToolbarButtons() { + viewCoordinator.toolbarFireButton.action = #selector(buttonPressed(sender:)) + viewCoordinator.toolbarFireButton.action = #selector(buttonPressed(sender:)) + viewCoordinator.lastToolbarButton.action = #selector(buttonPressed(sender:)) } private func configureTabBar() { @@ -100,15 +110,10 @@ class BlankSnapshotViewController: UIViewController { } private func configureOmniBar() { - omniBar = OmniBar.loadFromXib() - omniBar.frame = customNavigationBar.bounds - customNavigationBar.addSubview(omniBar) - if AppWidthObserver.shared.isLargeWidth { - omniBar.enterPadState() + viewCoordinator.omniBar.enterPadState() } - - omniBar.omniDelegate = self + viewCoordinator.omniBar.omniDelegate = self } @objc func buttonPressed(sender: Any) { @@ -140,7 +145,7 @@ extension BlankSnapshotViewController: OmniBarDelegate { func onTextFieldDidBeginEditing(_ omniBar: OmniBar) -> Bool { DispatchQueue.main.async { - self.omniBar.resignFirstResponder() + self.viewCoordinator.omniBar.resignFirstResponder() self.userInteractionDetected() } return false @@ -167,26 +172,36 @@ extension BlankSnapshotViewController: Themable { func decorate(with theme: Theme) { setNeedsStatusBarAppearanceUpdate() - - view.backgroundColor = theme.backgroundColor - + if AppWidthObserver.shared.isLargeWidth { - statusBarBackground.backgroundColor = theme.tabsBarBackgroundColor + viewCoordinator.statusBackground.backgroundColor = theme.tabsBarBackgroundColor } else { - statusBarBackground.backgroundColor = theme.barBackgroundColor + viewCoordinator.statusBackground.backgroundColor = theme.omniBarBackgroundColor } - customNavigationBar?.backgroundColor = theme.barBackgroundColor - customNavigationBar?.tintColor = theme.barTintColor - - omniBar?.decorate(with: theme) - - toolbar?.barTintColor = theme.barBackgroundColor - toolbar?.tintColor = theme.barTintColor - + + view.backgroundColor = theme.mainViewBackgroundColor + + viewCoordinator.navigationBarContainer.backgroundColor = theme.barBackgroundColor + viewCoordinator.navigationBarContainer.tintColor = theme.barTintColor + + viewCoordinator.omniBar.decorate(with: theme) + + viewCoordinator.progress.decorate(with: theme) + + viewCoordinator.toolbar.barTintColor = theme.barBackgroundColor + viewCoordinator.toolbar.tintColor = theme.barTintColor + tabSwitcherButton.decorate(with: theme) - tabsButton.tintColor = theme.barTintColor - + viewCoordinator.toolbarTabSwitcherButton.tintColor = theme.barTintColor + + viewCoordinator.logoText.tintColor = theme.ddgTextTintColor + + if appSettings.currentAddressBarPosition == .bottom { + viewCoordinator.statusBackground.backgroundColor = theme.backgroundColor + } + menuButton.decorate(with: theme) - } + + } } diff --git a/DuckDuckGo/BookmarkFolderCell.swift b/DuckDuckGo/BookmarkFolderCell.swift index acf03631aa..ae02cce8e8 100644 --- a/DuckDuckGo/BookmarkFolderCell.swift +++ b/DuckDuckGo/BookmarkFolderCell.swift @@ -36,7 +36,7 @@ class BookmarkFolderCell: UITableViewCell { if folder.uuid == BookmarkEntity.Constants.rootFolderID { title.text = UserText.sectionTitleBookmarks - } else if folder.uuid == BookmarkEntity.Constants.favoritesFolderID { + } else if folder.uuid.flatMap({ FavoritesFolderID.allCases.map(\.rawValue).contains($0) }) == true { title.text = "Favorites" assertionFailure("Favorites folder in UI") } else { diff --git a/DuckDuckGo/BookmarkFoldersTableViewController.swift b/DuckDuckGo/BookmarkFoldersTableViewController.swift index ebe5d41258..eef6dcf376 100644 --- a/DuckDuckGo/BookmarkFoldersTableViewController.swift +++ b/DuckDuckGo/BookmarkFoldersTableViewController.swift @@ -185,7 +185,9 @@ class BookmarkFoldersViewController: UITableViewController { fatalError("Failed to dequeue \(FavoriteCell.reuseIdentifier) as FavoriteCell") } - cell.favoriteToggle.isOn = viewModel?.bookmark.isFavorite == true + let displayedFolder = viewModel?.favoritesDisplayMode.displayedFolder ?? .mobile + + cell.favoriteToggle.isOn = viewModel?.bookmark.isFavorite(on: displayedFolder) == true cell.favoriteToggle.removeTarget(self, action: #selector(favoriteToggleDidChange(_:)), for: .valueChanged) cell.favoriteToggle.addTarget(self, action: #selector(favoriteToggleDidChange(_:)), for: .valueChanged) cell.favoriteToggle.onTintColor = ThemeManager.shared.currentTheme.buttonTintColor diff --git a/DuckDuckGo/BookmarksDataSource.swift b/DuckDuckGo/BookmarksDataSource.swift index 2f7b35d626..13f2834691 100644 --- a/DuckDuckGo/BookmarksDataSource.swift +++ b/DuckDuckGo/BookmarksDataSource.swift @@ -52,7 +52,7 @@ class BookmarksDataSource: NSObject, UITableViewDataSource { let cell = BookmarksViewControllerCellFactory.makeBookmarkCell(tableView, forIndexPath: indexPath) cell.faviconImageView.loadFavicon(forDomain: bookmark.urlObject?.host, usingCache: .fireproof) cell.titleLabel.text = bookmark.title - cell.favoriteImageViewContainer.isHidden = !bookmark.isFavorite + cell.favoriteImageViewContainer.isHidden = !bookmark.isFavorite(on: viewModel.favoritesDisplayMode.displayedFolder) return cell } } diff --git a/DuckDuckGo/BookmarksDatabase.swift b/DuckDuckGo/BookmarksDatabase.swift index e20d1f74c7..962c552516 100644 --- a/DuckDuckGo/BookmarksDatabase.swift +++ b/DuckDuckGo/BookmarksDatabase.swift @@ -39,6 +39,10 @@ public class BookmarksDatabase { return url }() + public static var defaultDBFileURL: URL = { + return defaultDBLocation.appendingPathComponent("Bookmarks.sqlite", conformingTo: .database) + }() + public static func make(location: URL = defaultDBLocation, readOnly: Bool = false) -> CoreDataDatabase { os_log("BookmarksDatabase.make - IN - %s", location.absoluteString) let bundle = Bookmarks.bundle diff --git a/DuckDuckGo/BookmarksViewController.swift b/DuckDuckGo/BookmarksViewController.swift index 78a5dca1d0..8363a61384 100644 --- a/DuckDuckGo/BookmarksViewController.swift +++ b/DuckDuckGo/BookmarksViewController.swift @@ -56,8 +56,10 @@ class BookmarksViewController: UIViewController, UITableViewDelegate { private let favicons: Favicons private let syncService: DDGSyncing private let syncDataProviders: SyncDataProviders + private let appSettings: AppSettings private var localUpdatesCancellable: AnyCancellable? private var syncUpdatesCancellable: AnyCancellable? + private var favoritesDisplayModeCancellable: AnyCancellable? /// Creating left and right toolbar UIBarButtonItems with customView so that 'Edit' button is centered private lazy var addFolderButton: UIButton = { @@ -111,14 +113,21 @@ class BookmarksViewController: UIViewController, UITableViewDelegate { parentID: NSManagedObjectID? = nil, favicons: Favicons = Favicons.shared, syncService: DDGSyncing, - syncDataProviders: SyncDataProviders + syncDataProviders: SyncDataProviders, + appSettings: AppSettings ) { self.bookmarksDatabase = bookmarksDatabase self.searchDataSource = SearchBookmarksDataSource(searchEngine: bookmarksSearch) - self.viewModel = BookmarkListViewModel(bookmarksDatabase: bookmarksDatabase, parentID: parentID, syncService: syncService) + self.viewModel = BookmarkListViewModel( + bookmarksDatabase: bookmarksDatabase, + parentID: parentID, + favoritesDisplayMode: appSettings.favoritesDisplayMode, + syncService: syncService + ) self.favicons = favicons self.syncService = syncService self.syncDataProviders = syncDataProviders + self.appSettings = appSettings super.init(coder: coder) bindSyncService() @@ -143,6 +152,18 @@ class BookmarksViewController: UIViewController, UITableViewDelegate { } } + private func bindFavoritesDisplayMode() { + favoritesDisplayModeCancellable = NotificationCenter.default.publisher(for: AppUserDefaults.Notifications.favoritesDisplayModeChange) + .receive(on: DispatchQueue.main) + .sink { [weak self] _ in + guard let self else { + return + } + self.viewModel.favoritesDisplayMode = self.appSettings.favoritesDisplayMode + self.tableView.reloadData() + } + } + override func viewDidLoad() { super.viewDidLoad() @@ -150,6 +171,7 @@ class BookmarksViewController: UIViewController, UITableViewDelegate { self?.tableView.reloadData() self?.refreshAll() } + bindFavoritesDisplayMode() syncService.scheduler.requestSyncImmediately() @@ -261,7 +283,8 @@ class BookmarksViewController: UIViewController, UITableViewDelegate { bookmarksSearch: self.searchDataSource.searchEngine, parentID: parent.objectID, syncService: self.syncService, - syncDataProviders: self.syncDataProviders) + syncDataProviders: self.syncDataProviders, + appSettings: self.appSettings) controller?.delegate = self.delegate return controller }) @@ -278,8 +301,8 @@ class BookmarksViewController: UIViewController, UITableViewDelegate { let cell = tableView.cellForRow(at: indexPath) cell?.tintColor = .black - let title = bookmark.isFavorite ? UserText.actionRemoveFavorite : UserText.favorite - let iconName = bookmark.isFavorite ? "Favorite-Remove-24" : "Favorite-24" + let title = bookmark.isFavorite(on: viewModel.favoritesDisplayMode.displayedFolder) ? UserText.actionRemoveFavorite : UserText.favorite + let iconName = bookmark.isFavorite(on: viewModel.favoritesDisplayMode.displayedFolder) ? "Favorite-Remove-24" : "Favorite-24" let toggleFavoriteAction = UIContextualAction(style: .normal, title: title) { [weak self] (_, _, completionHandler) in completionHandler(true) @@ -540,7 +563,8 @@ class BookmarksViewController: UIViewController, UITableViewDelegate { guard let controller = AddOrEditBookmarkViewController(coder: coder, editingEntityID: id, bookmarksDatabase: bookmarksDatabase, - syncService: syncService) else { + syncService: syncService, + appSettings: appSettings) else { assertionFailure("Failed to create controller") return nil } @@ -551,7 +575,8 @@ class BookmarksViewController: UIViewController, UITableViewDelegate { guard let controller = AddOrEditBookmarkViewController(coder: coder, parentFolderID: viewModel.currentFolder?.objectID, bookmarksDatabase: bookmarksDatabase, - syncService: syncService) else { + syncService: syncService, + appSettings: appSettings) else { assertionFailure("Failed to create controller") return nil } @@ -565,7 +590,8 @@ class BookmarksViewController: UIViewController, UITableViewDelegate { coder: coder, bookmarksDatabase: bookmarksDatabase, syncService: syncService, - syncDataProviders: syncDataProviders + syncDataProviders: syncDataProviders, + appSettings: appSettings ) else { fatalError("Failed to create controller") } @@ -597,7 +623,10 @@ class BookmarksViewController: UIViewController, UITableViewDelegate { let bookmarksDatabase = bookmarksDatabase Task { - let result = await BookmarksImporter(coreDataStore: bookmarksDatabase).parseAndSave(html: html) + let result = await BookmarksImporter( + coreDataStore: bookmarksDatabase, + favoritesDisplayMode: appSettings.favoritesDisplayMode + ).parseAndSave(html: html) switch result { case .success: WidgetCenter.shared.reloadAllTimelines() @@ -632,7 +661,8 @@ class BookmarksViewController: UIViewController, UITableViewDelegate { // create file to export let tempFileUrl = FileManager.default.temporaryDirectory.appendingPathComponent(Constants.bookmarksFileName) do { - try BookmarksExporter(coreDataStore: bookmarksDatabase).exportBookmarksTo(url: tempFileUrl) + try BookmarksExporter(coreDataStore: bookmarksDatabase, favoritesDisplayMode: viewModel.favoritesDisplayMode) + .exportBookmarksTo(url: tempFileUrl) } catch { os_log("bookmarks failed to export %s", type: .debug, error.localizedDescription) ActionMessageView.present(message: UserText.exportBookmarksFailedMessage) @@ -904,8 +934,10 @@ extension BookmarksViewController: AddOrEditBookmarkViewControllerDelegate { } // capture the optional details - let favoritesFolder = bookmark.favoriteFolder - let favoritesIndex = favoritesFolder?.favoritesArray.firstIndex(of: bookmark) + var favoritesFoldersAndIndexes: [BookmarkEntity: Int] = [:] + for favoritesFolder in bookmark.favoriteFoldersSet { + favoritesFoldersAndIndexes[favoritesFolder] = favoritesFolder.favoritesArray.firstIndex(of: bookmark) ?? 0 + } // capture this locally because this VC might have been closed when undo gets pressed let localViewModel = self.viewModel @@ -916,8 +948,7 @@ extension BookmarksViewController: AddOrEditBookmarkViewControllerDelegate { url: url, folder: parent, folderIndex: index, - favoritesFolder: favoritesFolder, - favoritesIndex: favoritesIndex) + favoritesFoldersAndIndexes: favoritesFoldersAndIndexes) self?.tableView.reloadData() self?.refreshAll() diff --git a/DuckDuckGo/BrokenSiteInfo.swift b/DuckDuckGo/BrokenSiteInfo.swift index ed96e5e460..2f310936e3 100644 --- a/DuckDuckGo/BrokenSiteInfo.swift +++ b/DuckDuckGo/BrokenSiteInfo.swift @@ -40,6 +40,7 @@ public struct BrokenSiteInfo { static let gpc = "gpc" static let ampUrl = "ampUrl" static let urlParametersRemoved = "urlParametersRemoved" + static let protectionsState = "protectionsState" } private let url: URL? @@ -54,12 +55,14 @@ public struct BrokenSiteInfo { private let manufacturer: String private let systemVersion: String private let gpc: Bool - + private let protectionsState: Bool + public init(url: URL?, httpsUpgrade: Bool, blockedTrackerDomains: [String], installedSurrogates: [String], isDesktop: Bool, tdsETag: String?, ampUrl: String?, urlParametersRemoved: Bool, + protectionsState: Bool, model: String = UIDevice.current.model, manufacturer: String = "Apple", systemVersion: String = UIDevice.current.systemVersion, @@ -76,7 +79,8 @@ public struct BrokenSiteInfo { self.model = model self.manufacturer = manufacturer self.systemVersion = systemVersion - + self.protectionsState = protectionsState + if let gpcParam = gpc { self.gpc = gpcParam } else { @@ -95,13 +99,13 @@ public struct BrokenSiteInfo { Keys.tds: tdsETag?.trimmingCharacters(in: CharacterSet(charactersIn: "\"")) ?? "", Keys.blockedTrackers: blockedTrackerDomains.joined(separator: ","), Keys.surrogates: installedSurrogates.joined(separator: ","), - Keys.atb: StatisticsUserDefaults().atb ?? "", Keys.os: systemVersion, Keys.manufacturer: manufacturer, Keys.model: model, Keys.gpc: gpc ? "true" : "false", - Keys.ampUrl: ampUrl ?? "", - Keys.urlParametersRemoved: urlParametersRemoved ? "true" : "false" + Keys.ampUrl: normalize(URL(string: ampUrl ?? "")), + Keys.urlParametersRemoved: urlParametersRemoved ? "true" : "false", + Keys.protectionsState: protectionsState ? "true" : "false" ] Pixel.fire(pixel: .brokenSiteReport, diff --git a/DuckDuckGo/BrowserChromeManager.swift b/DuckDuckGo/BrowserChromeManager.swift index 183f4b87d0..3f458dd74f 100644 --- a/DuckDuckGo/BrowserChromeManager.swift +++ b/DuckDuckGo/BrowserChromeManager.swift @@ -31,7 +31,7 @@ protocol BrowserChromeDelegate: AnyObject { var toolbarHeight: CGFloat { get } var barsMaxHeight: CGFloat { get } - var omniBar: OmniBar! { get } + var omniBar: OmniBar { get } var tabBarContainer: UIView { get } } diff --git a/DuckDuckGo/BrowsingMenu/BrowsingMenuViewController.swift b/DuckDuckGo/BrowsingMenu/BrowsingMenuViewController.swift index 3d39d1d51b..dec954d795 100644 --- a/DuckDuckGo/BrowsingMenu/BrowsingMenuViewController.swift +++ b/DuckDuckGo/BrowsingMenu/BrowsingMenuViewController.swift @@ -58,16 +58,18 @@ final class BrowsingMenuViewController: UIViewController { private var headerButtons: [BrowsingMenuButton] = [] private let headerEntries: [BrowsingMenuEntry] private let menuEntries: [BrowsingMenuEntry] + private let appSettings: AppSettings - class func instantiate(headerEntries: [BrowsingMenuEntry], menuEntries: [BrowsingMenuEntry]) -> BrowsingMenuViewController { + class func instantiate(headerEntries: [BrowsingMenuEntry], menuEntries: [BrowsingMenuEntry], appSettings: AppSettings = AppDependencyProvider.shared.appSettings) -> BrowsingMenuViewController { UIStoryboard(name: "BrowsingMenuViewController", bundle: nil).instantiateInitialViewController { coder in - BrowsingMenuViewController(headerEntries: headerEntries, menuEntries: menuEntries, coder: coder) + BrowsingMenuViewController(headerEntries: headerEntries, menuEntries: menuEntries, appSettings: appSettings, coder: coder) }! } - init?(headerEntries: [BrowsingMenuEntry], menuEntries: [BrowsingMenuEntry], coder: NSCoder) { + init?(headerEntries: [BrowsingMenuEntry], menuEntries: [BrowsingMenuEntry], appSettings: AppSettings, coder: NSCoder) { self.headerEntries = headerEntries self.menuEntries = menuEntries + self.appSettings = appSettings super.init(coder: coder) self.transitioningDelegate = self } @@ -219,7 +221,9 @@ final class BrowsingMenuViewController: UIViewController { // Make it go above WebView in Landscape topConstraint.constant = frame.minY + (isIPhoneLandscape ? -10 : 5) // Move menu up in Landscape, as bottom toolbar shrinks - bottomConstraint.constant = windowBounds.maxY - frame.maxY - (isIPhoneLandscape ? 2 : 10) + + let barPositionOffset: CGFloat = appSettings.currentAddressBarPosition.isBottom ? 52 : 0 + bottomConstraint.constant = windowBounds.maxY - frame.maxY - (isIPhoneLandscape ? 2 : 10) - barPositionOffset rightConstraint.constant = isIPad ? 67 : 10 recalculatePreferredWidthConstraint() diff --git a/DuckDuckGo/CriticalAlerts.swift b/DuckDuckGo/CriticalAlerts.swift new file mode 100644 index 0000000000..c2e46242b3 --- /dev/null +++ b/DuckDuckGo/CriticalAlerts.swift @@ -0,0 +1,73 @@ +// +// CriticalAlerts.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import UIKit +import Core + +struct CriticalAlerts { + + static func makePreemptiveCrashAlert() -> UIAlertController { + let alertController = UIAlertController(title: UserText.preemptiveCrashTitle, + message: UserText.preemptiveCrashBody, + preferredStyle: .alert) + alertController.overrideUserInterfaceStyle() + + let crashButton = UIAlertAction(title: UserText.preemptiveCrashAction, style: .default) { _ in + fatalError("App is in unrecoverable state") + } + + alertController.addAction(crashButton) + return alertController + } + + static func makeInsufficientDiskSpaceAlert() -> UIAlertController { + let alertController = UIAlertController(title: UserText.insufficientDiskSpaceTitle, + message: UserText.insufficientDiskSpaceBody, + preferredStyle: .alert) + alertController.overrideUserInterfaceStyle() + + let openSettingsButton = UIAlertAction(title: UserText.insufficientDiskSpaceAction, style: .default) { _ in + let url = URL(string: UIApplication.openSettingsURLString)! + UIApplication.shared.open(url, options: [:]) { _ in + fatalError("App is in unrecoverable state") + } + } + + alertController.addAction(openSettingsButton) + return alertController + } + + static func makeEmailProtectionSignInAlert() -> UIAlertController { + let alertController = UIAlertController(title: UserText.emailProtectionSignInTitle, + message: UserText.emailProtectionSignInBody, + preferredStyle: .alert) + alertController.overrideUserInterfaceStyle() + + let closeButton = UIAlertAction(title: UserText.keyCommandClose, style: .cancel) + let signInButton = UIAlertAction(title: UserText.emailProtectionSignInAction, style: .default) { _ in + UIApplication.shared.open(URL.emailProtectionQuickLink, options: [:], completionHandler: nil) + } + + alertController.addAction(closeButton) + alertController.addAction(signInButton) + return alertController + } + +} diff --git a/DuckDuckGo/DarkTheme.swift b/DuckDuckGo/DarkTheme.swift index c7080613bd..52ec2eece9 100644 --- a/DuckDuckGo/DarkTheme.swift +++ b/DuckDuckGo/DarkTheme.swift @@ -16,9 +16,12 @@ // See the License for the specific language governing permissions and // limitations under the License. // + import UIKit -import DesignResourcesKit +// If you add a new colour here: +// * and it uses the design system, please put it in Theme+DesignSystem instead +// * and it doesn't use the design, please only do so with designer approval struct DarkTheme: Theme { var name = ThemeName.dark @@ -28,15 +31,7 @@ struct DarkTheme: Theme { var tabsBarBackgroundColor = UIColor.black var tabsBarSeparatorColor = UIColor.darkGreyish - - var backgroundColor = UIColor(designSystemColor: .background) - - var mainViewBackgroundColor = UIColor(designSystemColor: .base) - - var omniBarBackgroundColor = UIColor(designSystemColor: .panel) - var barBackgroundColor = UIColor(designSystemColor: .panel) - var barTintColor = UIColor(designSystemColor: .icons) - + var navigationBarTitleColor = UIColor.white var navigationBarTintColor = UIColor.lightMercury @@ -45,7 +40,6 @@ struct DarkTheme: Theme { var searchBarBackgroundColor = UIColor.charcoalGrey var centeredSearchBarBackgroundColor = UIColor.nearlyBlackLight var searchBarTextColor = UIColor.white - var searchBarTextPlaceholderColor = UIColor(designSystemColor: .textSecondary) var searchBarTextDeemphasisColor = UIColor.lightMercury var searchBarBorderColor = UIColor.darkGreyish var searchBarClearTextIconColor = UIColor.greyish2 @@ -53,7 +47,6 @@ struct DarkTheme: Theme { var browsingMenuTextColor = UIColor.white var browsingMenuIconsColor = UIColor.gray20 - var browsingMenuBackgroundColor = UIColor(designSystemColor: .surface) var browsingMenuSeparatorColor = UIColor.charcoalGrey var browsingMenuHighlightColor = UIColor.darkGreyish @@ -63,7 +56,6 @@ struct DarkTheme: Theme { var autocompleteSuggestionTextColor = UIColor.white var autocompleteCellAccessoryColor = UIColor.lightMercury - var tableCellBackgroundColor = UIColor(designSystemColor: .surface) var tableCellSelectedColor = UIColor.charcoalGrey var tableCellSeparatorColor = UIColor.charcoalGrey var tableCellTextColor = UIColor.lightGreyish @@ -72,7 +64,6 @@ struct DarkTheme: Theme { var tableCellHighlightedBackgroundColor = UIColor.greyishBrown var tableHeaderTextColor = UIColor.greyish3 - var tabSwitcherCellBackgroundColor = UIColor(designSystemColor: .surface) var tabSwitcherCellBorderColor = UIColor.white var tabSwitcherCellTextColor = UIColor.white var tabSwitcherCellSecondaryTextColor = UIColor.lightMercury diff --git a/DuckDuckGo/Debug.storyboard b/DuckDuckGo/Debug.storyboard index 0b94ca5c81..9b09d31187 100644 --- a/DuckDuckGo/Debug.storyboard +++ b/DuckDuckGo/Debug.storyboard @@ -1,9 +1,9 @@ - + - + @@ -100,9 +100,29 @@ - + + + + + + + + + + + + + + + @@ -121,7 +141,7 @@ - + @@ -141,7 +161,7 @@ - + @@ -161,7 +181,7 @@ - + @@ -181,7 +201,7 @@ - + @@ -190,7 +210,7 @@ - + @@ -199,7 +219,7 @@ - + @@ -208,7 +228,7 @@ - + @@ -217,7 +237,7 @@ - + @@ -226,7 +246,7 @@ - + @@ -660,7 +680,7 @@ - + @@ -707,20 +727,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + diff --git a/DuckDuckGo/DuckDuckGo.entitlements b/DuckDuckGo/DuckDuckGo.entitlements index 3b229351b9..82bd4ed6cd 100644 --- a/DuckDuckGo/DuckDuckGo.entitlements +++ b/DuckDuckGo/DuckDuckGo.entitlements @@ -15,6 +15,7 @@ $(GROUP_ID_PREFIX).statistics $(GROUP_ID_PREFIX).database $(GROUP_ID_PREFIX).apptp + $(GROUP_ID_PREFIX).netp diff --git a/DuckDuckGo/EmailManagerRequestDelegate.swift b/DuckDuckGo/EmailManagerRequestDelegate.swift index 5664b7a7d6..47f3b0f253 100644 --- a/DuckDuckGo/EmailManagerRequestDelegate.swift +++ b/DuckDuckGo/EmailManagerRequestDelegate.swift @@ -53,27 +53,39 @@ extension EmailManagerRequestDelegate { } // swiftlint:enable function_parameter_count - func emailManagerKeychainAccessFailed(accessType: EmailKeychainAccessType, error: EmailKeychainAccessError) { + func emailManagerKeychainAccessFailed(_ emailManager: EmailManager, + accessType: EmailKeychainAccessType, + error: EmailKeychainAccessError) { var parameters = [ PixelParameters.emailKeychainAccessType: accessType.rawValue, PixelParameters.emailKeychainError: error.errorDescription ] - + + var errorCode: Int32? if case let .keychainLookupFailure(status) = error { + errorCode = status parameters[PixelParameters.emailKeychainKeychainStatus] = String(status) parameters[PixelParameters.emailKeychainKeychainOperation] = "lookup" } if case let .keychainDeleteFailure(status) = error { + errorCode = status parameters[PixelParameters.emailKeychainKeychainStatus] = String(status) parameters[PixelParameters.emailKeychainKeychainOperation] = "delete" } if case let .keychainSaveFailure(status) = error { + errorCode = status parameters[PixelParameters.emailKeychainKeychainStatus] = String(status) parameters[PixelParameters.emailKeychainKeychainOperation] = "save" } + // https://app.asana.com/0/414709148257752/1205196846001239/f + if errorCode == errSecNotAvailable { + emailManager.forceSignOut() + } + Pixel.fire(pixel: .emailAutofillKeychainError, withAdditionalParameters: parameters) } + } diff --git a/DuckDuckGo/EmailSignupViewController.swift b/DuckDuckGo/EmailSignupViewController.swift index b2eb876cc8..abcce2eb84 100644 --- a/DuckDuckGo/EmailSignupViewController.swift +++ b/DuckDuckGo/EmailSignupViewController.swift @@ -34,8 +34,6 @@ class EmailSignupViewController: UIViewController { static let duckDuckGoTitle: String = "DuckDuckGo" static let backImage = UIImage(systemName: "chevron.left") - static let signUpUrl: String = "https://duckduckgo.com/email/start-incontext" - static let emailPath: String = "email/" static let emailStartInContextPath: String = "email/start-incontext" static let emailChooseAddressPath: String = "email/choose-address" @@ -179,7 +177,7 @@ class EmailSignupViewController: UIViewController { // Slight delay needed for userScripts to load otherwise email protection webpage reports that this is an unsupported browser let workItem = DispatchWorkItem { [unowned self] in - self.loadUrl(URL(string: Constants.signUpUrl)) + self.loadUrl(URL.emailProtectionSignUp) } DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: workItem) @@ -357,30 +355,6 @@ extension EmailSignupViewController: EmailManagerRequestDelegate { } // swiftlint:enable function_parameter_count - func emailManagerKeychainAccessFailed(accessType: EmailKeychainAccessType, error: EmailKeychainAccessError) { - var parameters = [ - PixelParameters.emailKeychainAccessType: accessType.rawValue, - PixelParameters.emailKeychainError: error.errorDescription - ] - - if case let .keychainLookupFailure(status) = error { - parameters[PixelParameters.emailKeychainKeychainStatus] = String(status) - parameters[PixelParameters.emailKeychainKeychainOperation] = "lookup" - } - - if case let .keychainDeleteFailure(status) = error { - parameters[PixelParameters.emailKeychainKeychainStatus] = String(status) - parameters[PixelParameters.emailKeychainKeychainOperation] = "delete" - } - - if case let .keychainSaveFailure(status) = error { - parameters[PixelParameters.emailKeychainKeychainStatus] = String(status) - parameters[PixelParameters.emailKeychainKeychainOperation] = "save" - } - - Pixel.fire(pixel: .emailAutofillKeychainError, withAdditionalParameters: parameters) - } - } // MARK: - SecureVaultManagerDelegate @@ -447,6 +421,10 @@ extension EmailSignupViewController: SecureVaultManagerDelegate { // no-op } + func secureVaultManager(_: SecureVaultManager, didRequestRuntimeConfigurationForDomain domain: String, completionHandler: @escaping (String?) -> Void) { + completionHandler(nil) + } + func secureVaultManager(_: SecureVaultManager, didReceivePixel pixel: AutofillUserScript.JSPixel) { guard !pixel.isEmailPixel else { // The iOS app uses a native email autofill UI, and sends its pixels separately. Ignore pixels sent from the JS layer. diff --git a/DuckDuckGo/EventMapping+NetworkProtectionError.swift b/DuckDuckGo/EventMapping+NetworkProtectionError.swift index 2555135d5b..a139b4c9a2 100644 --- a/DuckDuckGo/EventMapping+NetworkProtectionError.swift +++ b/DuckDuckGo/EventMapping+NetworkProtectionError.swift @@ -32,6 +32,12 @@ extension EventMapping where Event == NetworkProtectionError { var params: [String: String] = [:] switch event { + case .failedToFetchLocationList(let error): + pixelEvent = .networkProtectionClientFailedToFetchLocations + pixelError = error + case .failedToParseLocationListResponse(let error): + pixelEvent = .networkProtectionClientFailedToParseLocationsResponse + pixelError = error case .failedToEncodeRedeemRequest: pixelEvent = .networkProtectionClientFailedToEncodeRedeemRequest case .invalidInviteCode: diff --git a/DuckDuckGo/Favicons.swift b/DuckDuckGo/Favicons.swift index 74487c3585..ceda1bf9d3 100644 --- a/DuckDuckGo/Favicons.swift +++ b/DuckDuckGo/Favicons.swift @@ -381,6 +381,8 @@ public class Favicons { return } + /// DuckDuckGo Privacy Browser uses built-in functionality from iOS to fetch the highest quality favicons for your bookmarks and favorites. + /// This functionality uses a user agent that is different from other network requests made by the app in order to find the best favicon available. let metadataFetcher = LPMetadataProvider() let completion: (LPLinkMetadata?, Error?) -> Void = { metadata, metadataError in guard let iconProvider = metadata?.iconProvider, metadataError == nil else { diff --git a/DuckDuckGo/FavoritesDisplayMode+UserDefaults.swift b/DuckDuckGo/FavoritesDisplayMode+UserDefaults.swift new file mode 100644 index 0000000000..82e1bf4274 --- /dev/null +++ b/DuckDuckGo/FavoritesDisplayMode+UserDefaults.swift @@ -0,0 +1,36 @@ +// +// FavoritesDisplayMode+UserDefaults.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Bookmarks +import Foundation + +extension FavoritesDisplayMode: LosslessStringConvertible { + static let `default` = FavoritesDisplayMode.displayNative(.mobile) + + public init?(_ description: String) { + switch description { + case FavoritesDisplayMode.displayNative(.mobile).description: + self = .displayNative(.mobile) + case FavoritesDisplayMode.displayUnified(native: .mobile).description: + self = .displayUnified(native: .mobile) + default: + return nil + } + } +} diff --git a/DuckDuckGo/FavoritesDisplayModeStorage.swift b/DuckDuckGo/FavoritesDisplayModeStorage.swift new file mode 100644 index 0000000000..f71e02014d --- /dev/null +++ b/DuckDuckGo/FavoritesDisplayModeStorage.swift @@ -0,0 +1,40 @@ +// +// FavoritesDisplayModeStorage.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Bookmarks +import Core +import Foundation + +final class FavoritesDisplayModeStorage: FavoritesDisplayModeStoring { + var favoritesDisplayMode: FavoritesDisplayMode { + get { + appSettings.favoritesDisplayMode + } + set { + appSettings.favoritesDisplayMode = newValue + NotificationCenter.default.post(name: AppUserDefaults.Notifications.favoritesDisplayModeChange, object: nil) + } + } + + init(appSettings: AppSettings = AppDependencyProvider.shared.appSettings) { + self.appSettings = appSettings + } + + private let appSettings: AppSettings +} diff --git a/DuckDuckGo/FavoritesDisplayModeSyncHandler.swift b/DuckDuckGo/FavoritesDisplayModeSyncHandler.swift new file mode 100644 index 0000000000..3622af2270 --- /dev/null +++ b/DuckDuckGo/FavoritesDisplayModeSyncHandler.swift @@ -0,0 +1,50 @@ +// +// FavoritesDisplayModeSyncHandler.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Bookmarks +import Combine +import Foundation +import SyncDataProviders + +final class FavoritesDisplayModeSyncHandler: FavoritesDisplayModeSyncHandlerBase { + + override func getValue() throws -> String? { + appSettings.favoritesDisplayMode.description + } + + override func setValue(_ value: String?) throws { + if let value, let displayMode = FavoritesDisplayMode(value) { + appSettings.favoritesDisplayMode = displayMode + NotificationCenter.default.post(name: AppUserDefaults.Notifications.favoritesDisplayModeChange, object: nil) + } + } + + override var valueDidChangePublisher: AnyPublisher { + NotificationCenter.default + .publisher(for: AppUserDefaults.Notifications.favoritesDisplayModeChange) + .map { _ in } + .eraseToAnyPublisher() + } + + init(_ appSettings: AppSettings = AppDependencyProvider.shared.appSettings) { + self.appSettings = appSettings + } + + private let appSettings: AppSettings +} diff --git a/DuckDuckGo/FavoritesOverlay.swift b/DuckDuckGo/FavoritesOverlay.swift index 913ae7c438..401f907d4f 100644 --- a/DuckDuckGo/FavoritesOverlay.swift +++ b/DuckDuckGo/FavoritesOverlay.swift @@ -25,6 +25,7 @@ import Persistence protocol FavoritesOverlayDelegate: AnyObject { func favoritesOverlay(_ overlay: FavoritesOverlay, didSelect favorite: BookmarkEntity) + } class FavoritesOverlay: UIViewController { @@ -32,19 +33,23 @@ class FavoritesOverlay: UIViewController { struct Constants { static let margin: CGFloat = 28 static let footerPadding: CGFloat = 50 + static let toolbarHeight: CGFloat = 52 } private let layout = UICollectionViewFlowLayout() var collectionView: UICollectionView! private var renderer: FavoritesHomeViewSectionRenderer! - + private let appSettings: AppSettings + private var theme: Theme! weak var delegate: FavoritesOverlayDelegate? - - init(viewModel: FavoritesListInteracting) { + + + init(viewModel: FavoritesListInteracting, appSettings: AppSettings = AppDependencyProvider.shared.appSettings) { renderer = FavoritesHomeViewSectionRenderer(allowsEditing: false, viewModel: viewModel) + self.appSettings = appSettings super.init(nibName: nil, bundle: nil) } @@ -100,16 +105,15 @@ class FavoritesOverlay: UIViewController { guard !AppWidthObserver.shared.isLargeWidth else { return } guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { return } let keyboardSize = keyboardFrame.size - let contentInsets = UIEdgeInsets(top: 0.0, left: 0.0, bottom: keyboardSize.height + Constants.margin * 2, right: 0.0) - collectionView.contentInset = contentInsets - collectionView.scrollIndicatorInsets = contentInsets + let bottomInset = appSettings.currentAddressBarPosition == .bottom ? 0 : keyboardSize.height - Constants.toolbarHeight + collectionView.contentInset = UIEdgeInsets(top: 0.0, left: 0.0, bottom: bottomInset, right: 0.0) } @objc private func keyboardWillHide(notification: NSNotification) { - let contentInsets = UIEdgeInsets.zero - collectionView.contentInset = contentInsets - collectionView.scrollIndicatorInsets = contentInsets + collectionView.contentInset = .zero + collectionView.scrollIndicatorInsets = .zero } + } extension FavoritesOverlay: FavoritesHomeViewSectionRendererDelegate { diff --git a/DuckDuckGo/FavoritesViewController.swift b/DuckDuckGo/FavoritesViewController.swift index ad8c58cd9a..54e6b883d0 100644 --- a/DuckDuckGo/FavoritesViewController.swift +++ b/DuckDuckGo/FavoritesViewController.swift @@ -52,10 +52,12 @@ class FavoritesViewController: UIViewController { private let bookmarksDatabase: CoreDataDatabase private let syncService: DDGSyncing private let syncDataProviders: SyncDataProviders + private let appSettings: AppSettings fileprivate var viewModelCancellable: AnyCancellable? private var localUpdatesCancellable: AnyCancellable? private var syncUpdatesCancellable: AnyCancellable? + private var favoritesDisplayModeCancellable: AnyCancellable? var hasFavorites: Bool { renderer.viewModel.favorites.count > 0 @@ -68,10 +70,17 @@ class FavoritesViewController: UIViewController { } } - init?(coder: NSCoder, bookmarksDatabase: CoreDataDatabase, syncService: DDGSyncing, syncDataProviders: SyncDataProviders) { + init?( + coder: NSCoder, + bookmarksDatabase: CoreDataDatabase, + syncService: DDGSyncing, + syncDataProviders: SyncDataProviders, + appSettings: AppSettings + ) { self.bookmarksDatabase = bookmarksDatabase self.syncService = syncService self.syncDataProviders = syncDataProviders + self.appSettings = appSettings super.init(coder: coder) } @@ -93,9 +102,13 @@ class FavoritesViewController: UIViewController { collectionView.backgroundColor = .clear view.addSubview(collectionView) - - renderer = FavoritesHomeViewSectionRenderer(allowsEditing: true, - viewModel: FavoritesListViewModel(bookmarksDatabase: bookmarksDatabase)) + + let favoritesListViewModel = FavoritesListViewModel( + bookmarksDatabase: bookmarksDatabase, + favoritesDisplayMode: appSettings.favoritesDisplayMode + ) + + renderer = FavoritesHomeViewSectionRenderer(allowsEditing: true, viewModel: favoritesListViewModel) renderer.install(into: self) // Has to happen after the renderer is installed @@ -106,6 +119,16 @@ class FavoritesViewController: UIViewController { self?.updateHeroImage() } + favoritesDisplayModeCancellable = NotificationCenter.default.publisher(for: AppUserDefaults.Notifications.favoritesDisplayModeChange) + .receive(on: DispatchQueue.main) + .sink { [weak self] _ in + guard let self else { + return + } + self.renderer.viewModel.favoritesDisplayMode = self.appSettings.favoritesDisplayMode + self.collectionView.reloadData() + } + registerForKeyboardNotifications() updateHeroImage() diff --git a/DuckDuckGo/FilePreviewHelper.swift b/DuckDuckGo/FilePreviewHelper.swift index f13b508379..f8f4009311 100644 --- a/DuckDuckGo/FilePreviewHelper.swift +++ b/DuckDuckGo/FilePreviewHelper.swift @@ -33,7 +33,10 @@ struct FilePreviewHelper { static func canAutoPreviewMIMEType(_ mimeType: MIMEType) -> Bool { switch mimeType { - case .reality, .usdz, .passbook, .calendar: + case .passbook: + return UIDevice.current.userInterfaceIdiom == .phone + + case .reality, .usdz, .calendar: return true default: return false diff --git a/DuckDuckGo/FullscreenDaxDialogViewController.swift b/DuckDuckGo/FullscreenDaxDialogViewController.swift index 2d8dcc6c6f..1557ae3c70 100644 --- a/DuckDuckGo/FullscreenDaxDialogViewController.swift +++ b/DuckDuckGo/FullscreenDaxDialogViewController.swift @@ -32,18 +32,22 @@ class FullscreenDaxDialogViewController: UIViewController { @IBOutlet weak var highlightCutOutView: HighlightCutOutView! @IBOutlet weak var containerHeight: NSLayoutConstraint! - + @IBOutlet weak var spacerPadding: NSLayoutConstraint! + + let appSettings = AppDependencyProvider.shared.appSettings + weak var daxDialogViewController: DaxDialogViewController? weak var delegate: FullscreenDaxDialogDelegate? var spec: DaxDialogs.BrowsingSpec? var woShown: Bool = false - + override func viewDidLoad() { super.viewDidLoad() daxDialogViewController?.cta = spec?.cta - daxDialogViewController?.message = spec?.message + daxDialogViewController?.message = spec?.message.replacingOccurrences(of: "☝️", + with: appSettings.currentAddressBarPosition == .bottom ? "👇" : "☝️") daxDialogViewController?.onTapCta = dismissCta highlightCutOutView.fillColor = UIColor(red: 0, green: 0, blue: 0, alpha: 1.0) @@ -69,6 +73,7 @@ class FullscreenDaxDialogViewController: UIViewController { containerHeight.constant = daxDialogViewController?.calculateHeight() ?? 0 updateCutOut() + updateForAddressBarPosition() } override func viewDidAppear(_ animated: Bool) { @@ -92,16 +97,20 @@ class FullscreenDaxDialogViewController: UIViewController { @objc func orientationDidChange() { updateCutOut() + updateForAddressBarPosition() } - + + private func updateForAddressBarPosition() { + if spec?.highlightAddressBar == true, appSettings.currentAddressBarPosition == .bottom { + spacerPadding.constant = 52 + } else { + spacerPadding.constant = 0 + } + } + @objc private func updateCutOut() { if spec?.highlightAddressBar ?? false, let rect = delegate?.daxDialogDidRquestAddressBarRect(controller: self) { - let padding: CGFloat = 6 - let paddedRect = CGRect(x: rect.origin.x - padding, - y: rect.origin.y - padding, - width: rect.size.width + padding * 2, - height: rect.size.height + padding * 2) - highlightCutOutView.cutOutPath = UIBezierPath(roundedRect: paddedRect, cornerRadius: paddedRect.height / 2.0) + highlightCutOutView.cutOutPath = UIBezierPath(roundedRect: rect, cornerRadius: 8) } else { highlightCutOutView.cutOutPath = nil } diff --git a/DuckDuckGo/HomeViewController.swift b/DuckDuckGo/HomeViewController.swift index f938dca3c1..c90a9ac317 100644 --- a/DuckDuckGo/HomeViewController.swift +++ b/DuckDuckGo/HomeViewController.swift @@ -63,31 +63,69 @@ class HomeViewController: UIViewController { private let tabModel: Tab private let favoritesViewModel: FavoritesListInteracting + private let appSettings: AppSettings private var viewModelCancellable: AnyCancellable? + private var favoritesDisplayModeCancellable: AnyCancellable? #if APP_TRACKING_PROTECTION private let appTPHomeViewModel: AppTPHomeViewModel #endif - static func loadFromStoryboard(model: Tab, favoritesViewModel: FavoritesListInteracting, appTPDatabase: CoreDataDatabase) -> HomeViewController { +#if APP_TRACKING_PROTECTION + static func loadFromStoryboard( + model: Tab, + favoritesViewModel: FavoritesListInteracting, + appSettings: AppSettings, + appTPDatabase: CoreDataDatabase + ) -> HomeViewController { + let storyboard = UIStoryboard(name: "Home", bundle: nil) let controller = storyboard.instantiateViewController(identifier: "HomeViewController", creator: { coder in - HomeViewController(coder: coder, tabModel: model, favoritesViewModel: favoritesViewModel, appTPDatabase: appTPDatabase) + HomeViewController( + coder: coder, + tabModel: model, + favoritesViewModel: favoritesViewModel, + appSettings: appSettings, + appTPDatabase: appTPDatabase + ) }) return controller } - - required init?(coder: NSCoder, tabModel: Tab, favoritesViewModel: FavoritesListInteracting, appTPDatabase: CoreDataDatabase) { - self.tabModel = tabModel - self.favoritesViewModel = favoritesViewModel +#else + static func loadFromStoryboard(model: Tab, favoritesViewModel: FavoritesListInteracting, appSettings: AppSettings) -> HomeViewController { + let storyboard = UIStoryboard(name: "Home", bundle: nil) + let controller = storyboard.instantiateViewController(identifier: "HomeViewController", creator: { coder in + HomeViewController(coder: coder, tabModel: model, favoritesViewModel: favoritesViewModel, appSettings: appSettings) + }) + return controller + } +#endif #if APP_TRACKING_PROTECTION + required init?( + coder: NSCoder, + tabModel: Tab, + favoritesViewModel: FavoritesListInteracting, + appSettings: AppSettings, + appTPDatabase: CoreDataDatabase + ) { + self.tabModel = tabModel + self.favoritesViewModel = favoritesViewModel + self.appSettings = appSettings self.appTPHomeViewModel = AppTPHomeViewModel(appTrackingProtectionDatabase: appTPDatabase) -#endif super.init(coder: coder) } - +#else + required init?(coder: NSCoder, tabModel: Tab, favoritesViewModel: FavoritesListInteracting, appSettings: AppSettings) { + self.tabModel = tabModel + self.favoritesViewModel = favoritesViewModel + self.appSettings = appSettings + + super.init(coder: coder) + } +#endif + required init?(coder: NSCoder) { fatalError("Not implemented") } @@ -117,6 +155,16 @@ class HomeViewController: UIViewController { self.delegate?.home(self, didRequestHideLogo: false) } } + + favoritesDisplayModeCancellable = NotificationCenter.default.publisher(for: AppUserDefaults.Notifications.favoritesDisplayModeChange) + .receive(on: DispatchQueue.main) + .sink { [weak self] _ in + guard let self else { + return + } + self.favoritesViewModel.favoritesDisplayMode = self.appSettings.favoritesDisplayMode + self.collectionView.reloadData() + } } @objc func bookmarksDidChange() { diff --git a/DuckDuckGo/Info.plist b/DuckDuckGo/Info.plist index d681c054b6..94ad683aca 100644 --- a/DuckDuckGo/Info.plist +++ b/DuckDuckGo/Info.plist @@ -4,6 +4,7 @@ BGTaskSchedulerPermittedIdentifiers + com.duckduckgo.app.vpnWaitlistStatus com.duckduckgo.app.windowsBrowserWaitlistStatus com.duckduckgo.app.configurationRefresh com.duckduckgo.app.remoteMessageRefresh diff --git a/DuckDuckGo/LightTheme.swift b/DuckDuckGo/LightTheme.swift index c3fd3d48f3..465680947a 100644 --- a/DuckDuckGo/LightTheme.swift +++ b/DuckDuckGo/LightTheme.swift @@ -16,9 +16,12 @@ // See the License for the specific language governing permissions and // limitations under the License. // + import UIKit -import DesignResourcesKit +// If you add a new colour here: +// * and it uses the design system, please put it in Theme+DesignSystem instead +// * and it doesn't use the design, please only do so with designer approval struct LightTheme: Theme { var name = ThemeName.light @@ -30,14 +33,7 @@ struct LightTheme: Theme { var tabsBarBackgroundColor = UIColor.gray20 var tabsBarSeparatorColor = UIColor.greyish - var backgroundColor = UIColor(designSystemColor: .background) - - var mainViewBackgroundColor = UIColor(designSystemColor: .base) - - var omniBarBackgroundColor = UIColor(designSystemColor: .panel) - var barBackgroundColor = UIColor(designSystemColor: .panel) - var barTintColor = UIColor(designSystemColor: .icons) - + var navigationBarTitleColor = UIColor.nearlyBlackLight var navigationBarTintColor = UIColor.darkGreyish @@ -46,7 +42,6 @@ struct LightTheme: Theme { var searchBarBackgroundColor = UIColor.lightGreyish var centeredSearchBarBackgroundColor = UIColor.mercury var searchBarTextColor = UIColor.nearlyBlackLight - var searchBarTextPlaceholderColor = UIColor.greyish3 var searchBarTextDeemphasisColor = UIColor.greyish3 var searchBarBorderColor = UIColor.lightGreyish var searchBarClearTextIconColor = UIColor.greyish2 @@ -54,7 +49,6 @@ struct LightTheme: Theme { var browsingMenuTextColor = UIColor.nearlyBlack var browsingMenuIconsColor = UIColor.nearlyBlackLight - var browsingMenuBackgroundColor = UIColor(designSystemColor: .surface) var browsingMenuSeparatorColor = UIColor.mercury var browsingMenuHighlightColor = UIColor.lightGreyish @@ -64,7 +58,6 @@ struct LightTheme: Theme { var autocompleteSuggestionTextColor = UIColor.black var autocompleteCellAccessoryColor = UIColor.darkGreyish - var tableCellBackgroundColor = UIColor(designSystemColor: .surface) var tableCellSelectedColor = UIColor.mercury var tableCellSeparatorColor = UIColor(white: 0, alpha: 0.09) var tableCellTextColor = UIColor.darkGreyish @@ -73,7 +66,6 @@ struct LightTheme: Theme { var tableCellHighlightedBackgroundColor = UIColor.mercury var tableHeaderTextColor = UIColor.greyish3 - var tabSwitcherCellBackgroundColor = UIColor(designSystemColor: .surface) var tabSwitcherCellBorderColor = UIColor.nearlyBlackLight var tabSwitcherCellTextColor = UIColor.black var tabSwitcherCellSecondaryTextColor = UIColor.greyishBrown2 diff --git a/DuckDuckGo/MacBrowserWaitlist.swift b/DuckDuckGo/MacBrowserWaitlist.swift index 60a0bfb413..0779de05b9 100644 --- a/DuckDuckGo/MacBrowserWaitlist.swift +++ b/DuckDuckGo/MacBrowserWaitlist.swift @@ -34,7 +34,7 @@ struct MacBrowserWaitlist: Waitlist { static let backgroundTaskName = "Mac Browser Waitlist Status Task" static let backgroundRefreshTaskIdentifier = "com.duckduckgo.app.macBrowserWaitlistStatus" - static let notificationIdentitier = "com.duckduckgo.ios.mac-browser.invite-code-available" + static let notificationIdentifier = "com.duckduckgo.ios.mac-browser.invite-code-available" static let inviteAvailableNotificationTitle = UserText.macWaitlistAvailableNotificationTitle static let inviteAvailableNotificationBody = UserText.waitlistAvailableNotificationBody diff --git a/DuckDuckGo/MainView.swift b/DuckDuckGo/MainView.swift index c22ef7a3f8..e78b925d59 100644 --- a/DuckDuckGo/MainView.swift +++ b/DuckDuckGo/MainView.swift @@ -19,6 +19,9 @@ import UIKit +// swiftlint:disable file_length +// swiftlint:disable line_length + class MainViewFactory { private let coordinator: MainViewCoordinator @@ -33,16 +36,13 @@ class MainViewFactory { static func createViewHierarchy(_ superview: UIView) -> MainViewCoordinator { let factory = MainViewFactory(superview) - factory.createViews() - factory.disableAutoresizingOnViewAndImmediateSubviews(superview) + factory.disableAutoresizingOnImmediateSubviews(superview) factory.constrainViews() - return factory.coordinator } - private func disableAutoresizingOnViewAndImmediateSubviews(_ view: UIView) { - view.translatesAutoresizingMaskIntoConstraints = false + private func disableAutoresizingOnImmediateSubviews(_ view: UIView) { view.subviews.forEach { $0.translatesAutoresizingMaskIntoConstraints = false } @@ -72,7 +72,10 @@ extension MainViewFactory { class NavigationBarContainer: UIView { } private func createNavigationBarContainer() { + coordinator.omniBar = OmniBar.loadFromXib() + coordinator.omniBar.translatesAutoresizingMaskIntoConstraints = false coordinator.navigationBarContainer = NavigationBarContainer() + coordinator.navigationBarContainer.addSubview(coordinator.omniBar) superview.addSubview(coordinator.navigationBarContainer) } @@ -111,6 +114,8 @@ extension MainViewFactory { private func createToolbar() { coordinator.toolbar = HitTestingToolbar() + coordinator.toolbar.isTranslucent = false + coordinator.toolbarBackButton = UIBarButtonItem(title: UserText.keyCommandBrowserBack, image: UIImage(named: "BrowsePrevious")) coordinator.toolbarForwardButton = UIBarButtonItem(title: UserText.keyCommandBrowserForward, image: UIImage(named: "BrowseNext")) coordinator.toolbarFireButton = UIBarButtonItem(title: UserText.actionForgetAll, image: UIImage(named: "Fire")) @@ -142,7 +147,7 @@ extension MainViewFactory { coordinator.logoContainer.addSubview(coordinator.logoText) superview.addSubview(coordinator.logoContainer) - disableAutoresizingOnViewAndImmediateSubviews(coordinator.logoContainer) + disableAutoresizingOnImmediateSubviews(coordinator.logoContainer) } } @@ -165,24 +170,37 @@ extension MainViewFactory { private func constrainProgress() { let progress = coordinator.progress! let navigationBarContainer = coordinator.navigationBarContainer! + + coordinator.constraints.progressBarTop = progress.constrainView(navigationBarContainer, by: .top, to: .bottom) + coordinator.constraints.progressBarBottom = progress.constrainView(navigationBarContainer, by: .bottom, to: .top) + NSLayoutConstraint.activate([ progress.constrainView(navigationBarContainer, by: .trailing), progress.constrainView(navigationBarContainer, by: .leading), progress.constrainAttribute(.height, to: 3), - progress.constrainView(navigationBarContainer, by: .top, to: .bottom), + coordinator.constraints.progressBarTop, ]) } private func constrainNavigationBarContainer() { let navigationBarContainer = coordinator.navigationBarContainer! + let toolbar = coordinator.toolbar! + let omniBar = coordinator.omniBar! coordinator.constraints.navigationBarContainerTop = navigationBarContainer.constrainView(superview.safeAreaLayoutGuide, by: .top) + coordinator.constraints.navigationBarContainerBottom = navigationBarContainer.constrainView(toolbar, by: .bottom, to: .top) + coordinator.constraints.omniBarBottom = omniBar.constrainView(navigationBarContainer, by: .bottom, relatedBy: .greaterThanOrEqual) NSLayoutConstraint.activate([ + coordinator.constraints.navigationBarContainerTop, navigationBarContainer.constrainView(superview, by: .centerX), navigationBarContainer.constrainView(superview, by: .width), - coordinator.constraints.navigationBarContainerTop, - navigationBarContainer.constrainAttribute(.height, to: 52), + navigationBarContainer.constrainAttribute(.height, to: 52, relatedBy: .greaterThanOrEqual), + omniBar.constrainAttribute(.height, to: 52), + omniBar.constrainView(navigationBarContainer, by: .top), + omniBar.constrainView(navigationBarContainer, by: .leading), + omniBar.constrainView(navigationBarContainer, by: .trailing), + coordinator.constraints.omniBarBottom, ]) } @@ -202,11 +220,16 @@ extension MainViewFactory { private func constrainStatusBackground() { let statusBackground = coordinator.statusBackground! let navigationBarContainer = coordinator.navigationBarContainer! + + coordinator.constraints.statusBackgroundToNavigationBarContainerBottom = statusBackground.constrainView(navigationBarContainer, by: .bottom) + + coordinator.constraints.statusBackgroundBottomToSafeAreaTop = statusBackground.constrainView(coordinator.superview.safeAreaLayoutGuide, by: .bottom, to: .top) + NSLayoutConstraint.activate([ statusBackground.constrainView(superview, by: .width), statusBackground.constrainView(superview, by: .centerX), statusBackground.constrainView(superview, by: .top), - statusBackground.constrainView(navigationBarContainer, by: .bottom), + coordinator.constraints.statusBackgroundToNavigationBarContainerBottom, ]) } @@ -214,8 +237,10 @@ extension MainViewFactory { let notificationBarContainer = coordinator.notificationBarContainer! let contentContainer = coordinator.contentContainer! let navigationBarContainer = coordinator.navigationBarContainer! + let statusBackground = coordinator.statusBackground! - coordinator.constraints.notificationContainerTop = notificationBarContainer.constrainView(navigationBarContainer, by: .top, to: .bottom) + coordinator.constraints.notificationContainerTopToNavigationBar = notificationBarContainer.constrainView(navigationBarContainer, by: .top, to: .bottom) + coordinator.constraints.notificationContainerTopToStatusBackground = notificationBarContainer.constrainView(statusBackground, by: .top, to: .bottom) coordinator.constraints.notificationContainerHeight = notificationBarContainer.constrainAttribute(.height, to: 0) NSLayoutConstraint.activate([ @@ -223,7 +248,7 @@ extension MainViewFactory { notificationBarContainer.constrainView(superview, by: .centerX), coordinator.constraints.notificationContainerHeight, notificationBarContainer.constrainView(contentContainer, by: .bottom, to: .top), - coordinator.constraints.notificationContainerTop, + coordinator.constraints.notificationContainerTopToNavigationBar, ]) } @@ -231,13 +256,16 @@ extension MainViewFactory { let contentContainer = coordinator.contentContainer! let toolbar = coordinator.toolbar! let notificationBarContainer = coordinator.notificationBarContainer! + let navigationBarContainer = coordinator.navigationBarContainer! coordinator.constraints.contentContainerTop = contentContainer.constrainView(notificationBarContainer, by: .top, to: .bottom) + coordinator.constraints.contentContainerBottomToToolbarTop = contentContainer.constrainView(toolbar, by: .bottom, to: .top) + coordinator.constraints.contentContainerBottomToNavigationBarContainerTop = contentContainer.constrainView(navigationBarContainer, by: .bottom, to: .top) NSLayoutConstraint.activate([ contentContainer.constrainView(superview, by: .leading), contentContainer.constrainView(superview, by: .trailing), - contentContainer.constrainView(toolbar, by: .bottom, to: .top), + coordinator.constraints.contentContainerBottomToToolbarTop, coordinator.constraints.contentContainerTop, ]) } @@ -288,25 +316,29 @@ class MainViewCoordinator { let superview: UIView - var logoContainer: UIView! + var contentContainer: UIView! + var lastToolbarButton: UIBarButtonItem! var logo: UIImageView! + var logoContainer: UIView! var logoText: UIImageView! - var toolbar: UIToolbar! - var suggestionTrayContainer: UIView! - var contentContainer: UIView! + var navigationBarContainer: UIView! var notificationBarContainer: UIView! + var omniBar: OmniBar! + var progress: ProgressView! var statusBackground: UIView! + var suggestionTrayContainer: UIView! var tabBarContainer: UIView! - var navigationBarContainer: UIView! - var progress: ProgressView! + var toolbar: UIToolbar! var toolbarBackButton: UIBarButtonItem! - var toolbarForwardButton: UIBarButtonItem! var toolbarFireButton: UIBarButtonItem! + var toolbarForwardButton: UIBarButtonItem! var toolbarTabSwitcherButton: UIBarButtonItem! - var lastToolbarButton: UIBarButtonItem! let constraints = Constraints() + // The default after creating the hiearchy is top + var addressBarPosition: AddressBarPosition = .top + fileprivate init(superview: UIView) { self.superview = superview } @@ -314,22 +346,69 @@ class MainViewCoordinator { func decorateWithTheme(_ theme: Theme) { superview.backgroundColor = theme.mainViewBackgroundColor logoText.tintColor = theme.ddgTextTintColor + omniBar.decorate(with: theme) + } + + func showToolbarSeparator() { + toolbar.setShadowImage(nil, forToolbarPosition: .any) } - func hideSuggestionTray() { - suggestionTrayContainer.isHidden = true - suggestionTrayContainer.backgroundColor = .clear + func hideToolbarSeparator() { + self.toolbar.setShadowImage(UIImage(), forToolbarPosition: .any) } class Constraints { var navigationBarContainerTop: NSLayoutConstraint! + var navigationBarContainerBottom: NSLayoutConstraint! var toolbarBottom: NSLayoutConstraint! var contentContainerTop: NSLayoutConstraint! var tabBarContainerTop: NSLayoutConstraint! - var notificationContainerTop: NSLayoutConstraint! + var notificationContainerTopToNavigationBar: NSLayoutConstraint! + var notificationContainerTopToStatusBackground: NSLayoutConstraint! var notificationContainerHeight: NSLayoutConstraint! + var omniBarBottom: NSLayoutConstraint! + var progressBarTop: NSLayoutConstraint! + var progressBarBottom: NSLayoutConstraint! + var statusBackgroundToNavigationBarContainerBottom: NSLayoutConstraint! + var statusBackgroundBottomToSafeAreaTop: NSLayoutConstraint! + var contentContainerBottomToToolbarTop: NSLayoutConstraint! + var contentContainerBottomToNavigationBarContainerTop: NSLayoutConstraint! } + func moveAddressBarToPosition(_ position: AddressBarPosition) { + guard position != addressBarPosition else { return } + switch position { + case .top: + setAddressBarBottomActive(false) + setAddressBarTopActive(true) + + case .bottom: + setAddressBarTopActive(false) + setAddressBarBottomActive(true) + } + + addressBarPosition = position + } + + func setAddressBarTopActive(_ active: Bool) { + constraints.contentContainerBottomToToolbarTop.isActive = active + constraints.navigationBarContainerTop.isActive = active + constraints.progressBarTop.isActive = active + constraints.notificationContainerTopToNavigationBar.isActive = active + constraints.statusBackgroundToNavigationBarContainerBottom.isActive = active + } + + func setAddressBarBottomActive(_ active: Bool) { + constraints.contentContainerBottomToNavigationBarContainerTop.isActive = active + constraints.progressBarBottom.isActive = active + constraints.navigationBarContainerBottom.isActive = active + constraints.notificationContainerTopToStatusBackground.isActive = active + constraints.statusBackgroundBottomToSafeAreaTop.isActive = active + } + } + +// swiftlint:enable line_length +// swiftlint:enable file_length diff --git a/DuckDuckGo/MainViewController+CookiesManaged.swift b/DuckDuckGo/MainViewController+CookiesManaged.swift index 53b4c7f465..27c1422bad 100644 --- a/DuckDuckGo/MainViewController+CookiesManaged.swift +++ b/DuckDuckGo/MainViewController+CookiesManaged.swift @@ -37,7 +37,7 @@ extension MainViewController { topURL == tabManager.current?.url else { return } - omniBar.showOrScheduleCookiesManagedNotification(isCosmetic: isCosmetic) + viewCoordinator.omniBar.showOrScheduleCookiesManagedNotification(isCosmetic: isCosmetic) } } diff --git a/DuckDuckGo/MainViewController+Email.swift b/DuckDuckGo/MainViewController+Email.swift index df085bc017..99f3942227 100644 --- a/DuckDuckGo/MainViewController+Email.swift +++ b/DuckDuckGo/MainViewController+Email.swift @@ -49,7 +49,9 @@ extension MainViewController { } let pasteBoard = UIPasteboard.general pasteBoard.string = emailManager.emailAddressFor(alias) - ActionMessageView.present(message: UserText.emailBrowsingMenuAlert) + let addressBarBottom = self.appSettings.currentAddressBarPosition.isBottom + ActionMessageView.present(message: UserText.emailBrowsingMenuAlert, + presentationLocation: .withBottomBar(andAddressBarBottom: addressBarBottom)) } } } @@ -73,30 +75,6 @@ extension MainViewController: EmailManagerRequestDelegate { } // swiftlint:enable function_parameter_count - func emailManagerKeychainAccessFailed(accessType: EmailKeychainAccessType, error: EmailKeychainAccessError) { - var parameters = [ - PixelParameters.emailKeychainAccessType: accessType.rawValue, - PixelParameters.emailKeychainError: error.errorDescription - ] - - if case let .keychainLookupFailure(status) = error { - parameters[PixelParameters.emailKeychainKeychainStatus] = String(status) - parameters[PixelParameters.emailKeychainKeychainOperation] = "lookup" - } - - if case let .keychainDeleteFailure(status) = error { - parameters[PixelParameters.emailKeychainKeychainStatus] = String(status) - parameters[PixelParameters.emailKeychainKeychainOperation] = "delete" - } - - if case let .keychainSaveFailure(status) = error { - parameters[PixelParameters.emailKeychainKeychainStatus] = String(status) - parameters[PixelParameters.emailKeychainKeychainOperation] = "save" - } - - Pixel.fire(pixel: .emailAutofillKeychainError, withAdditionalParameters: parameters) - } - } // MARK: - EmailManagerAliasPermissionDelegate diff --git a/DuckDuckGo/MainViewController+KeyCommands.swift b/DuckDuckGo/MainViewController+KeyCommands.swift index 4aa1cb4b89..f7fffded92 100644 --- a/DuckDuckGo/MainViewController+KeyCommands.swift +++ b/DuckDuckGo/MainViewController+KeyCommands.swift @@ -71,7 +71,7 @@ extension MainViewController { } var arrowKeys = [UIKeyCommand]() - if omniBar.textField.isFirstResponder { + if viewCoordinator.omniBar.textField.isFirstResponder { arrowKeys = [ UIKeyCommand(title: "", action: #selector(keyboardMoveSelectionUp), input: UIKeyCommand.inputUpArrow, modifierFlags: []), UIKeyCommand(title: "", action: #selector(keyboardMoveSelectionDown), input: UIKeyCommand.inputDownArrow, modifierFlags: []) @@ -110,7 +110,13 @@ extension MainViewController { UIKeyCommand(title: "", action: #selector(keyboardEscape), input: UIKeyCommand.inputEscape, modifierFlags: []) ] - return [alwaysAvailable, browsingCommands, findInPageCommands, arrowKeys, other].flatMap { $0 } + let commands = [alwaysAvailable, browsingCommands, findInPageCommands, arrowKeys, other].flatMap { $0 } + if #available(iOS 15, *) { + commands.forEach { + $0.wantsPriorityOverSystemBehavior = true + } + } + return commands } @objc func keyboardMoveSelectionUp() { @@ -140,7 +146,7 @@ extension MainViewController { controller.launchNewSearch() } else { showBars() - omniBar.becomeFirstResponder() + viewCoordinator.omniBar.becomeFirstResponder() } } diff --git a/DuckDuckGo/MainViewController+Segues.swift b/DuckDuckGo/MainViewController+Segues.swift index e1dddc6396..4eb01f301a 100644 --- a/DuckDuckGo/MainViewController+Segues.swift +++ b/DuckDuckGo/MainViewController+Segues.swift @@ -87,7 +87,8 @@ extension MainViewController { bookmarksDatabase: self.bookmarksDatabase, bookmarksSearch: self.bookmarksCachingSearch, syncService: self.syncService, - syncDataProviders: self.syncDataProviders) + syncDataProviders: self.syncDataProviders, + appSettings: self.appSettings) } bookmarks.delegate = self @@ -198,6 +199,14 @@ extension MainViewController { } } + func segueToSettingsSync() { + os_log(#function, log: .generalLog, type: .debug) + hideAllHighlightsIfNeeded() + launchSettings { + $0.showSync() + } + } + private func launchSettings(completion: ((SettingsViewController) -> Void)? = nil) { os_log(#function, log: .generalLog, type: .debug) let storyboard = UIStoryboard(name: "Settings", bundle: nil) diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index ceda17d760..66785d4359 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -19,11 +19,11 @@ import UIKit import WebKit +import WidgetKit import Combine import Common import Core import DDGSync -import Lottie import Kingfisher import BrowserServicesKit import Bookmarks @@ -52,7 +52,6 @@ class MainViewController: UIViewController { weak var notificationView: NotificationView? - var omniBar: OmniBar! var chromeManager: BrowserChromeManager! var allowContentUnderflow = false { @@ -78,20 +77,28 @@ class MainViewController: UIViewController { var tabManager: TabManager! let previewsSource = TabPreviewsSource() - fileprivate lazy var appSettings: AppSettings = AppUserDefaults() + let appSettings: AppSettings private var launchTabObserver: LaunchTabNotification.Observer? - + +#if APP_TRACKING_PROTECTION private let appTrackingProtectionDatabase: CoreDataDatabase +#endif + let bookmarksDatabase: CoreDataDatabase private weak var bookmarksDatabaseCleaner: BookmarkDatabaseCleaner? - private let favoritesViewModel: FavoritesListInteracting + private var favoritesViewModel: FavoritesListInteracting let syncService: DDGSyncing let syncDataProviders: SyncDataProviders private var localUpdatesCancellable: AnyCancellable? private var syncUpdatesCancellable: AnyCancellable? + private var favoritesDisplayModeCancellable: AnyCancellable? private var emailCancellables = Set() - lazy var menuBookmarksViewModel: MenuBookmarksInteracting = MenuBookmarksViewModel(bookmarksDatabase: bookmarksDatabase, syncService: syncService) + lazy var menuBookmarksViewModel: MenuBookmarksInteracting = { + let viewModel = MenuBookmarksViewModel(bookmarksDatabase: bookmarksDatabase, syncService: syncService) + viewModel.favoritesDisplayMode = appSettings.favoritesDisplayMode + return viewModel + }() weak var tabSwitcherController: TabSwitcherViewController? let tabSwitcherButton = TabSwitcherButton() @@ -99,7 +106,7 @@ class MainViewController: UIViewController { /// Do not reference directly, use `presentedMenuButton` let menuButton = MenuButton() var presentedMenuButton: MenuButton { - AppWidthObserver.shared.isLargeWidth ? omniBar.menuButtonContent : menuButton + AppWidthObserver.shared.isLargeWidth ? viewCoordinator.omniBar.menuButtonContent : menuButton } let gestureBookmarksButton = GestureToolbarButton() @@ -113,6 +120,11 @@ class MainViewController: UIViewController { return tabManager?.current } + var searchBarRect: CGRect { + let view = UIApplication.shared.windows.filter({ $0.isKeyWindow }).first?.rootViewController?.view + return viewCoordinator.omniBar.searchContainer.convert(viewCoordinator.omniBar.searchContainer.bounds, to: view) + } + var keyModifierFlags: UIKeyModifierFlags? var showKeyboardAfterFireButton: DispatchWorkItem? @@ -125,25 +137,50 @@ class MainViewController: UIViewController { var viewCoordinator: MainViewCoordinator! +#if APP_TRACKING_PROTECTION init( bookmarksDatabase: CoreDataDatabase, bookmarksDatabaseCleaner: BookmarkDatabaseCleaner, appTrackingProtectionDatabase: CoreDataDatabase, syncService: DDGSyncing, - syncDataProviders: SyncDataProviders + syncDataProviders: SyncDataProviders, + appSettings: AppSettings = AppUserDefaults() ) { self.appTrackingProtectionDatabase = appTrackingProtectionDatabase self.bookmarksDatabase = bookmarksDatabase self.bookmarksDatabaseCleaner = bookmarksDatabaseCleaner self.syncService = syncService self.syncDataProviders = syncDataProviders - self.favoritesViewModel = FavoritesListViewModel(bookmarksDatabase: bookmarksDatabase) + self.favoritesViewModel = FavoritesListViewModel(bookmarksDatabase: bookmarksDatabase, favoritesDisplayMode: appSettings.favoritesDisplayMode) self.bookmarksCachingSearch = BookmarksCachingSearch(bookmarksStore: CoreDataBookmarksSearchStore(bookmarksStore: bookmarksDatabase)) + self.appSettings = appSettings super.init(nibName: nil, bundle: nil) + bindFavoritesDisplayMode() + bindSyncService() + } +#else + init( + bookmarksDatabase: CoreDataDatabase, + bookmarksDatabaseCleaner: BookmarkDatabaseCleaner, + syncService: DDGSyncing, + syncDataProviders: SyncDataProviders, + appSettings: AppSettings + ) { + self.bookmarksDatabase = bookmarksDatabase + self.bookmarksDatabaseCleaner = bookmarksDatabaseCleaner + self.syncService = syncService + self.syncDataProviders = syncDataProviders + self.favoritesViewModel = FavoritesListViewModel(bookmarksDatabase: bookmarksDatabase, favoritesDisplayMode: appSettings.favoritesDisplayMode) + self.bookmarksCachingSearch = BookmarksCachingSearch(bookmarksStore: CoreDataBookmarksSearchStore(bookmarksStore: bookmarksDatabase)) + self.appSettings = appSettings + + super.init(nibName: nil, bundle: nil) + bindSyncService() } +#endif fileprivate var tabCountInfo: TabCountInfo? @@ -174,7 +211,8 @@ class MainViewController: UIViewController { super.viewDidLoad() viewCoordinator = MainViewFactory.createViewHierarchy(self.view) - + viewCoordinator.moveAddressBarToPosition(appSettings.currentAddressBarPosition) + viewCoordinator.toolbarBackButton.action = #selector(onBackPressed) viewCoordinator.toolbarForwardButton.action = #selector(onForwardPressed) viewCoordinator.toolbarFireButton.action = #selector(onFirePressed) @@ -200,6 +238,7 @@ class MainViewController: UIViewController { findInPageView.delegate = self findInPageBottomLayoutConstraint.constant = 0 registerForKeyboardNotifications() + registerForSyncPausedNotifications() applyTheme(ThemeManager.shared.currentTheme) @@ -210,7 +249,11 @@ class MainViewController: UIViewController { registerForApplicationEvents() registerForCookiesManagedNotification() + registerForSettingsChangeNotifications() + tabManager.cleanupTabsFaviconCache() + + refreshViewsBasedOnAddressBarPosition(appSettings.currentAddressBarPosition) } override func viewDidAppear(_ animated: Bool) { @@ -295,6 +338,80 @@ class MainViewController: UIViewController { object: nil) } + private func registerForSyncPausedNotifications() { + NotificationCenter.default.addObserver( + self, + selector: #selector(showSyncPausedError), + name: SyncBookmarksAdapter.bookmarksSyncLimitReached, + object: nil) + NotificationCenter.default.addObserver( + self, + selector: #selector(showSyncPausedError), + name: SyncCredentialsAdapter.credentialsSyncLimitReached, + object: nil) + } + + @objc private func showSyncPausedError(_ notification: Notification) { + Task { + await MainActor.run { + var title = UserText.syncBookmarkPausedAlertTitle + var description = UserText.syncBookmarkPausedAlertDescription + if notification.name == SyncCredentialsAdapter.credentialsSyncLimitReached { + title = UserText.syncCredentialsPausedAlertTitle + description = UserText.syncCredentialsPausedAlertDescription + } + if self.presentedViewController is SyncSettingsViewController { + return + } + self.presentedViewController?.dismiss(animated: true) + let alert = UIAlertController(title: title, + message: description, + preferredStyle: .alert) + let learnMoreAction = UIAlertAction(title: UserText.syncPausedAlertLearnMoreButton, style: .default) { _ in + self.segueToSettingsSync() + } + let okAction = UIAlertAction(title: UserText.syncPausedAlertOkButton, style: .cancel) + alert.addAction(learnMoreAction) + alert.addAction(okAction) + self.present(alert, animated: true) + } + } + } + + func registerForSettingsChangeNotifications() { + NotificationCenter.default.addObserver(self, selector: + #selector(onAddressBarPositionChanged), + name: AppUserDefaults.Notifications.addressBarPositionChanged, + object: nil) + } + + @objc func onAddressBarPositionChanged() { + viewCoordinator.moveAddressBarToPosition(appSettings.currentAddressBarPosition) + refreshViewsBasedOnAddressBarPosition(appSettings.currentAddressBarPosition) + } + + func refreshViewsBasedOnAddressBarPosition(_ position: AddressBarPosition) { + switch position { + case .top: + viewCoordinator.omniBar.moveSeparatorToBottom() + viewCoordinator.showToolbarSeparator() + + case .bottom: + viewCoordinator.omniBar.moveSeparatorToTop() + // If this is called before the toolbar has shown it will not re-add the separator when moving to the top position + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { + self.viewCoordinator.hideToolbarSeparator() + } + } + + let theme = ThemeManager.shared.currentTheme + self.decorate(with: theme) + } + + @objc func onShowFullSiteAddressChanged() { + refreshOmniBar() + } + /// Based on https://stackoverflow.com/a/46117073/73479 /// Handles iPhone X devices properly. @objc private func keyboardWillChangeFrame(_ notification: Notification) { @@ -303,6 +420,10 @@ class MainViewController: UIViewController { let keyboardFrame = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue else { return } + let duration: TimeInterval = (userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0 + let animationCurveRawNSN = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber + let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIView.AnimationOptions.curveEaseInOut.rawValue + let animationCurve = UIView.AnimationOptions(rawValue: animationCurveRaw) var height = keyboardFrame.size.height @@ -313,7 +434,7 @@ class MainViewController: UIViewController { findInPageBottomLayoutConstraint.constant = height currentTab?.webView.scrollView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: height, right: 0) - + if let suggestionsTray = suggestionTrayController { let suggestionsFrameInView = suggestionsTray.view.convert(suggestionsTray.contentFrame, to: view) @@ -325,20 +446,20 @@ class MainViewController: UIViewController { } } - animateForKeyboard(userInfo: userInfo, y: view.frame.height - height) - } - - private func animateForKeyboard(userInfo: [AnyHashable: Any], y: CGFloat) { - let duration: TimeInterval = (userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? NSNumber)?.doubleValue ?? 0 - let animationCurveRawNSN = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? NSNumber - let animationCurveRaw = animationCurveRawNSN?.uintValue ?? UIView.AnimationOptions.curveEaseInOut.rawValue - let animationCurve = UIView.AnimationOptions(rawValue: animationCurveRaw) - + let y = self.view.frame.height - height let frame = self.findInPageView.frame UIView.animate(withDuration: duration, delay: 0, options: animationCurve, animations: { self.findInPageView.frame = CGRect(x: 0, y: y - frame.height, width: frame.width, height: frame.height) }, completion: nil) + if self.appSettings.currentAddressBarPosition.isBottom { + let navBarOffset = min(0, self.toolbarHeight - intersection.height) + self.viewCoordinator.constraints.omniBarBottom.constant = navBarOffset + UIView.animate(withDuration: duration, delay: 0, options: animationCurve) { + self.viewCoordinator.navigationBarContainer.superview?.layoutIfNeeded() + } + } + } private func initTabButton() { @@ -357,12 +478,26 @@ class MainViewController: UIViewController { } private func initBookmarksButton() { - omniBar.bookmarksButton.addGestureRecognizer(UILongPressGestureRecognizer(target: self, + viewCoordinator.omniBar.bookmarksButton.addGestureRecognizer(UILongPressGestureRecognizer(target: self, action: #selector(quickSaveBookmarkLongPress(gesture:)))) gestureBookmarksButton.delegate = self gestureBookmarksButton.image = UIImage(named: "Bookmarks") } + private func bindFavoritesDisplayMode() { + favoritesDisplayModeCancellable = NotificationCenter.default.publisher(for: AppUserDefaults.Notifications.favoritesDisplayModeChange) + .receive(on: DispatchQueue.main) + .sink { [weak self] _ in + guard let self else { + return + } + self.menuBookmarksViewModel.favoritesDisplayMode = self.appSettings.favoritesDisplayMode + self.favoritesViewModel.favoritesDisplayMode = self.appSettings.favoritesDisplayMode + self.homeController?.collectionView.reloadData() + WidgetCenter.shared.reloadAllTimelines() + } + } + private func bindSyncService() { localUpdatesCancellable = favoritesViewModel.localUpdates .sink { [weak self] in @@ -387,7 +522,8 @@ class MainViewController: UIViewController { @objc func quickSaveBookmark() { UIImpactFeedbackGenerator(style: .heavy).impactOccurred() guard currentTab != nil else { - ActionMessageView.present(message: UserText.webSaveBookmarkNone) + ActionMessageView.present(message: UserText.webSaveBookmarkNone, + presentationLocation: .withBottomBar(andAddressBarBottom: appSettings.currentAddressBarPosition.isBottom)) return } @@ -479,13 +615,10 @@ class MainViewController: UIViewController { } private func attachOmniBar() { - omniBar = OmniBar.loadFromXib() - omniBar.omniDelegate = self - omniBar.menuButtonContent.delegate = self - omniBar.frame = viewCoordinator.navigationBarContainer.bounds - viewCoordinator.navigationBarContainer.addSubview(omniBar) + viewCoordinator.omniBar.omniDelegate = self + viewCoordinator.omniBar.menuButtonContent.delegate = self } - + fileprivate func attachHomeScreen() { viewCoordinator.logoContainer.isHidden = false findInPageView.isHidden = true @@ -496,10 +629,16 @@ class MainViewController: UIViewController { AppDependencyProvider.shared.homePageConfiguration.refresh() let tabModel = currentTab?.tabModel + +#if APP_TRACKING_PROTECTION let controller = HomeViewController.loadFromStoryboard(model: tabModel!, favoritesViewModel: favoritesViewModel, + appSettings: appSettings, appTPDatabase: appTrackingProtectionDatabase) - +#else + let controller = HomeViewController.loadFromStoryboard(model: tabModel!, favoritesViewModel: favoritesViewModel, appSettings: appSettings) +#endif + homeController = controller controller.chromeDelegate = self @@ -597,7 +736,7 @@ class MainViewController: UIViewController { func enterSearch() { if presentedViewController == nil { showBars() - omniBar.becomeFirstResponder() + viewCoordinator.omniBar.becomeFirstResponder() } } @@ -636,8 +775,8 @@ class MainViewController: UIViewController { allowContentUnderflow = false request() guard let tab = currentTab else { fatalError("no tab") } - select(tab: tab) dismissOmniBar() + select(tab: tab) } private func addTab(url: URL?, inheritedAttribution: AdClickAttributionLogic.State?) { @@ -689,7 +828,7 @@ class MainViewController: UIViewController { fileprivate func updateCurrentTab() { if let currentTab = currentTab { select(tab: currentTab) - omniBar.resignFirstResponder() + viewCoordinator.omniBar.resignFirstResponder() } else { attachHomeScreen() } @@ -711,25 +850,25 @@ class MainViewController: UIViewController { private func refreshOmniBar() { guard let tab = currentTab, tab.link != nil else { - omniBar.stopBrowsing() + viewCoordinator.omniBar.stopBrowsing() return } - omniBar.refreshText(forUrl: tab.url) + viewCoordinator.omniBar.refreshText(forUrl: tab.url) if tab.isError { - omniBar.hidePrivacyIcon() + viewCoordinator.omniBar.hidePrivacyIcon() } else if let privacyInfo = tab.privacyInfo, privacyInfo.url.host == tab.url?.host { - omniBar.updatePrivacyIcon(for: privacyInfo) + viewCoordinator.omniBar.updatePrivacyIcon(for: privacyInfo) } else { - omniBar.resetPrivacyIcon(for: tab.url) + viewCoordinator.omniBar.resetPrivacyIcon(for: tab.url) } - omniBar.startBrowsing() + viewCoordinator.omniBar.startBrowsing() } func dismissOmniBar() { - omniBar.resignFirstResponder() + viewCoordinator.omniBar.resignFirstResponder() hideSuggestionTray() refreshOmniBar() } @@ -738,8 +877,8 @@ class MainViewController: UIViewController { viewCoordinator.toolbarBackButton.isEnabled = currentTab?.canGoBack ?? false viewCoordinator.toolbarForwardButton.isEnabled = currentTab?.canGoForward ?? false - omniBar.backButton.isEnabled = viewCoordinator.toolbarBackButton.isEnabled - omniBar.forwardButton.isEnabled = viewCoordinator.toolbarForwardButton.isEnabled + viewCoordinator.omniBar.backButton.isEnabled = viewCoordinator.toolbarBackButton.isEnabled + viewCoordinator.omniBar.forwardButton.isEnabled = viewCoordinator.toolbarForwardButton.isEnabled } override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { @@ -786,7 +925,7 @@ class MainViewController: UIViewController { if homeController != nil { expectedState = .bookmarksImage viewCoordinator.lastToolbarButton.accessibilityLabel = UserText.bookmarksButtonHint - omniBar.menuButton.accessibilityLabel = UserText.bookmarksButtonHint + viewCoordinator.omniBar.menuButton.accessibilityLabel = UserText.bookmarksButtonHint } else { if presentedViewController is BrowsingMenuViewController { @@ -795,7 +934,7 @@ class MainViewController: UIViewController { expectedState = .menuImage } viewCoordinator.lastToolbarButton.accessibilityLabel = UserText.menuButtonHint - omniBar.menuButton.accessibilityLabel = UserText.menuButtonHint + viewCoordinator.omniBar.menuButton.accessibilityLabel = UserText.menuButtonHint } presentedMenuButton.decorate(with: ThemeManager.shared.currentTheme) @@ -804,7 +943,7 @@ class MainViewController: UIViewController { private func applyWidthToTrayController() { if AppWidthObserver.shared.isLargeWidth { - self.suggestionTrayController?.float(withWidth: self.omniBar.searchStackContainer.frame.width + 24) + self.suggestionTrayController?.float(withWidth: self.viewCoordinator.omniBar.searchStackContainer.frame.width + 24) } else { self.suggestionTrayController?.fill() } @@ -813,13 +952,13 @@ class MainViewController: UIViewController { private func applyLargeWidth() { viewCoordinator.tabBarContainer.isHidden = false viewCoordinator.toolbar.isHidden = true - omniBar.enterPadState() + viewCoordinator.omniBar.enterPadState() } private func applySmallWidth() { viewCoordinator.tabBarContainer.isHidden = true viewCoordinator.toolbar.isHidden = false - omniBar.enterPhoneState() + viewCoordinator.omniBar.enterPhoneState() } @discardableResult @@ -838,8 +977,8 @@ class MainViewController: UIViewController { if !DaxDialogs.shared.shouldShowFireButtonPulse { ViewHighlighter.hideAll() } - if type.hideOmnibarSeparator() { - omniBar.hideSeparator() + if type.hideOmnibarSeparator() && appSettings.currentAddressBarPosition != .bottom { + viewCoordinator.omniBar.hideSeparator() } } viewCoordinator.suggestionTrayContainer.isHidden = false @@ -847,13 +986,13 @@ class MainViewController: UIViewController { } func hideSuggestionTray() { - omniBar.showSeparator() + viewCoordinator.omniBar.showSeparator() viewCoordinator.suggestionTrayContainer.isHidden = true currentTab?.webView.accessibilityElementsHidden = false suggestionTrayController?.didHide() } - fileprivate func launchAutofillLogins(with currentTabUrl: URL? = nil) { + func launchAutofillLogins(with currentTabUrl: URL? = nil) { let appSettings = AppDependencyProvider.shared.appSettings let autofillSettingsViewController = AutofillLoginSettingsListViewController( appSettings: appSettings, @@ -893,12 +1032,10 @@ class MainViewController: UIViewController { notificationView.setTitle(text: title) notificationView.setMessage(text: message) viewCoordinator.notificationBarContainer.addSubview(notificationView) - viewCoordinator.constraints.notificationContainerTop.constant = -notificationView.frame.size.height self.notificationView = notificationView DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { - self.viewCoordinator.constraints.notificationContainerTop.constant = 0 - self.viewCoordinator.constraints.notificationContainerHeight.constant = notificationView.frame.size.height + self.viewCoordinator.constraints.notificationContainerHeight.constant = notificationView.frame.height UIView.animate(withDuration: 0.3) { self.view.layoutIfNeeded() } @@ -908,12 +1045,13 @@ class MainViewController: UIViewController { func hideNotification() { - viewCoordinator.constraints.notificationContainerTop.constant = -(notificationView?.frame.size.height ?? 0) viewCoordinator.constraints.notificationContainerHeight.constant = 0 UIView.animate(withDuration: 0.5, animations: { - self.view.layoutIfNeeded() + if let frame = self.notificationView?.frame { + self.notificationView?.frame = frame.offsetBy(dx: 0, dy: -frame.height) + } + self.view.layoutSubviews() }, completion: { _ in - self.viewCoordinator.constraints.notificationContainerTop.constant = 0 self.notificationView?.removeFromSuperview() }) @@ -938,16 +1076,6 @@ class MainViewController: UIViewController { tabsBarController?.backgroundTabAdded() } - func replaceToolbar(item target: UIBarButtonItem, with replacement: UIBarButtonItem) { - guard let items = viewCoordinator.toolbar.items else { return } - - let newItems = items.compactMap({ - $0 == target ? replacement : $0 - }) - - viewCoordinator.toolbar.setItems(newItems, animated: false) - } - func newTab(reuseExisting: Bool = false, allowingKeyboard: Bool = true) { if DaxDialogs.shared.shouldShowFireButtonPulse { ViewHighlighter.hideAll() @@ -987,7 +1115,7 @@ class MainViewController: UIViewController { private func showVoiceSearch() { // https://app.asana.com/0/0/1201408131067987 UIMenuController.shared.hideMenu() - omniBar.removeTextSelection() + viewCoordinator.omniBar.removeTextSelection() Pixel.fire(pixel: .openVoiceSearch) let voiceSearchController = VoiceSearchViewController() @@ -1032,6 +1160,7 @@ class MainViewController: UIViewController { @objc private func onDuckDuckGoEmailSignOut(_ notification: Notification) { fireEmailPixel(.emailDisabled, notification: notification) + presentEmailProtectionSignInAlertIfNeeded(notification) if let object = notification.object as? EmailManager, let emailManager = syncDataProviders.settingsAdapter.emailManager, object !== emailManager { @@ -1039,7 +1168,18 @@ class MainViewController: UIViewController { syncService.scheduler.notifyDataChanged() } } - + + private func presentEmailProtectionSignInAlertIfNeeded(_ notification: Notification) { + guard let userInfo = notification.userInfo as? [String: String], + userInfo[EmailManager.NotificationParameter.isForcedSignOut] != nil else { + return + } + let alertController = CriticalAlerts.makeEmailProtectionSignInAlert() + dismiss(animated: true) { + self.present(alertController, animated: true, completion: nil) + } + } + private func fireEmailPixel(_ pixel: Pixel.Event, notification: Notification) { var pixelParameters: [String: String] = [:] @@ -1079,6 +1219,10 @@ extension MainViewController: BrowserChromeDelegate { viewCoordinator.tabBarContainer } + var omniBar: OmniBar { + viewCoordinator.omniBar + } + private func hideKeyboard() { dismissOmniBar() _ = findInPageView.resignFirstResponder() @@ -1104,7 +1248,7 @@ extension MainViewController: BrowserChromeDelegate { self.view.layoutIfNeeded() - self.omniBar.alpha = percent + self.viewCoordinator.omniBar.alpha = percent self.viewCoordinator.tabBarContainer.alpha = percent self.viewCoordinator.toolbar.alpha = percent } @@ -1120,7 +1264,7 @@ extension MainViewController: BrowserChromeDelegate { if hidden { hideKeyboard() } updateNavBarConstant(hidden ? 0 : 1.0) - omniBar.alpha = hidden ? 0 : 1 + viewCoordinator.omniBar.alpha = hidden ? 0 : 1 viewCoordinator.tabBarContainer.alpha = hidden ? 0 : 1 viewCoordinator.statusBackground.alpha = hidden ? 0 : 1 } @@ -1138,7 +1282,7 @@ extension MainViewController: BrowserChromeDelegate { } var barsMaxHeight: CGFloat { - return max(toolbarHeight, omniBar.frame.size.height) + return max(toolbarHeight, viewCoordinator.omniBar.frame.size.height) } // 1.0 - full size, 0.0 - hidden @@ -1255,7 +1399,7 @@ extension MainViewController: OmniBarDelegate { func onEnterPressed() { guard !viewCoordinator.suggestionTrayContainer.isHidden else { return } - suggestionTrayController?.willDismiss(with: omniBar.textField.text ?? "") + suggestionTrayController?.willDismiss(with: viewCoordinator.omniBar.textField.text ?? "") } func onDismissed() { @@ -1313,7 +1457,7 @@ extension MainViewController: OmniBarDelegate { func onSharePressed() { hideSuggestionTray() guard let link = currentTab?.link else { return } - currentTab?.onShareAction(forLink: link, fromView: omniBar.shareButton, orginatedFromMenu: false) + currentTab?.onShareAction(forLink: link, fromView: viewCoordinator.omniBar.shareButton, orginatedFromMenu: false) } func onVoiceSearchPressed() { @@ -1342,6 +1486,7 @@ extension MainViewController: FavoritesOverlayDelegate { } showHomeRowReminder() } + } extension MainViewController: AutocompleteViewControllerDelegate { @@ -1367,26 +1512,23 @@ extension MainViewController: AutocompleteViewControllerDelegate { func autocomplete(pressedPlusButtonForSuggestion suggestion: Suggestion) { if let url = suggestion.url { if url.isDuckDuckGoSearch { - omniBar.textField.text = suggestion.suggestion + viewCoordinator.omniBar.textField.text = suggestion.suggestion } else if !url.isBookmarklet() { - omniBar.textField.text = url.absoluteString + viewCoordinator.omniBar.textField.text = url.absoluteString } } else { - omniBar.textField.text = suggestion.suggestion + viewCoordinator.omniBar.textField.text = suggestion.suggestion } - omniBar.textDidChange() + viewCoordinator.omniBar.textDidChange() } func autocomplete(highlighted suggestion: Suggestion, for query: String) { if let url = suggestion.url { - omniBar.textField.text = url.absoluteString + viewCoordinator.omniBar.textField.text = url.absoluteString } else { - omniBar.textField.text = suggestion.suggestion - - if suggestion.suggestion.hasPrefix(query), - let fromPosition = omniBar.textField.position(from: omniBar.textField.beginningOfDocument, offset: query.count) { - omniBar.textField.selectedTextRange = omniBar.textField.textRange(from: fromPosition, - to: omniBar.textField.endOfDocument) + viewCoordinator.omniBar.textField.text = suggestion.suggestion + if suggestion.suggestion.hasPrefix(query) { + viewCoordinator.omniBar.selectTextToEnd(query.count) } } } @@ -1394,6 +1536,7 @@ extension MainViewController: AutocompleteViewControllerDelegate { func autocompleteWasDismissed() { dismissOmniBar() } + } extension MainViewController: HomeControllerDelegate { @@ -1527,7 +1670,7 @@ extension MainViewController: TabDelegate { func tab(_ tab: TabViewController, didChangePrivacyInfo privacyInfo: PrivacyInfo?) { if currentTab == tab { - omniBar.updatePrivacyIcon(for: privacyInfo) + viewCoordinator.omniBar.updatePrivacyIcon(for: privacyInfo) } } @@ -1594,15 +1737,14 @@ extension MainViewController: TabDelegate { } func tabDidRequestSearchBarRect(tab: TabViewController) -> CGRect { - let view = UIApplication.shared.windows.filter({ $0.isKeyWindow }).first?.rootViewController?.view - return omniBar.searchContainer.convert(omniBar.searchContainer.bounds, to: view) + searchBarRect } func tab(_ tab: TabViewController, didRequestPresentingTrackerAnimation privacyInfo: PrivacyInfo, isCollapsing: Bool) { guard tabManager.current === tab else { return } - omniBar?.startTrackersAnimation(privacyInfo, forDaxDialog: !isCollapsing) + viewCoordinator.omniBar?.startTrackersAnimation(privacyInfo, forDaxDialog: !isCollapsing) } func tabDidRequestShowingMenuHighlighter(tab: TabViewController) { @@ -1816,7 +1958,8 @@ extension MainViewController: AutoClearWorker { DaxDialogs.shared.resumeRegularFlow() self.forgetTabs() } onTransitionCompleted: { - ActionMessageView.present(message: UserText.actionForgetAllDone) + ActionMessageView.present(message: UserText.actionForgetAllDone, + presentationLocation: .withBottomBar(andAddressBarBottom: self.appSettings.currentAddressBarPosition.isBottom)) transitionCompletion?() } completion: { Instruments.shared.endTimedEvent(for: spid) @@ -1855,8 +1998,12 @@ extension MainViewController: AutoClearWorker { extension MainViewController: Themable { func decorate(with theme: Theme) { + setNeedsStatusBarAppearanceUpdate() + // Does not appear to get updated when setting changes. + tabsBarController?.decorate(with: theme) + if AppWidthObserver.shared.isLargeWidth { viewCoordinator.statusBackground.backgroundColor = theme.tabsBarBackgroundColor } else { @@ -1868,12 +2015,13 @@ extension MainViewController: Themable { viewCoordinator.navigationBarContainer.backgroundColor = theme.barBackgroundColor viewCoordinator.navigationBarContainer.tintColor = theme.barTintColor - omniBar.decorate(with: theme) + viewCoordinator.omniBar.decorate(with: theme) + viewCoordinator.progress.decorate(with: theme) viewCoordinator.toolbar.barTintColor = theme.barBackgroundColor viewCoordinator.toolbar.tintColor = theme.barTintColor - + tabSwitcherButton.decorate(with: theme) gestureBookmarksButton.decorate(with: theme) viewCoordinator.toolbarTabSwitcherButton.tintColor = theme.barTintColor @@ -1885,8 +2033,12 @@ extension MainViewController: Themable { findInPageView.decorate(with: theme) viewCoordinator.logoText.tintColor = theme.ddgTextTintColor + + if appSettings.currentAddressBarPosition == .bottom { + viewCoordinator.statusBackground.backgroundColor = theme.backgroundColor + } } - + } extension MainViewController: OnboardingDelegate { @@ -1956,11 +2108,11 @@ extension MainViewController { } let backMenu = historyMenu(with: currentTab.webView.backForwardList.backList.reversed()) - omniBar.backButton.menu = backMenu + viewCoordinator.omniBar.backButton.menu = backMenu viewCoordinator.toolbarBackButton.menu = backMenu let forwardMenu = historyMenu(with: currentTab.webView.backForwardList.forwardList) - omniBar.forwardButton.menu = forwardMenu + viewCoordinator.omniBar.forwardButton.menu = forwardMenu viewCoordinator.toolbarForwardButton.menu = forwardMenu } diff --git a/DuckDuckGo/NetworkProtectionAccessController.swift b/DuckDuckGo/NetworkProtectionAccessController.swift new file mode 100644 index 0000000000..7b1faaacce --- /dev/null +++ b/DuckDuckGo/NetworkProtectionAccessController.swift @@ -0,0 +1,125 @@ +// +// NetworkProtectionAccessController.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if NETWORK_PROTECTION + +import Foundation +import BrowserServicesKit +import ContentBlocking +import Core +import NetworkProtection +import Waitlist + +enum NetworkProtectionAccessType { + /// Used if the user does not have waitlist feature flag access + case none + + /// Used if the user has waitlist feature flag access, but has not joined the waitlist + case waitlistAvailable + + /// Used if the user has waitlist feature flag access, and has joined the waitlist + case waitlistJoined + + /// Used if the user has been invited via the waitlist, but needs to accept the Privacy Policy and Terms of Service + case waitlistInvitedPendingTermsAcceptance + + /// Used if the user has been invited via the waitlist and has accepted the Privacy Policy and Terms of Service + case waitlistInvited + + /// Used if the user has been invited to test Network Protection directly + case inviteCodeInvited +} + +protocol NetworkProtectionAccess { + func networkProtectionAccessType() -> NetworkProtectionAccessType +} + +struct NetworkProtectionAccessController: NetworkProtectionAccess { + + private let networkProtectionActivation: NetworkProtectionFeatureActivation + private let networkProtectionWaitlistStorage: WaitlistStorage + private let networkProtectionTermsAndConditionsStore: NetworkProtectionTermsAndConditionsStore + private let featureFlagger: FeatureFlagger + + init( + networkProtectionActivation: NetworkProtectionFeatureActivation = NetworkProtectionKeychainTokenStore(), + networkProtectionWaitlistStorage: WaitlistStorage = WaitlistKeychainStore(waitlistIdentifier: VPNWaitlist.identifier), + networkProtectionTermsAndConditionsStore: NetworkProtectionTermsAndConditionsStore = NetworkProtectionTermsAndConditionsUserDefaultsStore(), + featureFlagger: FeatureFlagger = AppDependencyProvider.shared.featureFlagger + ) { + self.networkProtectionActivation = networkProtectionActivation + self.networkProtectionWaitlistStorage = networkProtectionWaitlistStorage + self.networkProtectionTermsAndConditionsStore = networkProtectionTermsAndConditionsStore + self.featureFlagger = featureFlagger + } + + func networkProtectionAccessType() -> NetworkProtectionAccessType { + // First, check for users who have activated the VPN via an invite code: + if networkProtectionActivation.isFeatureActivated && !networkProtectionWaitlistStorage.isInvited { + return .inviteCodeInvited + } + + // Next, check if the waitlist is still active; if not, the user has no access. + let isWaitlistActive = featureFlagger.isFeatureOn(.networkProtectionWaitlistActive) + if !isWaitlistActive { + return .none + } + + // Next, check if a waitlist user has NetP access and whether they need to accept T&C. + if networkProtectionActivation.isFeatureActivated && networkProtectionWaitlistStorage.isInvited { + if networkProtectionTermsAndConditionsStore.networkProtectionWaitlistTermsAndConditionsAccepted { + return .waitlistInvited + } else { + return .waitlistInvitedPendingTermsAcceptance + } + } + + // Next, check if the user has waitlist access at all and whether they've already joined. + let hasWaitlistAccess = featureFlagger.isFeatureOn(.networkProtectionWaitlistAccess) + if hasWaitlistAccess { + if networkProtectionWaitlistStorage.isOnWaitlist { + return .waitlistJoined + } else { + return .waitlistAvailable + } + } + + return .none + } + + func refreshNetworkProtectionAccess() { + guard networkProtectionActivation.isFeatureActivated else { + return + } + + if !featureFlagger.isFeatureOn(.networkProtectionWaitlistActive) { + networkProtectionWaitlistStorage.deleteWaitlistState() + try? NetworkProtectionKeychainTokenStore().deleteToken() + + Task { + let controller = NetworkProtectionTunnelController() + await controller.stop() + await controller.removeVPN() + } + } + } + +} + +#endif diff --git a/DuckDuckGo/NetworkProtectionConvenienceInitialisers.swift b/DuckDuckGo/NetworkProtectionConvenienceInitialisers.swift index 248b52aa81..2557f2f1d3 100644 --- a/DuckDuckGo/NetworkProtectionConvenienceInitialisers.swift +++ b/DuckDuckGo/NetworkProtectionConvenienceInitialisers.swift @@ -54,7 +54,50 @@ extension NetworkProtectionKeychainTokenStore { extension NetworkProtectionCodeRedemptionCoordinator { convenience init() { - self.init(tokenStore: NetworkProtectionKeychainTokenStore(), errorEvents: .networkProtectionAppDebugEvents) + let tunnelSettings = TunnelSettings(defaults: .networkProtectionGroupDefaults) + self.init( + environment: tunnelSettings.selectedEnvironment, + tokenStore: NetworkProtectionKeychainTokenStore(), + errorEvents: .networkProtectionAppDebugEvents + ) + } +} + +extension NetworkProtectionVPNNotificationsViewModel { + convenience init() { + let notificationsSettingsStore = NetworkProtectionNotificationsSettingsUserDefaultsStore(userDefaults: .networkProtectionGroupDefaults) + self.init( + notificationsAuthorization: NotificationsAuthorizationController(), + notificationsSettingsStore: notificationsSettingsStore + ) + } +} + +extension NetworkProtectionVPNSettingsViewModel { + convenience init() { + self.init( + tunnelSettings: TunnelSettings(defaults: .networkProtectionGroupDefaults) + ) + } +} + +extension NetworkProtectionLocationListCompositeRepository { + convenience init() { + let tunnelSettings = TunnelSettings(defaults: .networkProtectionGroupDefaults) + self.init( + environment: tunnelSettings.selectedEnvironment, + tokenStore: NetworkProtectionKeychainTokenStore() + ) + } +} + +extension NetworkProtectionVPNLocationViewModel { + convenience init() { + let locationListRepository = NetworkProtectionLocationListCompositeRepository() + self.init( + locationListRepository: locationListRepository, + tunnelSettings: TunnelSettings(defaults: .networkProtectionGroupDefaults) + ) } } diff --git a/DuckDuckGo/NetworkProtectionDebugUtilities.swift b/DuckDuckGo/NetworkProtectionDebugUtilities.swift index a3d47161b9..822d261b61 100644 --- a/DuckDuckGo/NetworkProtectionDebugUtilities.swift +++ b/DuckDuckGo/NetworkProtectionDebugUtilities.swift @@ -35,7 +35,7 @@ final class NetworkProtectionDebugUtilities { return } - try? activeSession.sendProviderMessage(.expireRegistrationKey) + try? await activeSession.sendProviderMessage(.expireRegistrationKey) } // MARK: - Notifications @@ -45,7 +45,7 @@ final class NetworkProtectionDebugUtilities { return } - try? activeSession.sendProviderMessage(.triggerTestNotification) + try? await activeSession.sendProviderMessage(.triggerTestNotification) } // MARK: - Failure Simulation @@ -58,7 +58,7 @@ final class NetworkProtectionDebugUtilities { guard let message = option.extensionMessage else { return } - try? activeSession.sendProviderMessage(message) + try? await activeSession.sendProviderMessage(message) } } diff --git a/DuckDuckGo/NetworkProtectionRootView.swift b/DuckDuckGo/NetworkProtectionRootView.swift index 3a31df02d0..0ec4d60535 100644 --- a/DuckDuckGo/NetworkProtectionRootView.swift +++ b/DuckDuckGo/NetworkProtectionRootView.swift @@ -22,6 +22,7 @@ import SwiftUI import NetworkProtection +@available(iOS 15, *) struct NetworkProtectionRootView: View { let model = NetworkProtectionRootViewModel() let inviteCompletion: () -> Void @@ -42,10 +43,4 @@ struct NetworkProtectionRootView: View { } } -struct NetworkProtectionRootView_Previews: PreviewProvider { - static var previews: some View { - NetworkProtectionRootView { } - } -} - #endif diff --git a/DuckDuckGo/NetworkProtectionRootViewController.swift b/DuckDuckGo/NetworkProtectionRootViewController.swift index 485f586f5d..8e3f21bdee 100644 --- a/DuckDuckGo/NetworkProtectionRootViewController.swift +++ b/DuckDuckGo/NetworkProtectionRootViewController.swift @@ -21,6 +21,7 @@ import SwiftUI +@available(iOS 15, *) final class NetworkProtectionRootViewController: UIHostingController { init(inviteCompletion: @escaping () -> Void = { }) { @@ -39,6 +40,7 @@ final class NetworkProtectionRootViewController: UIHostingController some View { Section { if let location = statusModel.location { - NetworkProtectionServerItemView( - imageID: "Server-Location-24", - title: UserText.netPStatusViewLocation, - value: location - ) + NavigationLink(destination: NetworkProtectionVPNLocationView()) { + NetworkProtectionServerItemView( + imageID: "Server-Location-24", + title: UserText.netPStatusViewLocation, + value: location + ) + } } if let ipAddress = statusModel.ipAddress { NetworkProtectionServerItemView( @@ -125,7 +129,21 @@ struct NetworkProtectionStatusView: View { ) } } header: { - Text(UserText.netPStatusViewConnectionDetails).foregroundColor(.textPrimary) + Text(UserText.netPStatusViewConnectionDetails).foregroundColor(.textSecondary) + } + } + + @ViewBuilder + private func settings() -> some View { + Section { + NavigationLink(UserText.netPVPNSettingsTitle, destination: NetworkProtectionVPNSettingsView()) + .font(.system(size: 16)) + .foregroundColor(.textPrimary) + NavigationLink(UserText.netPVPNNotificationsTitle, destination: NetworkProtectionVPNNotificationsView()) + .font(.system(size: 16)) + .foregroundColor(.textPrimary) + } header: { + Text(UserText.netPStatusViewSettingsSectionTitle).foregroundColor(.textSecondary) } footer: { inviteOnlyFooter() } @@ -183,41 +201,6 @@ private struct NetworkProtectionServerItemView: View { } } -private extension View { - @ViewBuilder - func hideScrollContentBackground() -> some View { - if #available(iOS 16, *) { - self.scrollContentBackground(.hidden) - } else { - let originalBackgroundColor = UITableView.appearance().backgroundColor - self.onAppear { - UITableView.appearance().backgroundColor = .clear - }.onDisappear { - UITableView.appearance().backgroundColor = originalBackgroundColor - } - } - } - - @ViewBuilder - func applyListStyle() -> some View { - self - .listStyle(.insetGrouped) - .hideScrollContentBackground() - .background( - Rectangle().ignoresSafeArea().foregroundColor(Color.viewBackground) - ) - } - - @ViewBuilder - func increaseHeaderProminence() -> some View { - if #available(iOS 15, *) { - self.headerProminence(.increased) - } else { - self - } - } -} - private extension Color { static let textPrimary = Color(designSystemColor: .textPrimary) static let textSecondary = Color(designSystemColor: .textSecondary) @@ -226,10 +209,4 @@ private extension Color { static let controlColor = Color(designSystemColor: .accent) } -struct NetworkProtectionStatusView_Previews: PreviewProvider { - static var previews: some View { - NetworkProtectionStatusView(statusModel: NetworkProtectionStatusViewModel()) - } -} - #endif diff --git a/DuckDuckGo/NetworkProtectionStatusViewModel.swift b/DuckDuckGo/NetworkProtectionStatusViewModel.swift index d9348f8caa..0a9af7e667 100644 --- a/DuckDuckGo/NetworkProtectionStatusViewModel.swift +++ b/DuckDuckGo/NetworkProtectionStatusViewModel.swift @@ -22,6 +22,7 @@ import Foundation import Combine import NetworkProtection +import WidgetKit final class NetworkProtectionStatusViewModel: ObservableObject { private static var dateFormatter: DateComponentsFormatter = { @@ -65,10 +66,13 @@ final class NetworkProtectionStatusViewModel: ObservableObject { @Published public var location: String? @Published public var ipAddress: String? + @Published public var animationsOn: Bool = false + public init(tunnelController: TunnelController = NetworkProtectionTunnelController(), statusObserver: ConnectionStatusObserver = ConnectionStatusObserverThroughSession(), serverInfoObserver: ConnectionServerInfoObserver = ConnectionServerInfoObserverThroughSession(), - errorObserver: ConnectionErrorObserver = ConnectionErrorObserverThroughSession()) { + errorObserver: ConnectionErrorObserver = ConnectionErrorObserverThroughSession(), + locationListRepository: NetworkProtectionLocationListRepository = NetworkProtectionLocationListCompositeRepository()) { self.tunnelController = tunnelController self.statusObserver = statusObserver self.serverInfoObserver = serverInfoObserver @@ -83,18 +87,10 @@ final class NetworkProtectionStatusViewModel: ObservableObject { setUpDisableTogglePublisher() setUpServerInfoPublishers() - errorObserver.publisher - .map { - $0.map { _ in - ErrorItem( - title: UserText.netPStatusViewErrorConnectionFailedTitle, - message: UserText.netPStatusViewErrorConnectionFailedMessage - ) - } - } - .receive(on: DispatchQueue.main) - .assign(to: \.error, onWeaklyHeld: self) - .store(in: &cancellables) + // Prefetching this now for snappy load times on the locations screens + Task { + _ = try? await locationListRepository.fetchLocationList() + } } private func setUpIsConnectedStatePublishers() { @@ -179,12 +175,18 @@ final class NetworkProtectionStatusViewModel: ObservableObject { .store(in: &cancellables) } + @MainActor func didToggleNetP(to enabled: Bool) async { + // This is to prevent weird looking animations on navigating to the screen. + // It makes sense as animations should mostly only happen when a user has interacted. + animationsOn = true if enabled { await enableNetP() } else { await disableNetP() } + + WidgetCenter.shared.reloadTimelines(ofKind: "VPNStatusWidget") } @MainActor diff --git a/DuckDuckGo/NetworkProtectionTermsAndConditionsStore.swift b/DuckDuckGo/NetworkProtectionTermsAndConditionsStore.swift new file mode 100644 index 0000000000..46a6c024e2 --- /dev/null +++ b/DuckDuckGo/NetworkProtectionTermsAndConditionsStore.swift @@ -0,0 +1,32 @@ +// +// NetworkProtectionTermsAndConditionsStore.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import Core + +protocol NetworkProtectionTermsAndConditionsStore { + var networkProtectionWaitlistTermsAndConditionsAccepted: Bool { get set } +} + +struct NetworkProtectionTermsAndConditionsUserDefaultsStore: NetworkProtectionTermsAndConditionsStore { + + @UserDefaultsWrapper(key: .networkProtectionWaitlistTermsAndConditionsAccepted, defaultValue: false) + var networkProtectionWaitlistTermsAndConditionsAccepted: Bool + +} diff --git a/DuckDuckGo/NetworkProtectionTunnelController.swift b/DuckDuckGo/NetworkProtectionTunnelController.swift index 6a5bef318b..2a1d1b6f8c 100644 --- a/DuckDuckGo/NetworkProtectionTunnelController.swift +++ b/DuckDuckGo/NetworkProtectionTunnelController.swift @@ -67,6 +67,31 @@ final class NetworkProtectionTunnelController: TunnelController { tunnelManager.connection.stopVPNTunnel() } + func removeVPN() async { + try? await tunnelManager?.removeFromPreferences() + } + + // MARK: - Connection Status Querying + + /// Queries Network Protection to know if its VPN is connected. + /// + /// - Returns: `true` if the VPN is connected, connecting or reasserting, and `false` otherwise. + /// + var isConnected: Bool { + get async { + guard let tunnelManager = await loadTunnelManager() else { + return false + } + + switch tunnelManager.connection.status { + case .connected, .connecting, .reasserting: + return true + default: + return false + } + } + } + private func startWithError() async throws { let tunnelManager: NETunnelProviderManager diff --git a/DuckDuckGo/NetworkProtectionVPNLocationView.swift b/DuckDuckGo/NetworkProtectionVPNLocationView.swift new file mode 100644 index 0000000000..dc673400e3 --- /dev/null +++ b/DuckDuckGo/NetworkProtectionVPNLocationView.swift @@ -0,0 +1,200 @@ +// +// NetworkProtectionVPNLocationView.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if NETWORK_PROTECTION + +import Foundation +import SwiftUI + +@available(iOS 15, *) +struct NetworkProtectionVPNLocationView: View { + @StateObject var model = NetworkProtectionVPNLocationViewModel() + + var body: some View { + List { + nearest(isSelected: model.isNearestSelected) + countries() + } + .applyInsetGroupedListStyle() + .animation(.default, value: model.state.isLoading) + .navigationTitle(UserText.netPVPNLocationTitle) + .onAppear { + Task { + await model.onViewAppeared() + } + } + } + + @ViewBuilder + private func nearest(isSelected: Bool) -> some View { + Section { + ChecklistItem( + isSelected: isSelected, + action: { + Task { + await model.onNearestItemSelection() + } + }, label: { + Text(UserText.netPPreferredLocationNearest) + .foregroundStyle(Color.textPrimary) + .font(.system(size: 16)) + } + ) + } header: { + Text(UserText.netPVPNLocationRecommendedSectionTitle) + .foregroundStyle(Color.textPrimary) + } footer: { + Text(UserText.netPVPNLocationRecommendedSectionFooter) + .foregroundStyle(Color.textSecondary) + .font(.system(size: 13)) + .padding(.top, 6) + } + } + + @ViewBuilder + private func countries() -> some View { + Section { + switch model.state { + case .loading: + EmptyView() + .listRowBackground(Color.clear) + case .loaded(let countryItems): + ForEach(countryItems) { item in + CountryItem(itemModel: item) { + Task { + await model.onCountryItemSelection(id: item.id) + } + } cityPickerAction: { cityId in + Task { + await model.onCountryItemSelection(id: item.id, cityId: cityId) + } + } + } + } + } header: { + Text(UserText.netPVPNLocationAllCountriesSectionTitle) + .foregroundStyle(Color.textPrimary) + } + .animation(.default, value: model.state.isLoading) + } +} + +@available(iOS 15, *) +private struct CountryItem: View { + let itemModel: NetworkProtectionVPNCountryItemModel + let action: () -> Void + let cityPickerAction: (String?) -> Void + + init(itemModel: NetworkProtectionVPNCountryItemModel, action: @escaping () -> Void, cityPickerAction: @escaping (String?) -> Void) { + self.itemModel = itemModel + self.action = action + self.cityPickerAction = cityPickerAction + } + + var body: some View { + ChecklistItem( + isSelected: itemModel.isSelected, + action: action, + label: { + Text(itemModel.emoji) + VStack(alignment: .leading, spacing: 4) { + Text(itemModel.title) + .font(.system(size: 16)) + .foregroundStyle(Color.textPrimary) + if let subtitle = itemModel.subtitle { + Text(subtitle) + .font(.system(size: 13)) + .foregroundStyle(Color.textSecondary) + } + } + if itemModel.shouldShowPicker { + Spacer() + Menu { + ForEach(itemModel.cityPickerItems) { cityItem in + MenuItem(isSelected: cityItem.isSelected, title: cityItem.name) { + cityPickerAction(cityItem.id) + } + } + } label: { + Image(systemName: "ellipsis.circle") + .resizable() + .frame(width: 22, height: 22) + } + } + } + ) + } +} + +@available(iOS 15, *) +private struct ChecklistItem: View where Content: View { + let isSelected: Bool + let action: () -> Void + @ViewBuilder let label: () -> Content + + var body: some View { + Button( + action: action, + label: { + HStack(spacing: 12) { + Image(systemName: "checkmark") + .tint(.controlColor) + .if(!isSelected) { + $0.hidden() + } + label() + } + } + ) + .tint(Color(designSystemColor: .textPrimary)) + .listRowInsets(EdgeInsets(top: 14, leading: 16, bottom: 14, trailing: 16)) + } +} + +@available(iOS 15, *) +private struct MenuItem: View { + let isSelected: Bool + let title: String + let action: () -> Void + + var body: some View { + Button( + action: action, + label: { + HStack(spacing: 12) { + Text(title) + Spacer() + Image(systemName: "checkmark") + .if(!isSelected) { + $0.hidden() + } + } + } + ) + .tint(Color(designSystemColor: .textPrimary)) + } +} + +private extension Color { + static let textPrimary = Color(designSystemColor: .textPrimary) + static let textSecondary = Color(designSystemColor: .textSecondary) + static let controlColor = Color(designSystemColor: .accent) +} + +#endif diff --git a/DuckDuckGo/NetworkProtectionVPNLocationViewModel.swift b/DuckDuckGo/NetworkProtectionVPNLocationViewModel.swift new file mode 100644 index 0000000000..c67feabce2 --- /dev/null +++ b/DuckDuckGo/NetworkProtectionVPNLocationViewModel.swift @@ -0,0 +1,159 @@ +// +// NetworkProtectionVPNLocationViewModel.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if NETWORK_PROTECTION + +import Foundation +import Combine +import NetworkProtection + +final class NetworkProtectionVPNLocationViewModel: ObservableObject { + private let locationListRepository: NetworkProtectionLocationListRepository + private let tunnelSettings: TunnelSettings + @Published public var state: LoadingState + @Published public var isNearestSelected: Bool + + enum LoadingState { + case loading + case loaded(countryItems: [NetworkProtectionVPNCountryItemModel]) + + var isLoading: Bool { + switch self { + case .loading: + return true + case .loaded: + return false + } + } + } + + init(locationListRepository: NetworkProtectionLocationListRepository, tunnelSettings: TunnelSettings) { + self.locationListRepository = locationListRepository + self.tunnelSettings = tunnelSettings + state = .loading + self.isNearestSelected = tunnelSettings.selectedLocation == .nearest + } + + func onViewAppeared() async { + await reloadList() + } + + func onNearestItemSelection() async { + tunnelSettings.selectedLocation = .nearest + await reloadList() + } + + func onCountryItemSelection(id: String, cityId: String? = nil) async { + let location = NetworkProtectionSelectedLocation(country: id, city: cityId) + tunnelSettings.selectedLocation = .location(location) + await reloadList() + } + + @MainActor + private func reloadList() async { + guard let list = try? await locationListRepository.fetchLocationList() else { return } + let selectedLocation = self.tunnelSettings.selectedLocation + let isNearestSelected = selectedLocation == .nearest + + let countryItems = list.map { currentLocation in + let isCountrySelected: Bool + let isNearestCitySelected: Bool + var cityPickerItems: [CityItem] + + switch selectedLocation { + case .location(let location): + isCountrySelected = location.country == currentLocation.country + isNearestCitySelected = location.city == nil && isCountrySelected + cityPickerItems = currentLocation.cities.map { currentCity in + let isCitySelected = currentCity.name == location.city + return CityItem(city: currentCity, isSelected: isCitySelected) + } + case .nearest: + isCountrySelected = false + isNearestCitySelected = false + cityPickerItems = currentLocation.cities.map { currentCity in + CityItem(city: currentCity, isSelected: false) + } + } + + let nearestItem = CityItem( + city: nil, + isSelected: isNearestCitySelected + ) + cityPickerItems.insert(nearestItem, at: 0) + + return NetworkProtectionVPNCountryItemModel( + netPLocation: currentLocation, + isSelected: isCountrySelected, + cityPickerItems: cityPickerItems + ) + } + self.isNearestSelected = isNearestSelected + state = .loaded(countryItems: countryItems) + } +} + +private typealias CountryItem = NetworkProtectionVPNCountryItemModel +private typealias CityItem = NetworkProtectionVPNCityItemModel + +struct NetworkProtectionVPNCountryItemModel: Identifiable { + let isSelected: Bool + var id: String + let emoji: String + let title: String + let subtitle: String? + let cityPickerItems: [NetworkProtectionVPNCityItemModel] + let shouldShowPicker: Bool + + fileprivate init(netPLocation: NetworkProtectionLocation, isSelected: Bool, cityPickerItems: [NetworkProtectionVPNCityItemModel]) { + self.isSelected = isSelected + self.id = netPLocation.country + self.title = Locale.current.localizedString(forRegionCode: id) ?? id + let hasMultipleCities = netPLocation.cities.count > 1 + self.subtitle = hasMultipleCities ? UserText.netPVPNLocationCountryItemFormattedCitiesCount(netPLocation.cities.count) : nil + self.cityPickerItems = cityPickerItems + self.emoji = Self.flag(country: netPLocation.country) + self.shouldShowPicker = hasMultipleCities + } + + static func flag(country: String) -> String { + let flagBase = UnicodeScalar("🇦").value - UnicodeScalar("A").value + + let flag = country + .uppercased() + .unicodeScalars + .compactMap({ UnicodeScalar(flagBase + $0.value)?.description }) + .joined() + return flag + } +} + +struct NetworkProtectionVPNCityItemModel: Identifiable { + let id: String? + let name: String + let isSelected: Bool + + fileprivate init(city: NetworkProtectionLocation.City?, isSelected: Bool) { + self.id = city?.name + self.name = city?.name ?? UserText.netPPreferredLocationNearest + self.isSelected = isSelected + } +} + +#endif diff --git a/DuckDuckGo/NetworkProtectionVPNNotificationsView.swift b/DuckDuckGo/NetworkProtectionVPNNotificationsView.swift new file mode 100644 index 0000000000..d5b0d22fd2 --- /dev/null +++ b/DuckDuckGo/NetworkProtectionVPNNotificationsView.swift @@ -0,0 +1,90 @@ +// +// NetworkProtectionVPNNotificationsView.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if NETWORK_PROTECTION + +import SwiftUI +import UIKit +import NetworkProtection +import Core + +@available(iOS 15, *) +struct NetworkProtectionVPNNotificationsView: View { + @StateObject var model = NetworkProtectionVPNNotificationsViewModel() + + var body: some View { + List { + switch model.viewKind { + case .loading: + EmptyView() + case .unauthorized: + unauthorizedView + case .authorized: + authorizedView + } + } + .animation(.default, value: model.viewKind) + .applyInsetGroupedListStyle() + .navigationTitle(UserText.netPVPNNotificationsTitle).onAppear { + Task { + await model.onViewAppeared() + } + } + } + + @ViewBuilder + private var unauthorizedView: some View { + Section { + Button(UserText.netPTurnOnNotificationsButtonTitle) { + model.turnOnNotifications() + } + .foregroundColor(.controlColor) + } footer: { + Text(UserText.netPTurnOnNotificationsSectionFooter) + .foregroundColor(.textSecondary) + .font(.system(size: 13)) + .padding(.top, 6) + } + } + + @ViewBuilder + private var authorizedView: some View { + Section { + Toggle(UserText.netPVPNAlertsToggleTitle, isOn: Binding( + get: { model.alertsEnabled }, + set: model.didToggleAlerts(to:) + )) + .toggleStyle(SwitchToggleStyle(tint: .controlColor)) + } footer: { + Text(UserText.netPVPNAlertsToggleSectionFooter) + .foregroundColor(.textSecondary) + .font(.system(size: 13)) + .padding(.top, 6) + } + } +} + +private extension Color { + static let textPrimary = Color(designSystemColor: .textPrimary) + static let textSecondary = Color(designSystemColor: .textSecondary) + static let cellBackground = Color(designSystemColor: .surface) + static let controlColor = Color(designSystemColor: .accent) +} + +#endif diff --git a/DuckDuckGo/NetworkProtectionVPNNotificationsViewModel.swift b/DuckDuckGo/NetworkProtectionVPNNotificationsViewModel.swift new file mode 100644 index 0000000000..62833eb25a --- /dev/null +++ b/DuckDuckGo/NetworkProtectionVPNNotificationsViewModel.swift @@ -0,0 +1,79 @@ +// +// NetworkProtectionVPNNotificationsViewModel.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if NETWORK_PROTECTION + +import Combine +import UserNotifications +import NetworkProtection + +enum NetworkProtectionNotificationsViewKind: Equatable { + case loading + case unauthorized + case authorized +} + +final class NetworkProtectionVPNNotificationsViewModel: ObservableObject { + private var notificationsAuthorization: NotificationsAuthorizationControlling + private var notificationsSettingsStore: NetworkProtectionNotificationsSettingsStore + @Published var viewKind: NetworkProtectionNotificationsViewKind = .loading + var alertsEnabled: Bool { + self.notificationsSettingsStore.alertsEnabled + } + + init(notificationsAuthorization: NotificationsAuthorizationControlling, + notificationsSettingsStore: NetworkProtectionNotificationsSettingsStore) { + self.notificationsAuthorization = notificationsAuthorization + self.notificationsSettingsStore = notificationsSettingsStore + self.notificationsAuthorization.delegate = self + } + + @MainActor + func onViewAppeared() async { + let status = await notificationsAuthorization.authorizationStatus + updateViewKind(for: status) + } + + func turnOnNotifications() { + notificationsAuthorization.requestAlertAuthorization() + } + + func didToggleAlerts(to enabled: Bool) { + notificationsSettingsStore.alertsEnabled = enabled + } + + private func updateViewKind(for authorizationStatus: UNAuthorizationStatus) { + switch authorizationStatus { + case .notDetermined, .denied: + viewKind = .unauthorized + case .authorized, .ephemeral, .provisional: + viewKind = .authorized + @unknown default: + assertionFailure("Unhandled enum case") + } + } +} + +extension NetworkProtectionVPNNotificationsViewModel: NotificationsPermissionsControllerDelegate { + func authorizationStateDidChange(toStatus status: UNAuthorizationStatus) { + updateViewKind(for: status) + } +} + +#endif diff --git a/DuckDuckGo/NetworkProtectionVPNSettingsView.swift b/DuckDuckGo/NetworkProtectionVPNSettingsView.swift new file mode 100644 index 0000000000..f22075f044 --- /dev/null +++ b/DuckDuckGo/NetworkProtectionVPNSettingsView.swift @@ -0,0 +1,86 @@ +// +// NetworkProtectionVPNSettingsView.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if NETWORK_PROTECTION + +import SwiftUI +import DesignResourcesKit + +@available(iOS 15, *) +struct NetworkProtectionVPNSettingsView: View { + @StateObject var viewModel = NetworkProtectionVPNSettingsViewModel() + + var body: some View { + List { + NavigationLink(destination: NetworkProtectionVPNLocationView()) { + HStack { + Text(UserText.netPPreferredLocationSettingTitle).daxBodyRegular().foregroundColor(.textPrimary) + Spacer() + Text(viewModel.preferredLocation).daxBodyRegular().foregroundColor(.textSecondary) + } + } + toggleSection( + text: UserText.netPAlwaysOnSettingTitle, + footerText: UserText.netPAlwaysOnSettingFooter + ) + toggleSection( + text: UserText.netPSecureDNSSettingTitle, + footerText: UserText.netPSecureDNSSettingFooter + ) + } + .applyInsetGroupedListStyle() + .navigationTitle(UserText.netPVPNSettingsTitle) + } + + @ViewBuilder + func toggleSection(text: String, footerText: String) -> some View { + Section { + HStack { + VStack(alignment: .leading, spacing: 4) { + Text(text) + .font(.system(size: 16)) + .foregroundColor(.textPrimary.opacity(0.4)) + .font(.system(size: 13)) + .foregroundColor(.textSecondary.opacity(0.4)) + } + + // These toggles are permanantly disabled as the features are permanantly enabled. Product decision. + Toggle("", isOn: .constant(true)) + .disabled(true) + .toggleStyle(SwitchToggleStyle(tint: .controlColor)) + } + .listRowBackground(Color.cellBackground) + } footer: { + Text(footerText) + .foregroundColor(.textSecondary) + .accentColor(Color.controlColor) + .font(.system(size: 13)) + .padding(.top, 6) + } + } +} + +private extension Color { + static let textPrimary = Color(designSystemColor: .textPrimary) + static let textSecondary = Color(designSystemColor: .textSecondary) + static let cellBackground = Color(designSystemColor: .surface) + static let controlColor = Color(designSystemColor: .accent) +} + +#endif diff --git a/DuckDuckGo/NetworkProtectionVPNSettingsViewModel.swift b/DuckDuckGo/NetworkProtectionVPNSettingsViewModel.swift new file mode 100644 index 0000000000..19664143e8 --- /dev/null +++ b/DuckDuckGo/NetworkProtectionVPNSettingsViewModel.swift @@ -0,0 +1,50 @@ +// +// NetworkProtectionVPNSettingsViewModel.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if NETWORK_PROTECTION + +import Foundation +import NetworkProtection +import Combine + +final class NetworkProtectionVPNSettingsViewModel: ObservableObject { + private let tunnelSettings: TunnelSettings + private var cancellable: AnyCancellable? + + @Published public var preferredLocation: String = UserText.netPPreferredLocationNearest + + init(tunnelSettings: TunnelSettings) { + self.tunnelSettings = tunnelSettings + cancellable = tunnelSettings.selectedLocationPublisher.map { selectedLocation in + guard let selectedLocation = selectedLocation.location else { + return UserText.netPPreferredLocationNearest + } + guard let city = selectedLocation.city else { + return Self.localizedString(forRegionCode: selectedLocation.country) + } + return "\(city), \(Self.localizedString(forRegionCode: selectedLocation.country))" + }.assign(to: \.preferredLocation, onWeaklyHeld: self) + } + + private static func localizedString(forRegionCode: String) -> String { + Locale.current.localizedString(forRegionCode: forRegionCode) ?? forRegionCode.capitalized + } +} + +#endif diff --git a/DuckDuckGo/NetworkProtectionWidgetRefreshModel.swift b/DuckDuckGo/NetworkProtectionWidgetRefreshModel.swift new file mode 100644 index 0000000000..b9b5871ffc --- /dev/null +++ b/DuckDuckGo/NetworkProtectionWidgetRefreshModel.swift @@ -0,0 +1,46 @@ +// +// NetworkProtectionWidgetRefreshModel.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if NETWORK_PROTECTION + +import Foundation +import Combine +import NetworkExtension +import WidgetKit + +class NetworkProtectionWidgetRefreshModel { + + private var cancellable: AnyCancellable? + + public func beginObservingVPNStatus() { + cancellable = NotificationCenter.default.publisher(for: .NEVPNStatusDidChange) + .debounce(for: .seconds(0.5), scheduler: RunLoop.main) + .receive(on: DispatchQueue.main) + .sink { [weak self] _ in + self?.refreshVPNWidget() + } + } + + public func refreshVPNWidget() { + WidgetCenter.shared.reloadTimelines(ofKind: "VPNStatusWidget") + } + +} + +#endif diff --git a/DuckDuckGo/NotificationsAuthorizationController.swift b/DuckDuckGo/NotificationsAuthorizationController.swift new file mode 100644 index 0000000000..a5524a4cc9 --- /dev/null +++ b/DuckDuckGo/NotificationsAuthorizationController.swift @@ -0,0 +1,87 @@ +// +// NotificationsAuthorizationController.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UserNotifications +import UIKit +import Combine + +protocol NotificationsAuthorizationControlling { + var authorizationStatus: UNAuthorizationStatus { get async } + var delegate: NotificationsPermissionsControllerDelegate? { get set } + + func requestAlertAuthorization() +} + +protocol NotificationsPermissionsControllerDelegate: AnyObject { + func authorizationStateDidChange(toStatus status: UNAuthorizationStatus) +} + +final class NotificationsAuthorizationController: NotificationsAuthorizationControlling { + + weak var delegate: NotificationsPermissionsControllerDelegate? + var notificationCancellable: AnyCancellable? + + var authorizationStatus: UNAuthorizationStatus { + get async { + let settings: UNNotificationSettings = await UNUserNotificationCenter.current().notificationSettings() + return settings.authorizationStatus + } + } + + init() { + // To handle navigating back from iOS Settings after changing the authorization + notificationCancellable = NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification) + .sink { _ in + Task { [weak self] in + await self?.updateDelegateWithNewState() + } + } + } + + func requestAlertAuthorization() { + Task { + switch await authorizationStatus { + case .notDetermined: + await requestAuthorization(options: .alert) + case .denied: + _ = await UIApplication.shared.openAppNotificationSettings() + case .authorized, .provisional, .ephemeral: + break + @unknown default: + break + } + } + } + + private func requestAuthorization(options: UNAuthorizationOptions) async { + do { + let authorized = try await UNUserNotificationCenter.current().requestAuthorization(options: options) + if authorized { + await updateDelegateWithNewState() + } + } catch { } + } + + private func updateDelegateWithNewState() async { + let newState = await authorizationStatus + DispatchQueue.main.async { [weak self] in + self?.delegate?.authorizationStateDidChange(toStatus: newState) + } + } +} diff --git a/DuckDuckGo/OmniBar.swift b/DuckDuckGo/OmniBar.swift index 9c48133305..bb8c6944ff 100644 --- a/DuckDuckGo/OmniBar.swift +++ b/DuckDuckGo/OmniBar.swift @@ -62,6 +62,7 @@ class OmniBar: UIView { @IBOutlet var searchContainerMaxWidthConstraint: NSLayoutConstraint! @IBOutlet var omniBarLeadingConstraint: NSLayoutConstraint! @IBOutlet var omniBarTrailingConstraint: NSLayoutConstraint! + @IBOutlet var separatorToBottom: NSLayoutConstraint! weak var omniDelegate: OmniBarDelegate? fileprivate var state: OmniBarState = SmallOmniBarState.HomeNonEditingState() @@ -69,12 +70,24 @@ class OmniBar: UIView { private var privacyIconAndTrackersAnimator = PrivacyIconAndTrackersAnimator() private var notificationAnimator = OmniBarNotificationAnimator() - - + static func loadFromXib() -> OmniBar { return OmniBar.load(nibName: "OmniBar") } + private let appSettings: AppSettings + + required init?(coder: NSCoder) { + appSettings = AppDependencyProvider.shared.appSettings + super.init(coder: coder) + } + + // Tests require this + override init(frame: CGRect) { + appSettings = AppDependencyProvider.shared.appSettings + super.init(frame: frame) + } + override func awakeFromNib() { super.awakeFromNib() configureMenuButton() @@ -186,6 +199,14 @@ class OmniBar: UIView { separatorView.isHidden = true } + func moveSeparatorToTop() { + separatorToBottom.constant = frame.height + } + + func moveSeparatorToBottom() { + separatorToBottom.constant = 0 + } + func startBrowsing() { refreshState(state.onBrowsingStartedState) } @@ -260,6 +281,11 @@ class OmniBar: UIView { } } + func selectTextToEnd(_ offset: Int) { + guard let fromPosition = textField.position(from: textField.beginningOfDocument, offset: offset) else { return } + textField.selectedTextRange = textField.textRange(from: fromPosition, to: textField.endOfDocument) + } + fileprivate func refreshState(_ newState: OmniBarState) { if state.name != newState.name { os_log("OmniBar entering %s from %s", log: .generalLog, type: .debug, newState.name, state.name) @@ -296,6 +322,11 @@ class OmniBar: UIView { } updateOmniBarPadding() + + UIView.animate(withDuration: 0.0) { + self.layoutIfNeeded() + } + } private func updateOmniBarPadding() { diff --git a/DuckDuckGo/PassKitPreviewHelper.swift b/DuckDuckGo/PassKitPreviewHelper.swift index 6b95cbe2e6..3a734eee03 100644 --- a/DuckDuckGo/PassKitPreviewHelper.swift +++ b/DuckDuckGo/PassKitPreviewHelper.swift @@ -34,8 +34,9 @@ class PassKitPreviewHelper: FilePreview { do { let data = try Data(contentsOf: self.filePath) let pass = try PKPass(data: data) - let controller = PKAddPassesViewController(pass: pass)! - viewController?.present(controller, animated: true) + if let controller = PKAddPassesViewController(pass: pass) { + viewController?.present(controller, animated: true) + } } catch { os_log("Can't present passkit: %s", type: .debug, error.localizedDescription) } diff --git a/DuckDuckGo/PasswordGenerationPromptView.swift b/DuckDuckGo/PasswordGenerationPromptView.swift index 1ed7fcdc0f..470084db8f 100644 --- a/DuckDuckGo/PasswordGenerationPromptView.swift +++ b/DuckDuckGo/PasswordGenerationPromptView.swift @@ -42,8 +42,12 @@ struct PasswordGenerationPromptView: View { .zIndex(1) VStack { + Spacer() + .frame(height: Const.Size.topPadding) + AutofillViews.AppIconHeader() + Spacer() + .frame(height: Const.Size.headlineTopPadding) AutofillViews.Headline(title: UserText.autofillPasswordGenerationPromptTitle) - .padding(.top, Const.Size.topPadding) if #available(iOS 16.0, *) { passwordView .padding([.top, .bottom], passwordVerticalPadding) @@ -185,6 +189,7 @@ private enum Const { static let closeButtonOffsetPortrait: CGFloat = 44.0 static let closeButtonOffsetPortraitSmallFrame: CGFloat = 16.0 static let topPadding: CGFloat = 56.0 + static let headlineTopPadding: CGFloat = 24.0 static let ios15scrollOffset: CGFloat = 80.0 static let passwordButtonSpacing: CGFloat = 10.0 static let passwordPaddingHeight: CGFloat = 28.0 diff --git a/DuckDuckGo/PrivacyDashboardViewController.swift b/DuckDuckGo/PrivacyDashboardViewController.swift index 52f548313b..319e31b91e 100644 --- a/DuckDuckGo/PrivacyDashboardViewController.swift +++ b/DuckDuckGo/PrivacyDashboardViewController.swift @@ -121,9 +121,10 @@ extension PrivacyDashboardViewController: Themable { } extension PrivacyDashboardViewController: PrivacyDashboardControllerDelegate { - - func privacyDashboardController(_ privacyDashboardController: PrivacyDashboardController, didChangeProtectionSwitch isEnabled: Bool) { - privacyDashboardProtectionSwitchChangeHandler(enabled: isEnabled) + + func privacyDashboardController(_ privacyDashboardController: PrivacyDashboardController, + didChangeProtectionSwitch protectionState: ProtectionState) { + privacyDashboardProtectionSwitchChangeHandler(enabled: protectionState.isProtected) } func privacyDashboardController(_ privacyDashboardController: PrivacyDashboardController, didRequestOpenUrlInNewTab url: URL) { diff --git a/DuckDuckGo/RemoteMessageRequest.swift b/DuckDuckGo/RemoteMessageRequest.swift index 02cd59b8a9..2bfcd698fa 100644 --- a/DuckDuckGo/RemoteMessageRequest.swift +++ b/DuckDuckGo/RemoteMessageRequest.swift @@ -30,7 +30,7 @@ public struct RemoteMessageRequest { #if DEBUG return URL(string: "https://raw.githubusercontent.com/duckduckgo/remote-messaging-config/main/samples/ios/sample1.json")! #else - return URL(string: "https://staticcdn.duckduckgo.com/remotemessaging/config/staging/ios-config.json")! + return URL(string: "https://staticcdn.duckduckgo.com/remotemessaging/config/v1/ios-config.json")! #endif } diff --git a/DuckDuckGo/RemoteMessaging.swift b/DuckDuckGo/RemoteMessaging.swift index dd0efb9808..ee6c3d897c 100644 --- a/DuckDuckGo/RemoteMessaging.swift +++ b/DuckDuckGo/RemoteMessaging.swift @@ -44,14 +44,17 @@ struct RemoteMessaging { return Date().timeIntervalSince(Self.lastRemoteMessagingRefreshDate) > Constants.minimumConfigurationRefreshInterval } - static func registerBackgroundRefreshTaskHandler(bookmarksDatabase: CoreDataDatabase) { + static func registerBackgroundRefreshTaskHandler( + bookmarksDatabase: CoreDataDatabase, + favoritesDisplayMode: @escaping @autoclosure () -> FavoritesDisplayMode + ) { BGTaskScheduler.shared.register(forTaskWithIdentifier: Constants.backgroundRefreshTaskIdentifier, using: nil) { task in guard shouldRefresh else { task.setTaskCompleted(success: true) scheduleBackgroundRefreshTask() return } - backgroundRefreshTaskHandler(bgTask: task, bookmarksDatabase: bookmarksDatabase) + backgroundRefreshTaskHandler(bgTask: task, bookmarksDatabase: bookmarksDatabase, favoritesDisplayMode: favoritesDisplayMode()) } } @@ -77,10 +80,14 @@ struct RemoteMessaging { #endif } - static func backgroundRefreshTaskHandler(bgTask: BGTask, bookmarksDatabase: CoreDataDatabase) { + static func backgroundRefreshTaskHandler( + bgTask: BGTask, + bookmarksDatabase: CoreDataDatabase, + favoritesDisplayMode: @escaping @autoclosure () -> FavoritesDisplayMode + ) { let fetchAndProcessTask = Task { do { - try await Self.fetchAndProcess(bookmarksDatabase: bookmarksDatabase) + try await Self.fetchAndProcess(bookmarksDatabase: bookmarksDatabase, favoritesDisplayMode: favoritesDisplayMode()) Self.lastRemoteMessagingRefreshDate = Date() scheduleBackgroundRefreshTask() bgTask.setTaskCompleted(success: true) @@ -97,24 +104,28 @@ struct RemoteMessaging { } /// Convenience function - static func fetchAndProcess(bookmarksDatabase: CoreDataDatabase) async throws { + static func fetchAndProcess(bookmarksDatabase: CoreDataDatabase, favoritesDisplayMode: FavoritesDisplayMode) async throws { var bookmarksCount = 0 var favoritesCount = 0 let context = bookmarksDatabase.makeContext(concurrencyType: .privateQueueConcurrencyType) context.performAndWait { + let displayedFavoritesFolder = BookmarkUtils.fetchFavoritesFolder(withUUID: favoritesDisplayMode.displayedFolder.rawValue, in: context)! + let bookmarksCountRequest = BookmarkEntity.fetchRequest() - bookmarksCountRequest.predicate = NSPredicate(format: "%K == nil AND %K == false AND %K == false", - #keyPath(BookmarkEntity.favoriteFolder), - #keyPath(BookmarkEntity.isFolder), - #keyPath(BookmarkEntity.isPendingDeletion)) + bookmarksCountRequest.predicate = NSPredicate(format: "SUBQUERY(%K, $x, $x CONTAINS %@).@count == 0 AND %K == false AND %K == false", + #keyPath(BookmarkEntity.favoriteFolders), + displayedFavoritesFolder, + #keyPath(BookmarkEntity.isFolder), + #keyPath(BookmarkEntity.isPendingDeletion)) bookmarksCount = (try? context.count(for: bookmarksCountRequest)) ?? 0 let favoritesCountRequest = BookmarkEntity.fetchRequest() - bookmarksCountRequest.predicate = NSPredicate(format: "%K != nil AND %K == false AND %K == false", - #keyPath(BookmarkEntity.favoriteFolder), - #keyPath(BookmarkEntity.isFolder), - #keyPath(BookmarkEntity.isPendingDeletion)) + favoritesCountRequest.predicate = NSPredicate(format: "%K CONTAINS %@ AND %K == false AND %K == false", + #keyPath(BookmarkEntity.favoriteFolders), + displayedFavoritesFolder, + #keyPath(BookmarkEntity.isFolder), + #keyPath(BookmarkEntity.isPendingDeletion)) favoritesCount = (try? context.count(for: favoritesCountRequest)) ?? 0 } diff --git a/DuckDuckGo/SaveAutofillLoginManager.swift b/DuckDuckGo/SaveAutofillLoginManager.swift index 139e12c12d..725b2f823d 100644 --- a/DuckDuckGo/SaveAutofillLoginManager.swift +++ b/DuckDuckGo/SaveAutofillLoginManager.swift @@ -94,6 +94,13 @@ final class SaveAutofillLoginManager: SaveAutofillLoginManagerProtocol { } } + func isNeverPromptWebsiteForDomain() -> Bool { + guard let domain = credentials.account.domain else { + return false + } + return AppDependencyProvider.shared.autofillNeverPromptWebsitesManager.hasNeverPromptWebsitesFor(domain: domain) + } + private var savedMatchingPasswordWithoutUsername: SecureVaultModels.WebsiteCredentials? { let credentialsWithSamePassword = domainStoredCredentials.filter { storedCredentials in storedCredentials.password == credentials.password && (storedCredentials.account.username?.count ?? 0) == 0 diff --git a/DuckDuckGo/SaveLoginView.swift b/DuckDuckGo/SaveLoginView.swift index 4e80ca014a..f2dc2cb311 100644 --- a/DuckDuckGo/SaveLoginView.swift +++ b/DuckDuckGo/SaveLoginView.swift @@ -46,10 +46,8 @@ struct SaveLoginView: View { private var title: String { switch layoutType { - case .newUser, .saveLogin: + case .newUser, .saveLogin, .savePassword: return UserText.autofillSaveLoginTitleNewUser - case .savePassword: - return UserText.autofillSavePasswordTitle case .updateUsername: return UserText.autofillUpdateUsernameTitle case .updatePassword: @@ -59,9 +57,7 @@ struct SaveLoginView: View { private var confirmButton: String { switch layoutType { - case .newUser, .saveLogin: - return UserText.autofillSaveLoginSaveCTA - case .savePassword: + case .newUser, .saveLogin, .savePassword: return UserText.autofillSavePasswordSaveCTA case .updateUsername: return UserText.autofillUpdateUsernameSaveCTA @@ -90,7 +86,7 @@ struct SaveLoginView: View { VStack { Spacer() .frame(height: Const.Size.topPadding) - AutofillViews.WebsiteWithFavicon(accountDomain: viewModel.accountDomain) + AutofillViews.AppIconHeader() Spacer() .frame(height: Const.Size.headlineTopPadding) AutofillViews.Headline(title: title) @@ -141,8 +137,8 @@ struct SaveLoginView: View { AutofillViews.PrimaryButton(title: confirmButton, action: viewModel.save) - AutofillViews.TertiaryButton(title: UserText.autofillSaveLoginNotNowCTA, - action: viewModel.cancelButtonPressed) + AutofillViews.TertiaryButton(title: UserText.autofillSaveLoginNeverPromptCTA, + action: viewModel.neverPrompt) } } @@ -218,7 +214,7 @@ private enum Const { struct SaveLoginView_Previews: PreviewProvider { private struct MockManager: SaveAutofillLoginManagerProtocol { - + var username: String { "dax@duck.com" } var visiblePassword: String { "supersecurepasswordquack" } var isNewAccount: Bool { false } diff --git a/DuckDuckGo/SaveLoginViewController.swift b/DuckDuckGo/SaveLoginViewController.swift index 82d0afec9e..056546bed6 100644 --- a/DuckDuckGo/SaveLoginViewController.swift +++ b/DuckDuckGo/SaveLoginViewController.swift @@ -26,6 +26,7 @@ protocol SaveLoginViewControllerDelegate: AnyObject { func saveLoginViewController(_ viewController: SaveLoginViewController, didSaveCredentials credentials: SecureVaultModels.WebsiteCredentials) func saveLoginViewController(_ viewController: SaveLoginViewController, didUpdateCredentials credentials: SecureVaultModels.WebsiteCredentials) func saveLoginViewControllerDidCancel(_ viewController: SaveLoginViewController) + func saveLoginViewController(_ viewController: SaveLoginViewController, didRequestNeverPromptForWebsite domain: String) func saveLoginViewController(_ viewController: SaveLoginViewController, didRequestPresentConfirmKeepUsingAlertController alertController: UIAlertController) } @@ -136,6 +137,11 @@ extension SaveLoginViewController: SaveLoginViewModelDelegate { delegate?.saveLoginViewControllerDidCancel(self) } + func saveLoginViewModelNeverPrompt(_ viewModel: SaveLoginViewModel) { + Pixel.fire(pixel: .autofillLoginsSaveLoginModalExcludeSiteConfirmed) + delegate?.saveLoginViewController(self, didRequestNeverPromptForWebsite: viewModel.accountDomain) + } + func saveLoginViewModelConfirmKeepUsing(_ viewModel: SaveLoginViewModel, isAlreadyDismissed: Bool) { let isSelfPresentingAlert = !isAlreadyDismissed diff --git a/DuckDuckGo/SaveLoginViewModel.swift b/DuckDuckGo/SaveLoginViewModel.swift index 074b3b76d4..1586627994 100644 --- a/DuckDuckGo/SaveLoginViewModel.swift +++ b/DuckDuckGo/SaveLoginViewModel.swift @@ -24,6 +24,7 @@ import Core protocol SaveLoginViewModelDelegate: AnyObject { func saveLoginViewModelDidSave(_ viewModel: SaveLoginViewModel) func saveLoginViewModelDidCancel(_ viewModel: SaveLoginViewModel) + func saveLoginViewModelNeverPrompt(_ viewModel: SaveLoginViewModel) func saveLoginViewModelConfirmKeepUsing(_ viewModel: SaveLoginViewModel, isAlreadyDismissed: Bool) func saveLoginViewModelDidResizeContent(_ viewModel: SaveLoginViewModel, contentHeight: CGFloat) } @@ -180,4 +181,10 @@ final class SaveLoginViewModel: ObservableObject { autofillFirstTimeUser = false delegate?.saveLoginViewModelDidSave(self) } + + func neverPrompt() { + didSave = true + autofillFirstTimeUser = false + delegate?.saveLoginViewModelNeverPrompt(self) + } } diff --git a/DuckDuckGo/ScriptSourceProviding.swift b/DuckDuckGo/ScriptSourceProviding.swift index 5ac97820af..615dfc71a2 100644 --- a/DuckDuckGo/ScriptSourceProviding.swift +++ b/DuckDuckGo/ScriptSourceProviding.swift @@ -74,8 +74,10 @@ struct DefaultScriptSourceProvider: ScriptSourceProviding { private static func makeAutofillSource(privacyConfigurationManager: PrivacyConfigurationManaging, properties: ContentScopeProperties) -> AutofillUserScriptSourceProvider { - DefaultAutofillSourceProvider(privacyConfigurationManager: privacyConfigurationManager, - properties: properties) + return DefaultAutofillSourceProvider.Builder(privacyConfigurationManager: privacyConfigurationManager, + properties: properties) + .withJSLoading() + .build() } private static func buildContentBlockerRulesConfig(contentBlockingManager: ContentBlockerRulesManagerProtocol, diff --git a/DuckDuckGo/Settings.bundle/Root.plist b/DuckDuckGo/Settings.bundle/Root.plist index 2e4f5bb46b..cb5e57cf93 100644 --- a/DuckDuckGo/Settings.bundle/Root.plist +++ b/DuckDuckGo/Settings.bundle/Root.plist @@ -6,7 +6,7 @@ DefaultValue - 7.92.0 + 7.97.1 Key version Title diff --git a/DuckDuckGo/SettingsViewController.swift b/DuckDuckGo/SettingsViewController.swift index 2cc139c0a9..3a8a70a8a6 100644 --- a/DuckDuckGo/SettingsViewController.swift +++ b/DuckDuckGo/SettingsViewController.swift @@ -40,6 +40,8 @@ class SettingsViewController: UITableViewController { @IBOutlet weak var defaultBrowserCell: UITableViewCell! @IBOutlet weak var themeAccessoryText: UILabel! @IBOutlet weak var fireButtonAnimationAccessoryText: UILabel! + @IBOutlet weak var addressBarPositionCell: UITableViewCell! + @IBOutlet weak var addressBarPositionAccessoryText: UILabel! @IBOutlet weak var appIconCell: UITableViewCell! @IBOutlet weak var appIconImageView: UIImageView! @IBOutlet weak var autocompleteToggle: UISwitch! @@ -114,6 +116,14 @@ class SettingsViewController: UITableViewController { return featureFlagger.isFeatureOn(.sync) } + private var shouldShowTextSizeCell: Bool { + return UIDevice.current.userInterfaceIdiom != .pad + } + + private var shouldShowAddressBarPositionCell: Bool { + return UIDevice.current.userInterfaceIdiom != .pad + } + private lazy var shouldShowNetPCell: Bool = { #if NETWORK_PROTECTION if #available(iOS 15, *) { @@ -133,6 +143,7 @@ class SettingsViewController: UITableViewController { configureSyncCell() configureThemeCellAccessory() configureFireButtonAnimationCellAccessory() + configureAddressBarPositionCell() configureTextSizeCell() configureDisableAutocompleteToggle() configureSecurityToggles() @@ -161,6 +172,7 @@ class SettingsViewController: UITableViewController { super.viewWillAppear(animated) configureFireButtonAnimationCellAccessory() + configureAddressBarPositionCell() configureTextSizeCell() configureAutoClearCellAccessory() configureRememberLogins() @@ -170,6 +182,11 @@ class SettingsViewController: UITableViewController { configureEmailProtectionAccessoryText() configureMacBrowserWaitlistCell() configureWindowsBrowserWaitlistCell() + configureSyncCell() + +#if NETWORK_PROTECTION + updateNetPCellSubtitle(connectionStatus: connectionObserver.recentValue) +#endif // Make sure multiline labels are correctly presented tableView.setNeedsLayout() @@ -241,6 +258,10 @@ class SettingsViewController: UITableViewController { } private func configureSyncCell() { + syncCell.textLabel?.text = "Sync & Back Up" + if SyncBookmarksAdapter.isSyncBookmarksPaused || SyncCredentialsAdapter.isSyncCredentialsPaused { + syncCell.textLabel?.text = "⚠️ " + "Sync & Back Up" + } syncCell.isHidden = !shouldShowSyncCell } @@ -263,9 +284,14 @@ class SettingsViewController: UITableViewController { private func configureFireButtonAnimationCellAccessory() { fireButtonAnimationAccessoryText.text = appSettings.currentFireButtonAnimation.descriptionText } - + + private func configureAddressBarPositionCell() { + addressBarPositionCell.isHidden = !shouldShowAddressBarPositionCell + addressBarPositionAccessoryText.text = appSettings.currentAddressBarPosition.descriptionText + } + private func configureTextSizeCell() { - textSizeCell.isHidden = UIDevice.current.userInterfaceIdiom == .pad + textSizeCell.isHidden = !shouldShowTextSizeCell textSizeAccessoryText.text = "\(appSettings.textSize)%" } @@ -333,27 +359,35 @@ class SettingsViewController: UITableViewController { private func configureNetPCell() { netPCell.isHidden = !shouldShowNetPCell #if NETWORK_PROTECTION + updateNetPCellSubtitle(connectionStatus: connectionObserver.recentValue) connectionObserver.publisher .receive(on: DispatchQueue.main) .sink { [weak self] status in - let detailText: String - switch status { - case .connected: - detailText = UserText.netPCellConnected - default: - detailText = UserText.netPCellDisconnected - } - self?.netPCell.detailTextLabel?.text = detailText + self?.updateNetPCellSubtitle(connectionStatus: status) } .store(in: &cancellables) #endif } +#if NETWORK_PROTECTION + private func updateNetPCellSubtitle(connectionStatus: ConnectionStatus) { + switch NetworkProtectionAccessController().networkProtectionAccessType() { + case .none, .waitlistAvailable, .waitlistJoined, .waitlistInvitedPendingTermsAcceptance: + netPCell.detailTextLabel?.text = VPNWaitlist.shared.settingsSubtitle + case .waitlistInvited, .inviteCodeInvited: + switch connectionStatus { + case .connected: netPCell.detailTextLabel?.text = UserText.netPCellConnected + default: netPCell.detailTextLabel?.text = UserText.netPCellDisconnected + } + } + } +#endif + private func configureDebugCell() { debugCell.isHidden = !shouldShowDebugCell } - private func showSync(animated: Bool = true) { + func showSync(animated: Bool = true) { let controller = SyncSettingsViewController() navigationController?.pushViewController(controller, animated: animated) } @@ -402,16 +436,24 @@ class SettingsViewController: UITableViewController { } #if NETWORK_PROTECTION + @available(iOS 15, *) private func showNetP() { - // This will be tidied up as part of https://app.asana.com/0/0/1205084446087078/f - let rootViewController = NetworkProtectionRootViewController { [weak self] in - self?.navigationController?.popViewController(animated: true) - let newRootViewController = NetworkProtectionRootViewController() - self?.pushNetP(newRootViewController) + switch NetworkProtectionAccessController().networkProtectionAccessType() { + case .inviteCodeInvited, .waitlistInvited: + // This will be tidied up as part of https://app.asana.com/0/0/1205084446087078/f + let rootViewController = NetworkProtectionRootViewController { [weak self] in + self?.navigationController?.popViewController(animated: true) + let newRootViewController = NetworkProtectionRootViewController() + self?.pushNetP(newRootViewController) + } + + pushNetP(rootViewController) + default: + navigationController?.pushViewController(VPNWaitlistViewController(nibName: nil, bundle: nil), animated: true) } - pushNetP(rootViewController) } + @available(iOS 15, *) private func pushNetP(_ rootViewController: NetworkProtectionRootViewController) { navigationController?.pushViewController( rootViewController, @@ -434,15 +476,15 @@ class SettingsViewController: UITableViewController { let cell = tableView.cellForRow(at: indexPath) switch cell { - + case defaultBrowserCell: Pixel.fire(pixel: .defaultBrowserButtonPressedSettings) guard let url = URL(string: UIApplication.openSettingsURLString) else { return } UIApplication.shared.open(url) - + case emailProtectionCell: showEmailWebDashboard() - + case macBrowserWaitlistCell: showMacBrowserWaitlistViewController() @@ -454,14 +496,15 @@ class SettingsViewController: UITableViewController { case syncCell: showSync() - + case netPCell: + if #available(iOS 15, *) { #if NETWORK_PROTECTION - showNetP() + showNetP() #else - break + break #endif - + } default: break } @@ -504,6 +547,7 @@ class SettingsViewController: UITableViewController { return UITableView.automaticDimension } + /// Only use this to hide the header if the entire section can be conditionally hidden. override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { if syncSectionIndex == section && !shouldShowSyncCell { return CGFloat.leastNonzeroMagnitude @@ -516,6 +560,7 @@ class SettingsViewController: UITableViewController { } } + /// Only use this to hide the footer if the entire section can be conditionally hidden. override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { if syncSectionIndex == section && !shouldShowSyncCell { return CGFloat.leastNonzeroMagnitude @@ -523,19 +568,20 @@ class SettingsViewController: UITableViewController { return CGFloat.leastNonzeroMagnitude } else if debugSectionIndex == section && !shouldShowDebugCell { return CGFloat.leastNonzeroMagnitude - } else if moreFromDDGSectionIndex == section && !shouldShowNetPCell { - return CGFloat.leastNonzeroMagnitude } else { return super.tableView(tableView, heightForFooterInSection: section) } } + /// Only use this if the *last cell* in the section is to be conditionally hidden in order to retain the section rounding. + /// If your cell is not the last you don't need to modify the number of rows. override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { let rows = super.tableView(tableView, numberOfRowsInSection: section) - if section == appearanceSectionIndex && textSizeCell.isHidden { - return rows - 1 - } else if section == moreFromDDGSectionIndex && !shouldShowNetPCell { + if section == moreFromDDGSectionIndex && !shouldShowNetPCell { return rows - 1 + } else if section == appearanceSectionIndex && UIDevice.current.userInterfaceIdiom == .pad { + // Both the text size and bottom bar settings are at the end of the section so need to reduce the section size appropriately + return rows - 2 } else { return rows } @@ -557,7 +603,11 @@ class SettingsViewController: UITableViewController { AppDependencyProvider.shared.voiceSearchHelper.enableVoiceSearch(enableVoiceSearch) } } - + + @IBAction func onAboutTapped() { + navigationController?.pushViewController(AboutViewController(), animated: true) + } + private func showNoMicrophonePermissionAlert() { let alertController = NoMicPermissionAlert.buildAlert() present(alertController, animated: true, completion: nil) diff --git a/DuckDuckGo/SuggestionTableViewCell.swift b/DuckDuckGo/SuggestionTableViewCell.swift index 0906326091..90a22c0106 100644 --- a/DuckDuckGo/SuggestionTableViewCell.swift +++ b/DuckDuckGo/SuggestionTableViewCell.swift @@ -32,7 +32,7 @@ class SuggestionTableViewCell: UITableViewCell { @IBOutlet weak var typeImage: UIImageView! @IBOutlet weak var plusButton: UIButton! - func updateFor(query: String, suggestion: Suggestion, with theme: Theme) { + func updateFor(query: String, suggestion: Suggestion, with theme: Theme, isAddressBarAtBottom: Bool) { switch suggestion.source { case .local: @@ -47,8 +47,14 @@ class SuggestionTableViewCell: UITableViewCell { self.accessibilityValue = UserText.voiceoverSuggestionTypeSearch } } + self.plusButton.accessibilityLabel = UserText.voiceoverActionAutocomplete - + if isAddressBarAtBottom { + self.plusButton.setImage(UIImage(named: "Arrow-Down-Left-24"), for: .normal) + } else { + self.plusButton.setImage(UIImage(named: "Arrow-Top-Left-24"), for: .normal) + } + styleText(query: query, text: suggestion.suggestion, regularColor: theme.tableCellTextColor, diff --git a/DuckDuckGo/SyncAssets.xcassets/Device-Mobile-Upload-96.imageset/Contents.json b/DuckDuckGo/SyncAssets.xcassets/Device-Mobile-Upload-96.imageset/Contents.json new file mode 100644 index 0000000000..44ce0b2a59 --- /dev/null +++ b/DuckDuckGo/SyncAssets.xcassets/Device-Mobile-Upload-96.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Device-Mobile-Upload-96.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/DuckDuckGo/SyncAssets.xcassets/Device-Mobile-Upload-96.imageset/Device-Mobile-Upload-96.svg b/DuckDuckGo/SyncAssets.xcassets/Device-Mobile-Upload-96.imageset/Device-Mobile-Upload-96.svg new file mode 100644 index 0000000000..206edb4762 --- /dev/null +++ b/DuckDuckGo/SyncAssets.xcassets/Device-Mobile-Upload-96.imageset/Device-Mobile-Upload-96.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/DuckDuckGo/SyncAssets.xcassets/Sync-Pair-96.imageset/Contents.json b/DuckDuckGo/SyncAssets.xcassets/Sync-Pair-96.imageset/Contents.json new file mode 100644 index 0000000000..3aa6516022 --- /dev/null +++ b/DuckDuckGo/SyncAssets.xcassets/Sync-Pair-96.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Sync-Pair-96.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/DuckDuckGo/SyncAssets.xcassets/Sync-Pair-96.imageset/Sync-Pair-96.svg b/DuckDuckGo/SyncAssets.xcassets/Sync-Pair-96.imageset/Sync-Pair-96.svg new file mode 100644 index 0000000000..ca10b82be7 --- /dev/null +++ b/DuckDuckGo/SyncAssets.xcassets/Sync-Pair-96.imageset/Sync-Pair-96.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DuckDuckGo/SyncAssets.xcassets/Sync-Start-128.imageset/Contents.json b/DuckDuckGo/SyncAssets.xcassets/Sync-Start-128.imageset/Contents.json new file mode 100644 index 0000000000..38c819bd10 --- /dev/null +++ b/DuckDuckGo/SyncAssets.xcassets/Sync-Start-128.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Sync-Start-128.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/DuckDuckGo/SyncAssets.xcassets/Sync-Start-128.imageset/Sync-Start-128.svg b/DuckDuckGo/SyncAssets.xcassets/Sync-Start-128.imageset/Sync-Start-128.svg new file mode 100644 index 0000000000..8f38d1c2e7 --- /dev/null +++ b/DuckDuckGo/SyncAssets.xcassets/Sync-Start-128.imageset/Sync-Start-128.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/DuckDuckGo/SyncAssets.xcassets/SyncAllDevices.imageset/Contents.json b/DuckDuckGo/SyncAssets.xcassets/SyncAllDevices.imageset/Contents.json new file mode 100644 index 0000000000..9411745913 --- /dev/null +++ b/DuckDuckGo/SyncAssets.xcassets/SyncAllDevices.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "SyncAllDevices.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/DuckDuckGo/SyncAssets.xcassets/SyncAllDevices.imageset/SyncAllDevices.svg b/DuckDuckGo/SyncAssets.xcassets/SyncAllDevices.imageset/SyncAllDevices.svg new file mode 100644 index 0000000000..b0680e4703 --- /dev/null +++ b/DuckDuckGo/SyncAssets.xcassets/SyncAllDevices.imageset/SyncAllDevices.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/DuckDuckGo/SyncAssets.xcassets/SyncFavicons.imageset/Contents.json b/DuckDuckGo/SyncAssets.xcassets/SyncFavicons.imageset/Contents.json new file mode 100644 index 0000000000..5428663cb2 --- /dev/null +++ b/DuckDuckGo/SyncAssets.xcassets/SyncFavicons.imageset/Contents.json @@ -0,0 +1,16 @@ +{ + "images" : [ + { + "filename" : "SyncFavicons.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true, + "template-rendering-intent" : "template" + } +} diff --git a/DuckDuckGo/SyncAssets.xcassets/SyncFavicons.imageset/SyncFavicons.svg b/DuckDuckGo/SyncAssets.xcassets/SyncFavicons.imageset/SyncFavicons.svg new file mode 100644 index 0000000000..e78a97101f --- /dev/null +++ b/DuckDuckGo/SyncAssets.xcassets/SyncFavicons.imageset/SyncFavicons.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/DuckDuckGo/SyncDebugViewController.swift b/DuckDuckGo/SyncDebugViewController.swift index cac0672300..b5fe9f351a 100644 --- a/DuckDuckGo/SyncDebugViewController.swift +++ b/DuckDuckGo/SyncDebugViewController.swift @@ -44,6 +44,8 @@ class SyncDebugViewController: UITableViewController { enum InfoRows: Int, CaseIterable { case syncNow + case logOut + case toggleFavoritesDisplayMode } @@ -105,6 +107,10 @@ class SyncDebugViewController: UITableViewController { switch InfoRows(rawValue: indexPath.row) { case .syncNow: cell.textLabel?.text = "Sync now" + case .logOut: + cell.textLabel?.text = "Log out of sync in 10 seconds" + case .toggleFavoritesDisplayMode: + cell.textLabel?.text = "Toggle favorites display mode in 10 seconds" case .none: break } @@ -161,6 +167,23 @@ class SyncDebugViewController: UITableViewController { switch InfoRows(rawValue: indexPath.row) { case .syncNow: sync.scheduler.requestSyncImmediately() + case .logOut: + Task { + try await Task.sleep(nanoseconds: UInt64(10e9)) + try await sync.disconnect() + } + case .toggleFavoritesDisplayMode: + Task { @MainActor in + try await Task.sleep(nanoseconds: UInt64(10e9)) + var displayMode = AppDependencyProvider.shared.appSettings.favoritesDisplayMode + if displayMode.isDisplayUnified { + displayMode = .displayNative(.mobile) + } else { + displayMode = .displayUnified(native: .mobile) + } + AppDependencyProvider.shared.appSettings.favoritesDisplayMode = displayMode + NotificationCenter.default.post(name: AppUserDefaults.Notifications.favoritesDisplayModeChange, object: nil) + } default: break } case .environment: diff --git a/DuckDuckGo/SyncSettingsViewController+SyncDelegate.swift b/DuckDuckGo/SyncSettingsViewController+SyncDelegate.swift index da16752a59..082b312ace 100644 --- a/DuckDuckGo/SyncSettingsViewController+SyncDelegate.swift +++ b/DuckDuckGo/SyncSettingsViewController+SyncDelegate.swift @@ -25,6 +25,18 @@ import AVFoundation extension SyncSettingsViewController: SyncManagementViewModelDelegate { + func launchAutofillViewController() { + guard let mainVC = view.window?.rootViewController as? MainViewController else { return } + self.dismiss(animated: true) + mainVC.launchAutofillLogins() + } + + func launchBookmarksViewController() { + guard let mainVC = view.window?.rootViewController as? MainViewController else { return } + self.dismiss(animated: true) + mainVC.segueToBookmarks() + } + func updateDeviceName(_ name: String) { Task { @MainActor in rootView.model.devices = [] @@ -37,13 +49,13 @@ extension SyncSettingsViewController: SyncManagementViewModelDelegate { } } - func createAccountAndStartSyncing() { + func createAccountAndStartSyncing(optionsViewModel: SyncSettingsViewModel) { Task { @MainActor in do { try await syncService.createAccount(deviceName: deviceName, deviceType: deviceType) self.rootView.model.syncEnabled(recoveryCode: recoveryCode) self.refreshDevices() - self.showRecoveryPDF() + self.showDeviceConnected([], optionsModel: optionsViewModel, isSingleSetUp: true, shouldShowOptions: false) } catch { handleError(error) } @@ -56,33 +68,28 @@ extension SyncSettingsViewController: SyncManagementViewModelDelegate { assertionFailure(error.localizedDescription) } - func showSyncSetup() { - let model = TurnOnSyncViewModel { [weak self] in - self?.dismissPresentedViewController() - // Handle the finished logic in the closing of the view controller so that we also handle the - // user dismissing it (cancel, swipe down, etc) - } - - let controller = DismissibleHostingController(rootView: TurnOnSyncView(model: model)) { [weak self] in - self?.rootView.model.setupFinished(model) - } - - navigationController?.present(controller, animated: true) - } - func showSyncWithAnotherDevice() { collectCode(showConnectMode: syncService.account == nil) } + func showSyncWithAnotherDeviceEnterText() { + collectCode(showConnectMode: syncService.account == nil, showEnterTextCode: true) + } + func showRecoverData() { - collectCode(showConnectMode: true) + collectCode(showConnectMode: false) } - func showDeviceConnected(_ devices: [SyncSettingsViewModel.Device]) { + func showDeviceConnected(_ devices: [SyncSettingsViewModel.Device], optionsModel: SyncSettingsViewModel, isSingleSetUp: Bool, shouldShowOptions: Bool) { let model = SaveRecoveryKeyViewModel(key: recoveryCode) { [weak self] in self?.shareRecoveryPDF() } - let controller = UIHostingController(rootView: DeviceConnectedView(model, devices: devices)) + let controller = UIHostingController( + rootView: DeviceConnectedView(model, + optionsViewModel: optionsModel, + devices: devices, + isSingleSetUp: isSingleSetUp, + shouldShowOptions: shouldShowOptions)) navigationController?.present(controller, animated: true) { [weak self] in self?.rootView.model.syncEnabled(recoveryCode: self!.recoveryCode) self?.refreshDevices() @@ -97,17 +104,23 @@ extension SyncSettingsViewController: SyncManagementViewModelDelegate { navigationController?.present(controller, animated: true) } - private func collectCode(showConnectMode: Bool) { + private func collectCode(showConnectMode: Bool, showEnterTextCode: Bool = false) { let model = ScanOrPasteCodeViewModel(showConnectMode: showConnectMode) model.delegate = self - let controller = UIHostingController(rootView: ScanOrPasteCodeView(model: model)) + var controller: UIHostingController + if showEnterTextCode { + controller = UIHostingController(rootView: AnyView(PasteCodeView(model: model, isfirstScreen: true))) + } else { + controller = UIHostingController(rootView: AnyView(ScanOrPasteCodeView(model: model))) + } let navController = UIDevice.current.userInterfaceIdiom == .phone ? PortraitNavigationController(rootViewController: controller) : UINavigationController(rootViewController: controller) navController.overrideUserInterfaceStyle = .dark + navController.setNeedsStatusBarAppearanceUpdate() navController.modalPresentationStyle = .fullScreen navigationController?.present(navController, animated: true) { self.checkCameraPermission(model: model) @@ -144,6 +157,8 @@ extension SyncSettingsViewController: SyncManagementViewModelDelegate { do { self.rootView.model.isSyncEnabled = false try await self.syncService.disconnect() + AppUserDefaults().isSyncBookmarksPaused = false + AppUserDefaults().isSyncCredentialsPaused = false } catch { self.handleError(error) } @@ -167,6 +182,8 @@ extension SyncSettingsViewController: SyncManagementViewModelDelegate { do { self.rootView.model.isSyncEnabled = false try await self.syncService.deleteAccount() + AppUserDefaults().isSyncBookmarksPaused = false + AppUserDefaults().isSyncCredentialsPaused = false } catch { self.handleError(error) } diff --git a/DuckDuckGo/SyncSettingsViewController.swift b/DuckDuckGo/SyncSettingsViewController.swift index 1ba341015b..6bba63102b 100644 --- a/DuckDuckGo/SyncSettingsViewController.swift +++ b/DuckDuckGo/SyncSettingsViewController.swift @@ -50,9 +50,13 @@ class SyncSettingsViewController: UIHostingController { var cancellables = Set() // For some reason, on iOS 14, the viewDidLoad wasn't getting called so do some setup here - convenience init() { - self.init(rootView: SyncSettingsView(model: SyncSettingsViewModel())) + convenience init(appSettings: AppSettings = AppDependencyProvider.shared.appSettings) { + let viewModel = SyncSettingsViewModel() + self.init(rootView: SyncSettingsView(model: viewModel)) + + setUpFavoritesDisplayModeSwitch(viewModel, appSettings) + setUpSyncPaused(viewModel, appSettings) refreshForState(syncService.authState) syncService.authStatePublisher @@ -67,6 +71,43 @@ class SyncSettingsViewController: UIHostingController { navigationItem.title = UserText.syncTitle } + private func setUpFavoritesDisplayModeSwitch(_ viewModel: SyncSettingsViewModel, _ appSettings: AppSettings) { + viewModel.isUnifiedFavoritesEnabled = appSettings.favoritesDisplayMode.isDisplayUnified + + viewModel.$isUnifiedFavoritesEnabled.dropFirst() + .sink { [weak self] isEnabled in + appSettings.favoritesDisplayMode = isEnabled ? .displayUnified(native: .mobile) : .displayNative(.mobile) + NotificationCenter.default.post(name: AppUserDefaults.Notifications.favoritesDisplayModeChange, object: self) + self?.syncService.scheduler.notifyDataChanged() + } + .store(in: &cancellables) + + NotificationCenter.default.publisher(for: AppUserDefaults.Notifications.favoritesDisplayModeChange) + .filter { [weak self] notification in + guard let viewController = notification.object as? SyncSettingsViewController else { + return true + } + return viewController !== self + } + .receive(on: DispatchQueue.main) + .sink { _ in + viewModel.isUnifiedFavoritesEnabled = appSettings.favoritesDisplayMode.isDisplayUnified + } + .store(in: &cancellables) + } + + private func setUpSyncPaused(_ viewModel: SyncSettingsViewModel, _ appSettings: AppSettings) { + viewModel.isSyncBookmarksPaused = appSettings.isSyncBookmarksPaused + viewModel.isSyncCredentialsPaused = appSettings.isSyncCredentialsPaused + NotificationCenter.default.publisher(for: AppUserDefaults.Notifications.syncPausedStateChanged) + .receive(on: DispatchQueue.main) + .sink { _ in + viewModel.isSyncBookmarksPaused = appSettings.isSyncBookmarksPaused + viewModel.isSyncCredentialsPaused = appSettings.isSyncCredentialsPaused + } + .store(in: &cancellables) + } + override func viewDidLoad() { super.viewDidLoad() applyTheme(ThemeManager.shared.currentTheme) @@ -75,6 +116,11 @@ class SyncSettingsViewController: UIHostingController { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) connector = nil + syncService.scheduler.requestSyncImmediately() + } + + func updateOptions() { + syncService.scheduler.requestSyncImmediately() } func refreshForState(_ authState: SyncAuthState) { @@ -139,20 +185,21 @@ extension SyncSettingsViewController: ScanOrPasteCodeViewModelDelegate { } } - func loginAndShowDeviceConnected(recoveryKey: SyncCode.RecoveryKey) async throws { + func loginAndShowDeviceConnected(recoveryKey: SyncCode.RecoveryKey, isActiveSyncDevice: Bool) async throws { let knownDevices = Set(self.rootView.model.devices.map { $0.id }) let registeredDevices = try await syncService.login(recoveryKey, deviceName: deviceName, deviceType: deviceType) mapDevices(registeredDevices) dismissPresentedViewController() let devices = self.rootView.model.devices.filter { !knownDevices.contains($0.id) && !$0.isThisDevice } - showDeviceConnected(devices) + let isSecondDevice = devices.count == 1 + showDeviceConnected(devices, optionsModel: self.rootView.model, isSingleSetUp: false, shouldShowOptions: isActiveSyncDevice && isSecondDevice) } func startPolling() { Task { @MainActor in do { if let recoveryKey = try await connector?.pollForRecoveryKey() { - try await loginAndShowDeviceConnected(recoveryKey: recoveryKey) + try await loginAndShowDeviceConnected(recoveryKey: recoveryKey, isActiveSyncDevice: false) } else { // Likely cancelled elsewhere return @@ -170,22 +217,31 @@ extension SyncSettingsViewController: ScanOrPasteCodeViewModelDelegate { } if let recoveryKey = syncCode.recovery { - try await loginAndShowDeviceConnected(recoveryKey: recoveryKey) + try await loginAndShowDeviceConnected(recoveryKey: recoveryKey, isActiveSyncDevice: true) return true } else if let connectKey = syncCode.connect { if syncService.account == nil { try await syncService.createAccount(deviceName: deviceName, deviceType: deviceType) rootView.model.syncEnabled(recoveryCode: recoveryCode) } + try await syncService.transmitRecoveryKey(connectKey) + self.dismissPresentedViewController() + self.rootView.model.isSyncingDevices = true + self.rootView.model.$devices .removeDuplicates() .dropFirst() .prefix(1) .sink { [weak self] devices in - self?.dismissPresentedViewController() - self?.showDeviceConnected(devices.filter { !$0.isThisDevice }) + guard let self else { return } + self.showDeviceConnected( + devices.filter { !$0.isThisDevice }, + optionsModel: self.rootView.model, + isSingleSetUp: false, + shouldShowOptions: devices.count == 2) + self.rootView.model.isSyncingDevices = false }.store(in: &cancellables) - try await syncService.transmitRecoveryKey(connectKey) + return true } @@ -196,9 +252,8 @@ extension SyncSettingsViewController: ScanOrPasteCodeViewModelDelegate { } func codeCollectionCancelled() { - assert(navigationController?.visibleViewController is UIHostingController) + assert(navigationController?.visibleViewController is UIHostingController) dismissPresentedViewController() - rootView.model.codeCollectionCancelled() } func gotoSettings() { diff --git a/DuckDuckGo/TabSwitcherViewController.swift b/DuckDuckGo/TabSwitcherViewController.swift index 29833eb944..59d82f551c 100644 --- a/DuckDuckGo/TabSwitcherViewController.swift +++ b/DuckDuckGo/TabSwitcherViewController.swift @@ -26,6 +26,7 @@ import Bookmarks import Persistence // swiftlint:disable file_length +// swiftlint:disable:next type_body_length class TabSwitcherViewController: UIViewController { struct Constants { @@ -247,6 +248,7 @@ class TabSwitcherViewController: UIViewController { alert.addAction(UIAlertAction(title: UserText.actionCancel, style: .cancel)) alert.addAction(title: UserText.actionBookmark, style: .default) { let model = MenuBookmarksViewModel(bookmarksDatabase: self.bookmarksDatabase, syncService: self.syncService) + model.favoritesDisplayMode = AppDependencyProvider.shared.appSettings.favoritesDisplayMode let result = self.bookmarkAll(viewModel: model) self.displayBookmarkAllStatusMessage(with: result, openTabsCount: self.tabsModel.tabs.count) } diff --git a/DuckDuckGo/TabViewController.swift b/DuckDuckGo/TabViewController.swift index d6b4c667bd..e54ad09196 100644 --- a/DuckDuckGo/TabViewController.swift +++ b/DuckDuckGo/TabViewController.swift @@ -85,11 +85,12 @@ class TabViewController: UIViewController { private weak var privacyDashboard: PrivacyDashboardViewController? private var storageCache: StorageCache = AppDependencyProvider.shared.storageCache - private lazy var appSettings = AppDependencyProvider.shared.appSettings + let appSettings: AppSettings lazy var featureFlagger = AppDependencyProvider.shared.featureFlagger private lazy var internalUserDecider = AppDependencyProvider.shared.internalUserDecider + private lazy var autofillNeverPromptWebsitesManager = AppDependencyProvider.shared.autofillNeverPromptWebsitesManager private lazy var autofillWebsiteAccountMatcher = AutofillWebsiteAccountMatcher(autofillUrlMatcher: AutofillDomainNameUrlMatcher(), tld: TabViewController.tld) private(set) var tabModel: Tab @@ -122,6 +123,8 @@ class TabViewController: UIViewController { private var saveLoginPromptLastDismissed: Date? private var saveLoginPromptIsPresenting: Bool = false + private var cachedRuntimeConfigurationForDomain: [String: String?] = [:] + // If no trackers dax dialog was shown recently in this tab, ie without the user navigating somewhere else, e.g. backgrounding or tab switcher private var woShownRecently = false @@ -254,11 +257,15 @@ class TabViewController: UIViewController { private let rulesCompilationMonitor = RulesCompilationMonitor.shared - static func loadFromStoryboard(model: Tab, bookmarksDatabase: CoreDataDatabase, syncService: DDGSyncing) -> TabViewController { + static func loadFromStoryboard(model: Tab, + appSettings: AppSettings = AppDependencyProvider.shared.appSettings, + bookmarksDatabase: CoreDataDatabase, + syncService: DDGSyncing) -> TabViewController { let storyboard = UIStoryboard(name: "Tab", bundle: nil) let controller = storyboard.instantiateViewController(identifier: "TabViewController", creator: { coder in TabViewController(coder: coder, tabModel: model, + appSettings: appSettings, bookmarksDatabase: bookmarksDatabase, syncService: syncService) }) @@ -271,9 +278,11 @@ class TabViewController: UIViewController { required init?(coder aDecoder: NSCoder, tabModel: Tab, + appSettings: AppSettings, bookmarksDatabase: CoreDataDatabase, syncService: DDGSyncing) { self.tabModel = tabModel + self.appSettings = appSettings self.bookmarksDatabase = bookmarksDatabase self.syncService = syncService super.init(coder: aDecoder) @@ -329,6 +338,8 @@ class TabViewController: UIViewController { override func buildActivities() -> [UIActivity] { let viewModel = MenuBookmarksViewModel(bookmarksDatabase: bookmarksDatabase, syncService: syncService) + viewModel.favoritesDisplayMode = appSettings.favoritesDisplayMode + var activities: [UIActivity] = [SaveBookmarkActivity(controller: self, viewModel: viewModel)] @@ -339,7 +350,7 @@ class TabViewController: UIViewController { return activities } - + func initAttributionLogic() { adClickAttributionLogic.delegate = self adClickAttributionDetection.delegate = adClickAttributionLogic @@ -608,6 +619,7 @@ class TabViewController: UIViewController { public func reload() { updateContentMode() + cachedRuntimeConfigurationForDomain = [:] webView.reload() privacyDashboard?.dismiss(animated: true) } @@ -807,7 +819,9 @@ class TabViewController: UIViewController { delegate?.tabLoadingStateDidChange(tab: self) UIApplication.shared.open(url, options: [:]) { opened in if !opened { - ActionMessageView.present(message: UserText.failedToOpenExternally) + let addressBarBottom = self.appSettings.currentAddressBarPosition.isBottom + ActionMessageView.present(message: UserText.failedToOpenExternally, + presentationLocation: .withBottomBar(andAddressBarBottom: addressBarBottom)) } // just showing a blank tab at this point, so close it @@ -858,7 +872,10 @@ class TabViewController: UIViewController { public func getCurrentWebsiteInfo() -> BrokenSiteInfo { let blockedTrackerDomains = privacyInfo?.trackerInfo.trackersBlocked.compactMap { $0.domain } ?? [] - + + let configuration = ContentBlocking.shared.privacyConfigurationManager.privacyConfig + let protectionsState = configuration.isFeature(.contentBlocking, enabledForDomain: url?.host) + return BrokenSiteInfo(url: url, httpsUpgrade: httpsForced, blockedTrackerDomains: blockedTrackerDomains, @@ -866,7 +883,8 @@ class TabViewController: UIViewController { isDesktop: tabModel.isDesktop, tdsETag: ContentBlocking.shared.contentBlockingManager.currentMainRules?.etag ?? "", ampUrl: linkProtection.lastAMPURLString, - urlParametersRemoved: linkProtection.urlParametersRemoved) + urlParametersRemoved: linkProtection.urlParametersRemoved, + protectionsState: protectionsState) } public func print() { @@ -1086,7 +1104,8 @@ extension TabViewController: WKNavigationDelegate { func preparePreview(completion: @escaping (UIImage?) -> Void) { DispatchQueue.main.async { [weak self] in - guard let webView = self?.webView else { completion(nil); return } + guard let webView = self?.webView, + webView.bounds.height > 0 && webView.bounds.width > 0 else { completion(nil); return } UIGraphicsBeginImageContextWithOptions(webView.bounds.size, false, UIScreen.main.scale) webView.drawHierarchy(in: webView.bounds, afterScreenUpdates: true) if let jsAlertController = self?.jsAlertController { @@ -1826,7 +1845,9 @@ extension TabViewController { let attributedMessage = DownloadActionMessageViewHelper.makeDownloadStartedMessage(for: download) DispatchQueue.main.async { - ActionMessageView.present(message: attributedMessage, numberOfLines: 2, actionTitle: UserText.actionGenericShow, onAction: { + ActionMessageView.present(message: attributedMessage, numberOfLines: 2, actionTitle: UserText.actionGenericShow, + presentationLocation: .withBottomBar(andAddressBarBottom: self.appSettings.currentAddressBarPosition.isBottom), + onAction: { Pixel.fire(pixel: .downloadsListOpened, withAdditionalParameters: [PixelParameters.originatedFromMenu: "0"]) self.delegate?.tabDidRequestDownloads(tab: self) @@ -1840,7 +1861,9 @@ extension TabViewController { let downloadWasCancelled = nserror.domain == "NSURLErrorDomain" && nserror.code == -999 if !downloadWasCancelled { - ActionMessageView.present(message: UserText.messageDownloadFailed) + let addressBarBottom = self.appSettings.currentAddressBarPosition.isBottom + ActionMessageView.present(message: UserText.messageDownloadFailed, + presentationLocation: .withBottomBar(andAddressBarBottom: addressBarBottom)) } return @@ -1851,7 +1874,10 @@ extension TabViewController { DispatchQueue.main.async { if !download.temporary { let attributedMessage = DownloadActionMessageViewHelper.makeDownloadFinishedMessage(for: download) - ActionMessageView.present(message: attributedMessage, numberOfLines: 2, actionTitle: UserText.actionGenericShow, onAction: { + let addressBarBottom = self.appSettings.currentAddressBarPosition.isBottom + ActionMessageView.present(message: attributedMessage, numberOfLines: 2, actionTitle: UserText.actionGenericShow, + presentationLocation: .withBottomBar(andAddressBarBottom: addressBarBottom), + onAction: { Pixel.fire(pixel: .downloadsListOpened, withAdditionalParameters: [PixelParameters.originatedFromMenu: "0"]) self.delegate?.tabDidRequestDownloads(tab: self) @@ -2279,7 +2305,7 @@ extension TabViewController: SecureVaultManagerDelegate { func secureVaultInitFailed(_ error: SecureStorageError) { SecureVaultErrorReporter.shared.secureVaultInitFailed(error) } - + func secureVaultManagerIsEnabledStatus(_ manager: SecureVaultManager, forType type: AutofillType?) -> Bool { let isEnabled = AutofillSettingStatus.isAutofillEnabledInSettings && featureFlagger.isFeatureOn(.autofillCredentialInjecting) && @@ -2292,8 +2318,8 @@ extension TabViewController: SecureVaultManagerDelegate { return isEnabled } - func secureVaultManagerShouldSaveData(_: SecureVaultManager) -> Bool { - true + func secureVaultManagerShouldSaveData(_ manager: SecureVaultManager) -> Bool { + return secureVaultManagerIsEnabledStatus(manager, forType: nil) } func secureVaultManager(_ vault: SecureVaultManager, @@ -2336,7 +2362,7 @@ extension TabViewController: SecureVaultManagerDelegate { } // if user is interacting with the searchBar, don't show the autofill prompt since it will overlay the keyboard - if let parent = parent as? MainViewController, parent.omniBar.textField.isFirstResponder { + if let parent = parent as? MainViewController, parent.viewCoordinator.omniBar.textField.isFirstResponder { completionHandler(nil) return } @@ -2432,6 +2458,38 @@ extension TabViewController: SecureVaultManagerDelegate { func secureVaultManager(_: BrowserServicesKit.SecureVaultManager, didRequestPasswordManagerForDomain domain: String) { } + func secureVaultManager(_: SecureVaultManager, didRequestRuntimeConfigurationForDomain domain: String, completionHandler: @escaping (String?) -> Void) { + // didRequestRuntimeConfigurationForDomain fires for every iframe loaded on a website + // so caching the runtime configuration for the domain to prevent unnecessary re-building of the configuration + if let runtimeConfigurationForDomain = cachedRuntimeConfigurationForDomain[domain] as? String { + completionHandler(runtimeConfigurationForDomain) + return + } + + let runtimeConfiguration = + DefaultAutofillSourceProvider.Builder(privacyConfigurationManager: ContentBlocking.shared.privacyConfigurationManager, + properties: buildContentScopePropertiesForDomain(domain)) + .build() + .buildRuntimeConfigResponse() + + cachedRuntimeConfigurationForDomain = [domain: runtimeConfiguration] + completionHandler(runtimeConfiguration) + } + + private func buildContentScopePropertiesForDomain(_ domain: String) -> ContentScopeProperties { + var supportedFeatures = ContentScopeFeatureToggles.supportedFeaturesOniOS + + if AutofillSettingStatus.isAutofillEnabledInSettings, + featureFlagger.isFeatureOn(.autofillCredentialsSaving), + autofillNeverPromptWebsitesManager.hasNeverPromptWebsitesFor(domain: domain) { + supportedFeatures.passwordGeneration = false + } + + return ContentScopeProperties(gpcEnabled: appSettings.sendDoNotSell, + sessionKey: autofillUserScript?.sessionKey ?? "", + featureToggles: supportedFeatures) + } + func secureVaultManager(_: SecureVaultManager, didReceivePixel pixel: AutofillUserScript.JSPixel) { guard !pixel.isEmailPixel else { // The iOS app uses a native email autofill UI, and sends its pixels separately. Ignore pixels sent from the JS layer. @@ -2465,9 +2523,12 @@ extension TabViewController: SaveLoginViewControllerDelegate { if let newCredential = try vault.websiteCredentialsFor(accountId: credentialID) { DispatchQueue.main.async { + let addressBarBottom = self.appSettings.currentAddressBarPosition.isBottom ActionMessageView.present(message: message, - actionTitle: UserText.autofillLoginSaveToastActionButton, onAction: { - + actionTitle: UserText.autofillLoginSaveToastActionButton, + presentationLocation: .withBottomBar(andAddressBarBottom: addressBarBottom), + onAction: { + self.showLoginDetails(with: newCredential.account) }) Favicons.shared.loadFavicon(forDomain: newCredential.account.domain, intoCache: .fireproof, fromCache: .tabs) @@ -2493,6 +2554,18 @@ extension TabViewController: SaveLoginViewControllerDelegate { saveLoginPromptLastDismissed = Date() saveLoginPromptIsPresenting = false } + + func saveLoginViewController(_ viewController: SaveLoginViewController, didRequestNeverPromptForWebsite domain: String) { + viewController.dismiss(animated: true) + saveLoginPromptLastDismissed = Date() + saveLoginPromptIsPresenting = false + + do { + _ = try autofillNeverPromptWebsitesManager.saveNeverPromptWebsite(domain) + } catch { + os_log("%: failed to save never prompt for website %s", type: .error, #function, error.localizedDescription) + } + } func saveLoginViewController(_ viewController: SaveLoginViewController, didRequestPresentConfirmKeepUsingAlertController alertController: UIAlertController) { diff --git a/DuckDuckGo/TabViewControllerBrowsingMenuExtension.swift b/DuckDuckGo/TabViewControllerBrowsingMenuExtension.swift index 3018d66275..8c9e3a274b 100644 --- a/DuckDuckGo/TabViewControllerBrowsingMenuExtension.swift +++ b/DuckDuckGo/TabViewControllerBrowsingMenuExtension.swift @@ -54,7 +54,9 @@ extension TabViewController { } Pixel.fire(pixel: .browsingMenuCopy) - ActionMessageView.present(message: UserText.actionCopyMessage) + let addressBarBottom = strongSelf.appSettings.currentAddressBarPosition.isBottom + ActionMessageView.present(message: UserText.actionCopyMessage, + presentationLocation: .withBottomBar(andAddressBarBottom: addressBarBottom)) })) entries.append(BrowsingMenuEntry.regular(name: UserText.actionPrint, image: UIImage(named: "Print-24")!, action: { [weak self] in @@ -220,7 +222,9 @@ extension TabViewController { syncService.scheduler.notifyDataChanged() ActionMessageView.present(message: UserText.webSaveBookmarkDone, - actionTitle: UserText.actionGenericEdit, onAction: { + actionTitle: UserText.actionGenericEdit, + presentationLocation: .withBottomBar(andAddressBarBottom: appSettings.currentAddressBarPosition.isBottom), + onAction: { self.performEditBookmarkAction(for: link) }) } @@ -234,7 +238,7 @@ extension TabViewController { private func buildFavoriteEntry(for link: Link, bookmark: BookmarkEntity?, with bookmarksInterface: MenuBookmarksInteracting) -> BrowsingMenuEntry { - if bookmark?.isFavorite ?? false { + if bookmark?.isFavorite(on: .mobile) ?? false { let action: () -> Void = { [weak self] in Pixel.fire(pixel: .browsingMenuRemoveFromFavorites) self?.performRemoveFavoriteAction(for: link, with: bookmarksInterface) @@ -266,7 +270,10 @@ extension TabViewController { WidgetCenter.shared.reloadAllTimelines() syncService.scheduler.notifyDataChanged() - ActionMessageView.present(message: UserText.webSaveFavoriteDone, actionTitle: UserText.actionGenericUndo, onAction: { + ActionMessageView.present(message: UserText.webSaveFavoriteDone, + actionTitle: UserText.actionGenericUndo, + presentationLocation: .withBottomBar(andAddressBarBottom: appSettings.currentAddressBarPosition.isBottom), + onAction: { self.performRemoveFavoriteAction(for: link, with: bookmarksInterface) }) } @@ -277,7 +284,10 @@ extension TabViewController { WidgetCenter.shared.reloadAllTimelines() syncService.scheduler.notifyDataChanged() - ActionMessageView.present(message: UserText.webFavoriteRemoved, actionTitle: UserText.actionGenericUndo, onAction: { + ActionMessageView.present(message: UserText.webFavoriteRemoved, + actionTitle: UserText.actionGenericUndo, + presentationLocation: .withBottomBar(andAddressBarBottom: appSettings.currentAddressBarPosition.isBottom), + onAction: { self.performAddFavoriteAction(for: link, with: bookmarksInterface) }) } @@ -399,7 +409,9 @@ extension TabViewController { ContentBlocking.shared.contentBlockingManager.scheduleCompilation() - ActionMessageView.present(message: message, actionTitle: UserText.actionGenericUndo, onAction: { [weak self] in + ActionMessageView.present(message: message, actionTitle: UserText.actionGenericUndo, + presentationLocation: .withBottomBar(andAddressBarBottom: appSettings.currentAddressBarPosition.isBottom), + onAction: { [weak self] in self?.togglePrivacyProtection(domain: domain) }) } diff --git a/DuckDuckGo/TabViewControllerLongPressBookmarkExtension.swift b/DuckDuckGo/TabViewControllerLongPressBookmarkExtension.swift index 01f61c030c..54f825f339 100644 --- a/DuckDuckGo/TabViewControllerLongPressBookmarkExtension.swift +++ b/DuckDuckGo/TabViewControllerLongPressBookmarkExtension.swift @@ -35,18 +35,24 @@ extension TabViewController { syncService.scheduler.notifyDataChanged() DispatchQueue.main.async { - ActionMessageView.present(message: UserText.webSaveFavoriteDone) + let addressBarBottom = self.appSettings.currentAddressBarPosition.isBottom + ActionMessageView.present(message: UserText.webSaveFavoriteDone, + presentationLocation: .withBottomBar(andAddressBarBottom: addressBarBottom)) } } else if nil == viewModel.bookmark(for: link.url) { viewModel.createBookmark(title: link.displayTitle, url: link.url) syncService.scheduler.notifyDataChanged() DispatchQueue.main.async { - ActionMessageView.present(message: UserText.webSaveBookmarkDone) + let addressBarBottom = self.appSettings.currentAddressBarPosition.isBottom + ActionMessageView.present(message: UserText.webSaveBookmarkDone, + presentationLocation: .withBottomBar(andAddressBarBottom: addressBarBottom)) } } else { DispatchQueue.main.async { - ActionMessageView.present(message: UserText.webBookmarkAlreadySaved) + let addressBarBottom = self.appSettings.currentAddressBarPosition.isBottom + ActionMessageView.present(message: UserText.webBookmarkAlreadySaved, + presentationLocation: .withBottomBar(andAddressBarBottom: addressBarBottom)) } } } diff --git a/DuckDuckGo/TabsModelPersistenceExtension.swift b/DuckDuckGo/TabsModelPersistenceExtension.swift index 184799ffe5..a7a046b890 100644 --- a/DuckDuckGo/TabsModelPersistenceExtension.swift +++ b/DuckDuckGo/TabsModelPersistenceExtension.swift @@ -18,6 +18,8 @@ // import Foundation +import Core +import Common extension TabsModel { @@ -26,8 +28,21 @@ extension TabsModel { } public static func get() -> TabsModel? { - guard let data = UserDefaults.app.object(forKey: Constants.key) as? Data else { return nil } - return try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? TabsModel + guard let data = UserDefaults.app.object(forKey: Constants.key) as? Data else { + return nil + } + var tabsModel: TabsModel? + do { + let unarchiver = try NSKeyedUnarchiver(forReadingFrom: data) + unarchiver.requiresSecureCoding = false + tabsModel = unarchiver.decodeObject(of: self, forKey: NSKeyedArchiveRootObjectKey) + if let error = unarchiver.error { + throw error + } + } catch { + os_log("Something went wrong unarchiving TabsModel %@", log: .generalLog, type: .error, error.localizedDescription) + } + return tabsModel } public static func clear() { @@ -35,8 +50,11 @@ extension TabsModel { } func save() { - guard let data = try? NSKeyedArchiver.archivedData(withRootObject: self, requiringSecureCoding: false) else { return } - UserDefaults.app.set(data, forKey: Constants.key) + do { + let data = try NSKeyedArchiver.archivedData(withRootObject: self, requiringSecureCoding: false) + UserDefaults.app.set(data, forKey: Constants.key) + } catch { + os_log("Something went wrong archiving TabsModel %@", log: .generalLog, type: .error, error.localizedDescription) + } } - } diff --git a/DuckDuckGo/Theme+DesignSystem.swift b/DuckDuckGo/Theme+DesignSystem.swift new file mode 100644 index 0000000000..f52d2e923e --- /dev/null +++ b/DuckDuckGo/Theme+DesignSystem.swift @@ -0,0 +1,36 @@ +// +// Theme+DesignSystem.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import DesignResourcesKit + +// Once all colours are from the design system we can consider removing having multiple themes. +extension Theme { + + var omniBarBackgroundColor: UIColor { UIColor(designSystemColor: .panel) } + var backgroundColor: UIColor { UIColor(designSystemColor: .background) } + var mainViewBackgroundColor: UIColor { UIColor(designSystemColor: .background) } + var barBackgroundColor: UIColor { UIColor(designSystemColor: .panel) } + var barTintColor: UIColor { UIColor(designSystemColor: .icons) } + var browsingMenuBackgroundColor: UIColor { UIColor(designSystemColor: .surface) } + var tableCellBackgroundColor: UIColor { UIColor(designSystemColor: .surface) } + var tabSwitcherCellBackgroundColor: UIColor { UIColor(designSystemColor: .surface) } + var searchBarTextPlaceholderColor: UIColor { UIColor(designSystemColor: .textSecondary) } + +} diff --git a/DuckDuckGo/UIApplicationExtension.swift b/DuckDuckGo/UIApplicationExtension.swift new file mode 100644 index 0000000000..c89cabd385 --- /dev/null +++ b/DuckDuckGo/UIApplicationExtension.swift @@ -0,0 +1,41 @@ +// +// UIApplicationExtension.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit + +extension UIApplication { + private static let notificationSettingsURL: URL? = { + let settingsString: String + if #available(iOS 16, *) { + settingsString = UIApplication.openNotificationSettingsURLString + } else if #available(iOS 15.4, *) { + settingsString = UIApplicationOpenNotificationSettingsURLString + } else { + settingsString = UIApplication.openSettingsURLString + } + return URL(string: settingsString) + }() + + func openAppNotificationSettings() async -> Bool { + guard + let url = UIApplication.notificationSettingsURL, + self.canOpenURL(url) else { return false } + return await self.open(url) + } +} diff --git a/DuckDuckGo/UserScripts.swift b/DuckDuckGo/UserScripts.swift index 9f8b7cafeb..06cd05c46e 100644 --- a/DuckDuckGo/UserScripts.swift +++ b/DuckDuckGo/UserScripts.swift @@ -45,6 +45,7 @@ final class UserScripts: UserScriptsProvider { contentBlockerUserScript = ContentBlockerRulesUserScript(configuration: sourceProvider.contentBlockerRulesConfig) surrogatesScript = SurrogatesUserScript(configuration: sourceProvider.surrogatesConfig) autofillUserScript = AutofillUserScript(scriptSourceProvider: sourceProvider.autofillSourceProvider) + autofillUserScript.sessionKey = sourceProvider.contentScopeProperties.sessionKey loginFormDetectionScript = sourceProvider.loginDetectionEnabled ? LoginFormDetectionUserScript() : nil contentScopeUserScript = ContentScopeUserScript(sourceProvider.privacyConfigurationManager, diff --git a/DuckDuckGo/UserText.swift b/DuckDuckGo/UserText.swift index b57f9df4c4..6b15c281f1 100644 --- a/DuckDuckGo/UserText.swift +++ b/DuckDuckGo/UserText.swift @@ -21,7 +21,10 @@ import Core public struct UserText { - + + public static let addressBarPositionTop = NSLocalizedString("address.bar.top", value: "Top", comment: "Settings label for top position for the address bar") + public static let addressBarPositionBottom = NSLocalizedString("address.bar.bottom", value: "Bottom", comment: "Settings label for bottom position for the address bar") + public static let appUnlock = NSLocalizedString("app.authentication.unlock", value: "Unlock DuckDuckGo.", comment: "Shown on authentication screen") public static let searchDuckDuckGo = NSLocalizedString("search.hint.duckduckgo", value: "Search or enter address", comment: "") public static let webSaveBookmarkDone = NSLocalizedString("web.url.save.bookmark.done", value: "Bookmark added", comment: "Confirmation message") @@ -258,9 +261,7 @@ public struct UserText { public static let homeTabSearchAndFavorites = NSLocalizedString("homeTab.searchAndFavorites", value: "Search or enter address", comment: "This describes empty tab") public static let homeTabTitle = NSLocalizedString("homeTab.title", value: "Home", comment: "Home tab title") - - public static let settingsAboutText = NSLocalizedString("settings.about.text", value: "At DuckDuckGo, we’re setting the new standard of trust online.\n\nDuckDuckGo Privacy Browser provides all the privacy essentials you need to protect yourself as you search and browse the web, including tracker blocking, smarter encryption, and DuckDuckGo private search.\n\nAfter all, the Internet shouldn’t feel so creepy, and getting the privacy you deserve online should be as simple as closing the blinds.", comment: "") - + public static let daxDialogHomeInitial = NSLocalizedString("dax.onboarding.home.initial", value: "Next, try visiting one of your favorite sites!\n\nI’ll block trackers so they can’t spy on you. I’ll also upgrade the security of your connection if possible. 🔒", comment: "") public static let daxDialogHomeSubsequent = NSLocalizedString("dax.onboarding.home.subsequent", value: "You’ve got this!\n\nRemember: Every time you browse with me, a creepy ad loses its wings. 👍", comment: "ad = advertisment") public static let daxDialogHomeAddFavorite = NSLocalizedString("dax.onboarding.home.add.favorite", value: "Visit your favorite sites in a flash!\n\nGo to a site you love. Then tap the \"⋯\" icon and select *Add to Favorites*.", comment: "Encourage user to add favorite site using the browsing menu.") @@ -400,21 +401,20 @@ public struct UserText { public static let emptyDownloads = NSLocalizedString("downloads.downloads-list.empty", value: "No files downloaded yet", comment: "Empty downloads list placholder") - public static let autofillSaveLoginTitleNewUser = NSLocalizedString("autofill.save-login.new-user.title", value: "Do you want DuckDuckGo to save your Login?", comment: "Title displayed on modal asking for the user to save the login for the first time") + public static let autofillSaveLoginTitleNewUser = NSLocalizedString("autofill.save-login.new-user.title", value: "Do you want DuckDuckGo to save your password?", comment: "Title displayed on modal asking for the user to save the login for the first time") public static let autofillSaveLoginTitle = NSLocalizedString("autofill.save-login.title", value: "Save Login?", comment: "Title displayed on modal asking for the user to save the login") public static let autofillUpdateUsernameTitle = NSLocalizedString("autofill.update-usernamr.title", value: "Update username?", comment: "Title displayed on modal asking for the user to update the username") - public static let autofillSaveLoginMessageNewUser = NSLocalizedString("autofill.save-login.new-user.message", value: "Logins are stored securely on your device in the Logins menu.", comment: "Message displayed on modal asking for the user to save the login for the first time") + public static let autofillSaveLoginMessageNewUser = NSLocalizedString("autofill.save-login.new-user.message", value: "Passwords are stored securely on your device in the Logins menu.", comment: "Message displayed on modal asking for the user to save the login for the first time") public static let autofillSaveLoginNotNowCTA = NSLocalizedString("autofill.save-login.not-now.CTA", value: "Don’t Save", comment: "Cancel CTA displayed on modal asking for the user to save the login") - - public static let autofillSavePasswordTitle = NSLocalizedString("autofill.save-password.title", value: "Save Password?", comment: "Title displayed on modal asking for the user to save the password") + public static let autofillSaveLoginNeverPromptCTA = NSLocalizedString("autofill.save-login.never-prompt.CTA", value:"Never Ask for This Site", comment: "CTA displayed on modal asking if the user never wants to be prompted to save a login for this website agin") + public static func autofillUpdatePassword(for title: String) -> String { let message = NSLocalizedString("autofill.update-password.title", value: "Update password for\n%@?", comment: "Title displayed on modal asking for the user to update the password") return message.format(arguments: title) } - public static let autoUpdatePasswordMessage = NSLocalizedString("autofill.update-password.message", value: "DuckDuckGo will update this stored Login on your device.", comment: "Message displayed on modal asking for the user to update the password") + public static let autoUpdatePasswordMessage = NSLocalizedString("autofill.update-password.message", value: "DuckDuckGo will update this stored password on your device.", comment: "Message displayed on modal asking for the user to update the password") - public static let autofillSaveLoginSaveCTA = NSLocalizedString("autofill.save-login.save.CTA", value: "Save Login", comment: "Confirm CTA displayed on modal asking for the user to save the login") public static let autofillSavePasswordSaveCTA = NSLocalizedString("autofill.save-password.save.CTA", value: "Save Password", comment: "Confirm CTA displayed on modal asking for the user to save the password") public static let autofillUpdatePasswordSaveCTA = NSLocalizedString("autofill.update-password.save.CTA", value: "Update Password", comment: "Confirm CTA displayed on modal asking for the user to update the password") public static let autofillShowPassword = NSLocalizedString("autofill.show-password", value: "Show Password", comment: "Accessibility title for a Show Password button displaying actial password instead of *****") @@ -622,7 +622,7 @@ In addition to the details entered into this form, your app issue report will co public static let netPCellConnected = NSLocalizedString("netP.cell.connected", value: "Connected", comment: "String indicating NetP is connected when viewed from the settings screen") public static let netPCellDisconnected = NSLocalizedString("netP.cell.disconnected", value: "Not connected", comment: "String indicating NetP is disconnected when viewed from the settings screen") - static let netPInviteTitle = NSLocalizedString("network.protection.invite.dialog.title", value: "You're invited to try Network Protection", comment: "Title for the network protection invite screen") + static let netPInviteTitle = NSLocalizedString("network.protection.invite.dialog.title", value: "You’re invited to try Network Protection", comment: "Title for the network protection invite screen") static let netPInviteMessage = NSLocalizedString("network.protection.invite.dialog.message", value: "Enter your invite code to get started.", comment: "Message for the network protection invite dialog") static let netPInviteFieldPrompt = NSLocalizedString("network.protection.invite.field.prompt", value: "Invite Code", comment: "Prompt for the network protection invite code text field") static let netPInviteSuccessTitle = NSLocalizedString("network.protection.invite.success.title", value: "Success! You’re in.", comment: "Title for the network protection invite success view") @@ -643,9 +643,33 @@ In addition to the details entered into this form, your app issue report will co static let netPStatusViewLocation = NSLocalizedString("network.protection.status.view.location", value: "Location", comment: "Location label shown in NetworkProtection's status view.") static let netPStatusViewIPAddress = NSLocalizedString("network.protection.status.view.ip.address", value: "IP Address", comment: "IP Address label shown in NetworkProtection's status view.") static let netPStatusViewConnectionDetails = NSLocalizedString("network.protection.status.view.connection.details", value: "Connection Details", comment: "Connection details label shown in NetworkProtection's status view.") + static let netPStatusViewSettingsSectionTitle = NSLocalizedString("network.protection.status.view.settings.section.title", value: "Manage", comment: "Label shown on the title of the settings section in NetworkProtection's status view.") + static let netPVPNSettingsTitle = NSLocalizedString("network.protection.vpn.settings.title", value: "VPN Settings", comment: "Title for the VPN Settings screen.") + static let netPVPNNotificationsTitle = NSLocalizedString("network.protection.vpn.notifications.title", value: "VPN Notifications", comment: "Title for the VPN Notifications management screen.") static let netPStatusViewShareFeedback = NSLocalizedString("network.protection.status.menu.share.feedback", value: "Share Feedback", comment: "The status view 'Share Feedback' button which is shown inline on the status view after the \(netPInviteOnlyMessage) text") static let netPStatusViewErrorConnectionFailedTitle = NSLocalizedString("network.protection.status.view.error.connection.failed.title", value: "Failed to Connect.", comment: "Generic connection failed error title shown in NetworkProtection's status view.") static let netPStatusViewErrorConnectionFailedMessage = NSLocalizedString("network.protection.status.view.error.connection.failed.message", value: "Please try again later.", comment: "Generic connection failed error message shown in NetworkProtection's status view.") + static let netPPreferredLocationSettingTitle = NSLocalizedString("network.protection.vpn.preferred.location.title", value: "Preferred Location", comment: "Title for the Preferred Location VPN Settings item.") + static let netPPreferredLocationNearest = NSLocalizedString("network.protection.vpn.preferred.location.nearest", value: "Nearest Available", comment: "Label for the Preferred Location VPN Settings item when the nearest available location is selected.") + static let netPVPNLocationTitle = NSLocalizedString("network.protection.vpn.location.title", value: "VPN Location", comment: "Title for the VPN Location screen.") + static let netPVPNLocationRecommendedSectionTitle = NSLocalizedString("network.protection.vpn.location.recommended.section.title", value: "Recommended", comment: "Title for the VPN Location screen's Recommended section.") + static let netPVPNLocationRecommendedSectionFooter = NSLocalizedString("network.protection.vpn.location.recommended.section.footer", value: "Automatically connect to the nearest server we can find", comment: "Footer describing the VPN Location screen's Recommended section which just has Nearest Available.") + static let netPVPNLocationAllCountriesSectionTitle = NSLocalizedString("network.protection.vpn.location.all.countries.section.title", value: "All Countries", comment: "Title for the VPN Location screen's All Countries section.") + static let netPVPNLocationNearestAvailableItemTitle = NSLocalizedString("network.protection.vpn.location.nearest.available.item.title", value: "Nearest Available", comment: "Title for the VPN Location screen's Nearest Available selection item.") + static func netPVPNLocationCountryItemFormattedCitiesCount(_ count: Int) -> String { + let message = NSLocalizedString("network.protection.vpn.location.country.item.formatted.cities.count", value: "%d cities", comment: "Subtitle of countries item when there are multiple cities, example : ") + return message.format(arguments: count) + } + static let netPAlwaysOnSettingTitle = NSLocalizedString("network.protection.vpn.always.on.setting.title", value: "Always On", comment: "Title for the Always on VPN setting item.") + static let netPAlwaysOnSettingFooter = NSLocalizedString("network.protection.vpn.always.on.setting.footer", value: "Automatically restore a VPN connection after interruption.", comment: "Footer text for the Always on VPN setting item.") + static let netPSecureDNSSettingTitle = NSLocalizedString("network.protection.vpn.secure.dns.setting.title", value: "Secure DNS", comment: "Title for the Always on VPN setting item.") + static let netPSecureDNSSettingFooter = NSLocalizedString("network.protection.vpn.secure.dns.setting.footer", value: "Network Protection prevents DNS leaks to your Internet Service Provider by routing DNS queries though the VPN tunnel to our own resolver.", comment: "Footer text for the Always on VPN setting item.") + static let netPTurnOnNotificationsButtonTitle = NSLocalizedString("network.protection.turn.on.notifications.button.title", value: "Turn on Notifications", comment: "Title for the button to link to the iOS app settings and enable notifications app-wide.") + static let netPTurnOnNotificationsSectionFooter = NSLocalizedString("network.protection.turn.on.notifications.section.footer", value: "Allow DuckDuckGo to notify you if your connection drops or VPN status changes.", comment: "Footer text under the button to link to the iOS app settings and enable notifications app-wide.") + static let netPVPNAlertsToggleTitle = NSLocalizedString("network.protection.vpn.alerts.toggle.title", value: "VPN Alerts", comment: "Title for the toggle for VPN alerts.") + static let netPVPNAlertsToggleSectionFooter = NSLocalizedString("network.protection.vpn.alerts.toggle.section.footer", value: "Get notified if your connection drops or VPN status changes.", comment: "List section footer for the toggle for VPN alerts.") + + static let netPOpenVPNQuickAction = NSLocalizedString("network.protection.quick-action.open-vpn", value: "Open VPN", comment: "Title text for an iOS quick action that opens VPN settings") static let inviteDialogContinueButton = NSLocalizedString("invite.dialog.continue.button", value: "Continue", comment: "Continue button on an invite dialog") static let inviteDialogGetStartedButton = NSLocalizedString("invite.dialog.get.started.button", value: "Get Started", comment: "Get Started button on an invite dialog") @@ -661,7 +685,11 @@ In addition to the details entered into this form, your app issue report will co public static let emailSettingsSubtitle = NSLocalizedString("email.settings.subtitle", value: "Block email trackers and hide your address", comment: "Subtitle for the email settings cell") public static let macWaitlistBrowsePrivately = NSLocalizedString("mac-waitlist.settings.browse-privately", value: "Browse privately with our app for Mac", comment: "Title for the settings subtitle") - + public static let favoritesDisplayPreferencesHeader = NSLocalizedString("favorites.settings.header", value: "Display Preferences", comment: "Header of the favorites settings table") + public static let favoritesDisplayPreferencesFooter = NSLocalizedString("favorites.settings.footer", value: "Choose which favorites to display on a new tab based on their origin.", comment: "Footer of the favorites settings table") + public static let favoritesDisplayPreferencesMobileOnly = NSLocalizedString("favorites.settings.mobile-only", value: "Mobile Favorites Only", comment: "Display Mode for favorites") + public static let favoritesDisplayPreferencesAllDevices = NSLocalizedString("favorites.settings.all-devices", value: "All Device Favorites", comment: "Display Mode for favorites") + // MARK: Share Sheet public static let macWaitlistShareSheetTitle = NSLocalizedString("mac-waitlist.share-sheet.title", value: "DuckDuckGo for Mac", comment: "Title for the share sheet entry") @@ -693,15 +721,30 @@ In addition to the details entered into this form, your app issue report will co let message = NSLocalizedString("autofill.logins.search.no-results.subtitle", value: "for '%@'", comment: "Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle)") return message.format(arguments: query) } - + + public static let aboutText = NSLocalizedString("settings.about.text", value: """ +DuckDuckGo is the independent Internet privacy company founded in 2008 for anyone who’s tired of being tracked online and wants an easy solution. We’re proof you can get real privacy protection online without tradeoffs. + +The DuckDuckGo browser comes with the features you expect from a go-to browser, like bookmarks, tabs, passwords, and more, plus over [a dozen powerful privacy protections](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/) not offered in most popular browsers by default. This uniquely comprehensive set of privacy protections helps protect your online activities, from searching to browsing, emailing, and more. + +Our privacy protections work without having to know anything about the technical details or deal with complicated settings. All you have to do is switch your browser to DuckDuckGo across all your devices and you get privacy by default. + +But if you *do* want a peek under the hood, you can find more information about how DuckDuckGo privacy protections work on our [help pages](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/). +""", comment: "about page") + public static let autofillEnableSettings = NSLocalizedString("autofill.logins.list.enable", value:"Save and Autofill Logins", comment: "Title for a toggle that enables autofill") + public static let autofillNeverSavedSettings = NSLocalizedString("autofill.logins.list.never.saved", value:"Reset Excluded Sites", comment: "Title for a button that allows a user to reset their list of never saved sites") public static let autofillLoginListTitle = NSLocalizedString("autofill.logins.list.title", value:"Logins", comment: "Title for screen listing autofill logins") public static let autofillLoginListSearchPlaceholder = NSLocalizedString("autofill.logins.list.search-placeholder", value:"Search Logins", comment: "Placeholder for search field on autofill login listing") public static let autofillLoginListSuggested = NSLocalizedString("autofill.logins.list.suggested", value:"Suggested", comment: "Section title for group of suggested saved logins") + public static let autofillResetNeverSavedActionTitle = NSLocalizedString("autofill.logins.list.never.saved.reset.action.title", value:"If you reset excluded sites, you will be prompted to save your Login next time you sign in to any of these sites.", comment: "Alert title") + public static let autofillResetNeverSavedActionConfirmButton = NSLocalizedString("autofill.logins.list.never.saved.reset.action.confirm", value: "Reset Excluded Sites", comment: "Confirm button to reset list of never saved sites") + public static let autofillResetNeverSavedActionCancelButton = NSLocalizedString("autofill.logins.list.never.saved.reset.action.cancel", value: "Cancel", comment: "Cancel button for resetting list of never saved sites") + public static let autofillLoginPromptAuthenticationCancelButton = NSLocalizedString("autofill.logins.prompt.auth.cancel", value:"Cancel", comment: "Cancel button for auth during login prompt") public static let autofillLoginPromptAuthenticationReason = NSLocalizedString("autofill.logins.prompt.auth.reason", value:"Unlock To Use Saved Login", comment: "Reason for auth during login prompt") - public static let autofillLoginPromptTitle = NSLocalizedString("autofill.logins.prompt.title", value:"Use a saved Login?", comment: "Title for autofill login prompt") + public static let autofillLoginPromptTitle = NSLocalizedString("autofill.logins.prompt.title", value:"Use a saved password?", comment: "Title for autofill login prompt") public static let autofillLoginPromptExactMatchTitle = NSLocalizedString("autofill.logins.prompt.exact.match.title", value:"From this website", comment: "Title for section of autofill logins that are an exact match to the current website") public static func autofillLoginPromptPartialMatchTitle(for type: String) -> String { let message = NSLocalizedString("autofill.logins.prompt.partial.match.title", value: "From %@", comment: "Title for section of autofill logins that are an approximate match to the current website") @@ -822,10 +865,73 @@ In addition to the details entered into this form, your app issue report will co } public static let syncRemoveDeviceConfirmAction = "Remove" public static let syncCodeCopied = "Recovery Code copied" - public static let syncTitle = "Sync" + public static let syncTitle = "Sync & Back Up" // MARK: Errors static let unknownErrorTryAgainMessage = NSLocalizedString("error.unknown.try.again", value: "An unknown error has occurred", comment: "Generic error message on a dialog for when the cause is not known.") + static let syncBookmarkPausedAlertTitle = NSLocalizedString("alert.sync-bookmarks-paused-title", value: "Bookmarks Sync is Paused", comment: "Title for alert shown when sync bookmarks paused for too many items") + static let syncBookmarkPausedAlertDescription = NSLocalizedString("alert.sync-bookmarks-paused-description", value: "You have exceeded the bookmarks sync limit. Try deleting some bookmarks. Until this is resolved your bookmarks will not be backed up.", comment: "Description for alert shown when sync bookmarks paused for too many items") + static let syncCredentialsPausedAlertTitle = NSLocalizedString("alert.sync-credentials-paused-title", value: "Passwords Sync is Paused", comment: "Title for alert shown when sync credentials paused for too many items") + static let syncCredentialsPausedAlertDescription = NSLocalizedString("alert.sync-credentials-paused-description", value: "You have exceeded the passwords sync limit. Try deleting some passwords. Until this is resolved your passwords will not be backed up.", comment: "Description for alert shown when sync credentials paused for too many items") + public static let syncPausedAlertOkButton = NSLocalizedString("alert.sync-paused-alert-ok-button", value: "OK", comment: "Confirmation button in alert") + public static let syncPausedAlertLearnMoreButton = NSLocalizedString("alert.sync-paused-alert-learn-more-button", value: "Learn More", comment: "Learn more button in alert") + + static let preemptiveCrashTitle = NSLocalizedString("error.preemptive-crash.title", value: "App issue detected", comment: "Alert title") + static let preemptiveCrashBody = NSLocalizedString("error.preemptive-crash.body", value: "Looks like there's an issue with the app and it needs to close. Please reopen to continue.", comment: "Alert message") + static let preemptiveCrashAction = NSLocalizedString("error.preemptive-crash.action", value: "Close App", comment: "Button title that is shutting down the app") + + static let insufficientDiskSpaceTitle = NSLocalizedString("error.insufficient-disk-space.title", value: "Not enough storage", comment: "Alert title") + static let insufficientDiskSpaceBody = NSLocalizedString("error.insufficient-disk-space.body", value: "Looks like your device has run out of storage space. Please free up space to continue.", comment: "Alert message") + static let insufficientDiskSpaceAction = NSLocalizedString("error.insufficient-disk-space.action", value: "Open Settings", comment: "Button title to open device settings") + + static let emailProtectionSignInTitle = NSLocalizedString("error.email-protection-sign-in.title", value: "Email Protection Error", comment: "Alert title") + static let emailProtectionSignInBody = NSLocalizedString("error.email-protection-sign-in.body", value: "Sorry, please sign in again to re-enable Email Protection features on this browser.", comment: "Alert message") + static let emailProtectionSignInAction = NSLocalizedString("error.email-protection-sign-in.action", value: "Sign In", comment: "Button title to Sign In") + + // MARK: - VPN Waitlist + + static let networkProtectionWaitlistJoinTitle = NSLocalizedString("network-protection.waitlist.join.title", value: "Network Protection Early Access", comment: "Title for Network Protection join waitlist screen") + static let networkProtectionWaitlistJoinSubtitle1 = NSLocalizedString("network-protection.waitlist.join.subtitle.1", value: "Secure your connection anytime, anywhere with Network Protection, the VPN from DuckDuckGo.", comment: "First subtitle for Network Protection join waitlist screen") + static let networkProtectionWaitlistJoinSubtitle2 = NSLocalizedString("network-protection.waitlist.join.subtitle.2", value: "Join the waitlist, and we’ll notify you when it’s your turn.", comment: "Second subtitle for Network Protection join waitlist screen") + + static let networkProtectionWaitlistJoinedTitle = NSLocalizedString("network-protection.waitlist.joined.title", value: "You’re on the list!", comment: "Title for Network Protection joined waitlist screen") + static let networkProtectionWaitlistJoinedWithNotificationsSubtitle1 = NSLocalizedString("network-protection.waitlist.joined.with-notifications.subtitle.1", value: "New invites are sent every few days, on a first come, first served basis.", comment: "Subtitle 1 for Network Protection joined waitlist screen when notifications are enabled") + static let networkProtectionWaitlistJoinedWithNotificationsSubtitle2 = NSLocalizedString("network-protection.waitlist.joined.with-notifications.subtitle.2", value: "We’ll notify you when your invite is ready.", comment: "Subtitle 2 for Network Protection joined waitlist screen when notifications are enabled") + + static let networkProtectionWaitlistNotificationTitle = NSLocalizedString("network-protection.waitlist.notification.title", value: "Network Protection is ready!", comment: "Title for Network Protection waitlist notification") + static let networkProtectionWaitlistNotificationText = NSLocalizedString("network-protection.waitlist.notification.text", value: "Open your invite", comment: "Title for Network Protection waitlist notification") + + static let networkProtectionWaitlistInvitedTitle = NSLocalizedString("network-protection.waitlist.invited.title", value: "You’re invited to try\nNetwork Protection early access!", comment: "Title for Network Protection invited screen") + static let networkProtectionWaitlistInvitedSubtitle = NSLocalizedString("network-protection.waitlist.invited.subtitle", value: "Get an extra layer of protection online with the VPN built for speed and simplicity. Encrypt your internet connection across your entire device and hide your location and IP address from sites you visit.", comment: "Subtitle for Network Protection invited screen") + + static let networkProtectionWaitlistInvitedSection1Title = NSLocalizedString("network-protection.waitlist.invited.section-1.title", value: "Full-device coverage", comment: "Title for section 1 of the Network Protection invited screen") + static let networkProtectionWaitlistInvitedSection1Subtitle = NSLocalizedString("network-protection.waitlist.invited.section-1.subtitle", value: "Encrypt online traffic across your browsers and apps.", comment: "Subtitle for section 1 of the Network Protection invited screen") + + static let networkProtectionWaitlistInvitedSection2Title = NSLocalizedString("network-protection.waitlist.invited.section-2.title", value: "Fast, reliable, and easy to use", comment: "Title for section 2 of the Network Protection invited screen") + static let networkProtectionWaitlistInvitedSection2Subtitle = NSLocalizedString("network-protection.waitlist.invited.section-2.subtitle", value: "No need for a separate app. Connect in one click and see your connection status at a glance.", comment: "Subtitle for section 2 of the Network Protection invited screen") + + static let networkProtectionWaitlistInvitedSection3Title = NSLocalizedString("network-protection.waitlist.invited.section-3.title", value: "Strict no-logging policy", comment: "Title for section 3 of the Network Protection invited screen") + static let networkProtectionWaitlistInvitedSection3Subtitle = NSLocalizedString("network-protection.waitlist.invited.section-3.subtitle", value: "We do not log or save any data that can connect you to your online activity.", comment: "Subtitle for section 3 of the Network Protection invited screen") + + static let networkProtectionWaitlistButtonEnableNotifications = NSLocalizedString("network-protection.waitlist.button.enable-notifications", value: "Enable Notifications", comment: "Enable Notifications button for Network Protection joined waitlist screen") + static let networkProtectionWaitlistButtonJoinWaitlist = NSLocalizedString("network-protection.waitlist.button.join-waitlist", value: "Join the Waitlist", comment: "Join Waitlist button for Network Protection join waitlist screen") + static let networkProtectionWaitlistButtonAgreeAndContinue = NSLocalizedString("network-protection.waitlist.button.agree-and-continue", value: "Agree and Continue", comment: "Agree and Continue button for Network Protection join waitlist screen") + static let networkProtectionWaitlistButtonExistingInviteCode = NSLocalizedString("network-protection.waitlist.button.existing-invite-code", value: "I Have an Invite Code", comment: "Button title for users who already have an invite code") + + static let networkProtectionWaitlistAvailabilityDisclaimer = NSLocalizedString("network-protection.waitlist.availability-disclaimer", value: "Network Protection is free to use during early access.", comment: "Availability disclaimer for Network Protection join waitlist screen") + + static let networkProtectionPrivacyPolicyTitle = NSLocalizedString("network-protection.privacy-policy.title", value: "Privacy Policy", comment: "Privacy Policy title for Network Protection") + + static let networkProtectionWaitlistNotificationAlertDescription = NSLocalizedString("network-protection.waitlist.notification-alert.description", value: "We’ll send you a notification when your invite to test Network Protection is ready.", comment: "Body text for the alert to enable notifications") + + static let networkProtectionWaitlistGetStarted = NSLocalizedString("network-protection.waitlist.get-started", value: "Get Started", comment: "Button title text for the Network Protection waitlist confirmation prompt") + static let networkProtectionWaitlistAgreeAndContinue = NSLocalizedString("network-protection.waitlist.agree-and-continue", value: "Agree and Continue", comment: "Title text for the Network Protection terms and conditions accept button") + + static let networkProtectionSettingsSubtitleNotJoined = NSLocalizedString("network-protection.waitlist.settings-subtitle.waitlist-not-joined", value: "Join the private waitlist", comment: "Subtitle text for the Network Protection settings row") + static let networkProtectionSettingsSubtitleJoinedButNotInvited = NSLocalizedString("network-protection.waitlist.settings-subtitle.joined-but-not-invited", value: "You’re on the list!", comment: "Subtitle text for the Network Protection settings row") + static let networkProtectionSettingsSubtitleJoinedAndInvited = NSLocalizedString("network-protection.waitlist.settings-subtitle.joined-and-invited", value: "Your invite is ready!", comment: "Subtitle text for the Network Protection settings row") + static let networkProtectionNotificationPromptTitle = NSLocalizedString("network-protection.waitlist.notification-prompt-title", value: "Know the instant you're invited", comment: "Title for the alert to confirm enabling notifications") + static let networkProtectionNotificationPromptDescription = NSLocalizedString("network-protection.waitlist.notification-prompt-description", value: "Get a notification when your copy of Network Protection early access is ready.", comment: "Subtitle for the alert to confirm enabling notifications") } diff --git a/DuckDuckGo/VPNIntents.swift b/DuckDuckGo/VPNIntents.swift new file mode 100644 index 0000000000..716396607f --- /dev/null +++ b/DuckDuckGo/VPNIntents.swift @@ -0,0 +1,102 @@ +// +// VPNIntents.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import AppIntents +import NetworkExtension +import WidgetKit + +@available(iOS 17.0, *) +struct DisableVPNIntent: AppIntent { + + static let title: LocalizedStringResource = "Disable VPN" + static let description: LocalizedStringResource = "Disables the DuckDuckGo VPN" + static let openAppWhenRun: Bool = false + + @MainActor + func perform() async throws -> some IntentResult { + do { + let managers = try await NETunnelProviderManager.loadAllFromPreferences() + guard let manager = managers.first else { + return .result() + } + + manager.isOnDemandEnabled = false + try await manager.saveToPreferences() + manager.connection.stopVPNTunnel() + + WidgetCenter.shared.reloadTimelines(ofKind: "VPNStatusWidget") + var iterations = 0 + + while iterations <= 10 { + try? await Task.sleep(interval: .seconds(0.5)) + + if manager.connection.status == .disconnected { + return .result() + } + + iterations += 1 + } + + return .result() + } catch { + return .result() + } + } + +} + +@available(iOS 17.0, *) +struct EnableVPNIntent: AppIntent { + + static let title: LocalizedStringResource = "Enable VPN" + static let description: LocalizedStringResource = "Enables the DuckDuckGo VPN" + static let openAppWhenRun: Bool = false + + @MainActor + func perform() async throws -> some IntentResult { + do { + let managers = try await NETunnelProviderManager.loadAllFromPreferences() + guard let manager = managers.first else { + return .result() + } + + manager.isOnDemandEnabled = true + try await manager.saveToPreferences() + try manager.connection.startVPNTunnel() + + WidgetCenter.shared.reloadTimelines(ofKind: "VPNStatusWidget") + var iterations = 0 + + while iterations <= 10 { + try? await Task.sleep(interval: .seconds(0.5)) + + if manager.connection.status == .connected { + return .result() + } + + iterations += 1 + } + + return .result() + } catch { + return .result() + } + } + +} diff --git a/DuckDuckGo/VPNWaitlist.swift b/DuckDuckGo/VPNWaitlist.swift new file mode 100644 index 0000000000..522a31a414 --- /dev/null +++ b/DuckDuckGo/VPNWaitlist.swift @@ -0,0 +1,118 @@ +// +// VPNWaitlist.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if NETWORK_PROTECTION + +import Foundation +import BrowserServicesKit +import Combine +import Core +import Waitlist +import NetworkProtection + +final class VPNWaitlist: Waitlist { + + enum AccessType { + /// Used if the user does not have waitlist feature flag access + case none + + /// Used if the user has waitlist feature flag access, but has not joined the waitlist + case waitlistAvailable + + /// Used if the user has waitlist feature flag access, and has joined the waitlist + case waitlistJoined + + /// Used if the user has been invited via the waitlist, but needs to accept the Privacy Policy and Terms of Service + case waitlistInvitedPendingTermsAcceptance + + /// Used if the user has been invited via the waitlist and has accepted the Privacy Policy and Terms of Service + case waitlistInvited + + /// Used if the user has been invited to test Network Protection directly + case inviteCodeInvited + } + + static let identifier: String = "vpn" + static let apiProductName: String = "networkprotection_ios" + static let downloadURL: URL = URL.windows + + static let shared: VPNWaitlist = .init() + + static let backgroundTaskName = "VPN Waitlist Status Task" + static let backgroundRefreshTaskIdentifier = "com.duckduckgo.app.vpnWaitlistStatus" + static let notificationIdentifier = "com.duckduckgo.ios.vpn.invite-code-available" + static let inviteAvailableNotificationTitle = UserText.networkProtectionWaitlistNotificationTitle + static let inviteAvailableNotificationBody = UserText.networkProtectionWaitlistNotificationText + + var isAvailable: Bool { + let hasWaitlistAccess = featureFlagger.isFeatureOn(.networkProtectionWaitlistAccess) + let isWaitlistActive = featureFlagger.isFeatureOn(.networkProtectionWaitlistActive) + return hasWaitlistAccess && isWaitlistActive + } + + var isWaitlistRemoved: Bool { + return false + } + + let waitlistStorage: WaitlistStorage + let waitlistRequest: WaitlistRequest + private let featureFlagger: FeatureFlagger + private let networkProtectionAccess: NetworkProtectionAccess + + init(store: WaitlistStorage, request: WaitlistRequest, featureFlagger: FeatureFlagger, networkProtectionAccess: NetworkProtectionAccess) { + self.waitlistStorage = store + self.waitlistRequest = request + self.featureFlagger = featureFlagger + self.networkProtectionAccess = networkProtectionAccess + } + + convenience init(store: WaitlistStorage, request: WaitlistRequest) { + self.init( + store: store, + request: request, + featureFlagger: AppDependencyProvider.shared.featureFlagger, + networkProtectionAccess: NetworkProtectionAccessController() + ) + } + + var settingsSubtitle: String { + switch networkProtectionAccess.networkProtectionAccessType() { + case .none: + return "" + case .waitlistAvailable: + return UserText.networkProtectionSettingsSubtitleNotJoined + case .waitlistJoined: + return UserText.networkProtectionSettingsSubtitleJoinedButNotInvited + case .waitlistInvitedPendingTermsAcceptance: + return UserText.networkProtectionSettingsSubtitleJoinedAndInvited + case .waitlistInvited, .inviteCodeInvited: + assertionFailure("These states should use the VPN connection status") + return "" + } + } + +} + +extension WaitlistViewModel.ViewCustomAction { + static var openNetworkProtectionInviteCodeScreen = WaitlistViewModel.ViewCustomAction(identifier: "openNetworkProtectionInviteCodeScreen") + static var openNetworkProtectionPrivacyPolicyScreen = WaitlistViewModel.ViewCustomAction(identifier: "openNetworkProtectionPrivacyPolicyScreen") + static var acceptNetworkProtectionTerms = WaitlistViewModel.ViewCustomAction(identifier: "acceptNetworkProtectionTerms") +} + +#endif diff --git a/DuckDuckGo/VPNWaitlistDebugViewController.swift b/DuckDuckGo/VPNWaitlistDebugViewController.swift new file mode 100644 index 0000000000..6b3fa717ef --- /dev/null +++ b/DuckDuckGo/VPNWaitlistDebugViewController.swift @@ -0,0 +1,199 @@ +// +// VPNWaitlistDebugViewController.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if NETWORK_PROTECTION + +import UIKit +import Core +import BackgroundTasks +import Waitlist + +final class VPNWaitlistDebugViewController: UITableViewController { + + enum Sections: Int, CaseIterable { + + case waitlistInformation + case debuggingActions + + } + + private let waitlistInformationTitles = [ + WaitlistInformationRows.waitlistTimestamp: "Timestamp", + WaitlistInformationRows.waitlistToken: "Token", + WaitlistInformationRows.waitlistInviteCode: "Invite Code", + WaitlistInformationRows.backgroundTask: "Earliest Refresh Date" + ] + + enum WaitlistInformationRows: Int, CaseIterable { + + case waitlistTimestamp + case waitlistToken + case waitlistInviteCode + case backgroundTask + + } + + private let debuggingActionTitles = [ + DebuggingActionRows.resetTermsAndConditionsAcceptance: "Reset T&C Acceptance", + DebuggingActionRows.scheduleWaitlistNotification: "Fire Waitlist Notification in 3 seconds", + DebuggingActionRows.setMockInviteCode: "Set Mock Invite Code", + DebuggingActionRows.deleteInviteCode: "Delete Invite Code" + ] + + enum DebuggingActionRows: Int, CaseIterable { + + case resetTermsAndConditionsAcceptance + case scheduleWaitlistNotification + case setMockInviteCode + case deleteInviteCode + + } + + private let storage = WaitlistKeychainStore(waitlistIdentifier: VPNWaitlist.identifier) + + private var backgroundTaskExecutionDate: String? + + override func viewDidLoad() { + super.viewDidLoad() + + let clearDataItem = UIBarButtonItem(image: UIImage(systemName: "trash")!, + style: .done, + target: self, + action: #selector(presentClearDataPrompt(_:))) + clearDataItem.tintColor = .systemRed + navigationItem.rightBarButtonItem = clearDataItem + + BGTaskScheduler.shared.getPendingTaskRequests { tasks in + if let task = tasks.first(where: { $0.identifier == VPNWaitlist.backgroundRefreshTaskIdentifier }) { + let formatter = DateFormatter() + formatter.dateStyle = .short + formatter.timeStyle = .medium + + self.backgroundTaskExecutionDate = formatter.string(from: task.earliestBeginDate!) + + DispatchQueue.main.async { + self.tableView.reloadData() + } + } + } + } + + override func numberOfSections(in tableView: UITableView) -> Int { + return Sections.allCases.count + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + switch Sections(rawValue: section)! { + case .waitlistInformation: return WaitlistInformationRows.allCases.count + case .debuggingActions: return DebuggingActionRows.allCases.count + } + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let section = Sections(rawValue: indexPath.section)! + + switch section { + case .waitlistInformation: + let cell = tableView.dequeueReusableCell(withIdentifier: "DetailCell", for: indexPath) + let row = WaitlistInformationRows(rawValue: indexPath.row)! + cell.textLabel?.text = waitlistInformationTitles[row] + + switch row { + case .waitlistTimestamp: + if let timestamp = storage.getWaitlistTimestamp() { + cell.detailTextLabel?.text = String(timestamp) + } else { + cell.detailTextLabel?.text = "None" + } + + case .waitlistToken: + cell.detailTextLabel?.text = storage.getWaitlistToken() ?? "None" + + case .waitlistInviteCode: + cell.detailTextLabel?.text = storage.getWaitlistInviteCode() ?? "None" + + case .backgroundTask: + cell.detailTextLabel?.text = backgroundTaskExecutionDate ?? "None" + } + + return cell + + case .debuggingActions: + let cell = tableView.dequeueReusableCell(withIdentifier: "ActionCell", for: indexPath) + let row = DebuggingActionRows(rawValue: indexPath.row)! + cell.textLabel?.text = debuggingActionTitles[row] + + return cell + } + + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let section = Sections(rawValue: indexPath.section)! + + switch section { + case .waitlistInformation: break + case .debuggingActions: + let row = DebuggingActionRows(rawValue: indexPath.row)! + + switch row { + case .resetTermsAndConditionsAcceptance: + var termsAndConditionsStore = NetworkProtectionTermsAndConditionsUserDefaultsStore() + termsAndConditionsStore.networkProtectionWaitlistTermsAndConditionsAccepted = false + case .scheduleWaitlistNotification: + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 3) { + self.storage.store(inviteCode: "ABCD1234") + VPNWaitlist.shared.sendInviteCodeAvailableNotification() + } + case .setMockInviteCode: + storage.store(inviteCode: "ABCD1234") + case .deleteInviteCode: + storage.delete(field: .inviteCode) + tableView.reloadData() + } + } + + tableView.deselectRow(at: indexPath, animated: true) + tableView.reloadData() + } + + @objc + private func presentClearDataPrompt(_ sender: AnyObject) { + let alert = UIAlertController(title: "Clear Waitlist Data?", message: nil, preferredStyle: .actionSheet) + + if UIDevice.current.userInterfaceIdiom == .pad { + alert.popoverPresentationController?.barButtonItem = (sender as? UIBarButtonItem) + } + + alert.addAction(UIAlertAction(title: "Clear Data", style: .destructive, handler: { _ in + self.clearDataAndReload() + })) + + alert.addAction(UIAlertAction(title: "Cancel", style: .cancel)) + + present(alert, animated: true) + } + + private func clearDataAndReload() { + storage.deleteWaitlistState() + tableView.reloadData() + } +} + +#endif diff --git a/DuckDuckGo/VPNWaitlistTermsAndConditionsViewController.swift b/DuckDuckGo/VPNWaitlistTermsAndConditionsViewController.swift new file mode 100644 index 0000000000..d1f80e13ca --- /dev/null +++ b/DuckDuckGo/VPNWaitlistTermsAndConditionsViewController.swift @@ -0,0 +1,73 @@ +// +// VPNWaitlistTermsAndConditionsViewController.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if NETWORK_PROTECTION + +import UIKit +import SwiftUI +import Core +import Waitlist + +@available(iOS 15.0, *) +final class VPNWaitlistTermsAndConditionsViewController: UIViewController { + + override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + + title = UserText.networkProtectionWaitlistJoinTitle + addHostingControllerToViewHierarchy() + } + + private func addHostingControllerToViewHierarchy() { + let waitlistView = VPNWaitlistPrivacyPolicyView { _ in + var termsAndConditionsStore = NetworkProtectionTermsAndConditionsUserDefaultsStore() + termsAndConditionsStore.networkProtectionWaitlistTermsAndConditionsAccepted = true + + self.navigationController?.popToRootViewController(animated: true) + let networkProtectionViewController = NetworkProtectionRootViewController() + self.navigationController?.pushViewController(networkProtectionViewController, animated: true) + } + + let waitlistViewController = UIHostingController(rootView: waitlistView) + waitlistViewController.view.backgroundColor = UIColor(designSystemColor: .background) + + addChild(waitlistViewController) + waitlistViewController.view.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(waitlistViewController.view) + waitlistViewController.didMove(toParent: self) + + NSLayoutConstraint.activate([ + waitlistViewController.view.widthAnchor.constraint(equalTo: view.widthAnchor), + waitlistViewController.view.heightAnchor.constraint(equalTo: view.heightAnchor), + waitlistViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), + waitlistViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor) + ]) + } + +} + +#endif diff --git a/DuckDuckGo/VPNWaitlistUserText.swift b/DuckDuckGo/VPNWaitlistUserText.swift new file mode 100644 index 0000000000..08c685910a --- /dev/null +++ b/DuckDuckGo/VPNWaitlistUserText.swift @@ -0,0 +1,67 @@ +// +// VPNWaitlistUserText.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +// swiftlint:disable line_length +struct VPNWaitlistUserText { + + static let networkProtectionPrivacyPolicySection1Title = "We don’t ask for any personal information from you in order to use this beta service." + static let networkProtectionPrivacyPolicySection1ListMarkdown = "This Privacy Policy is for our limited waitlist beta VPN product.\n\nOur main [Privacy Policy](https://duckduckgo.com/privacy) also applies here." + + static let networkProtectionPrivacyPolicySection2Title = "We don’t keep any logs of your online activity." + static let networkProtectionPrivacyPolicySection2List = "That means we have no way to tie what you do online to you as an individual and we don’t have any record of things like:\n • Website visits\n • DNS requests\n • Connections made\n • IP addresses used\n • Session lengths" + + static let networkProtectionPrivacyPolicySection3Title = "We only keep anonymous performance metrics that we cannot connect to your online activity." + static let networkProtectionPrivacyPolicySection3List = "Our servers store generic usage (for example, CPU load) and diagnostic data (for example, errors), but none of that data is connected to any individual’s activity.\n\nWe use this non-identifying information to monitor and ensure the performance and quality of the service, for example to make sure servers aren’t overloaded." + + static let networkProtectionPrivacyPolicySection4Title = "We use dedicated servers for all VPN traffic." + static let networkProtectionPrivacyPolicySection4List = "Dedicated servers means they are not shared with anyone else.\n\nWe rent our servers from providers we carefully selected because they meet our privacy requirements.\n\nWe have strict access controls in place so that only limited DuckDuckGo team members have access to our servers." + + static let networkProtectionPrivacyPolicySection5Title = "We protect and limit use of your data when you communicate directly with DuckDuckGo." + static let networkProtectionPrivacyPolicySection5List = "If you reach out to us for support by submitting a bug report or through email and agree to be contacted to troubleshoot the issue, we’ll contact you using the information you provide.\n\nIf you participate in a voluntary product survey or questionnaire and agree to provide further feedback, we may contact you using the information you provide.\n\nWe will permanently delete all personal information you provided to us (email, contact information), within 30 days after closing a support case or, in the case of follow up feedback, within 60 days after ending this beta service." + + static let networkProtectionTermsOfServiceTitle = "Terms of Service" + + static let networkProtectionTermsOfServiceSection1Title = "The service is for limited and personal use only." + static let networkProtectionTermsOfServiceSection1List = "This service is provided for your personal use only.\n\nYou are responsible for all activity in the service that occurs on or through your device.\n\nThis service may only be used through the DuckDuckGo app on the device on which you are given access. If you delete the DuckDuckGo app, you will lose access to the service.\n\nYou may not use this service through a third-party client." + + static let networkProtectionTermsOfServiceSection2Title = "You agree to comply with all applicable laws, rules, and regulations." + static let networkProtectionTermsOfServiceSection2ListMarkdown = "You agree that you will not use the service for any unlawful, illicit, criminal, or fraudulent purpose, or in any manner that could give rise to civil or criminal liability under applicable law.\n\nYou agree to comply with our [DuckDuckGo Terms of Service](https://duckduckgo.com/terms), which are incorporated by reference." + + static let networkProtectionTermsOfServiceSection3Title = "You must be eligible to use this service." + static let networkProtectionTermsOfServiceSection3List = "Access to this beta is randomly awarded. You are responsible for ensuring eligibility.\n\nYou must be at least 18 years old and live in a location where use of a VPN is legal in order to be eligible to use this service." + + static let networkProtectionTermsOfServiceSection4Title = "We provide this beta service as-is and without warranty." + static let networkProtectionTermsOfServiceSection4List = "This service is provided as-is and without warranties or guarantees of any kind.\n\nTo the extent possible under applicable law, DuckDuckGo will not be liable for any damage or loss arising from your use of the service. In any event, the total aggregate liability of DuckDuckGo shall not exceed $25 or the equivalent in your local currency.\n\nWe may in the future transfer responsibility for the service to a subsidiary of DuckDuckGo. If that happens, you agree that references to “DuckDuckGo” will refer to our subsidiary, which will then become responsible for providing the service and for any liabilities relating to it." + + static let networkProtectionTermsOfServiceSection5Title = "We may terminate access at any time." + static let networkProtectionTermsOfServiceSection5List = "We reserve the right to revoke access to the service at any time in our sole discretion.\n\nWe may also terminate access for violation of these terms, including for repeated infringement of the intellectual property rights of others." + + static let networkProtectionTermsOfServiceSection6Title = "The service is free during the beta period." + static let networkProtectionTermsOfServiceSection6List = "Access to this service is currently free of charge, but that is limited to this beta period.\n\nYou understand and agree that this service is provided on a temporary, testing basis only." + + static let networkProtectionTermsOfServiceSection7Title = "We are continually updating the service." + static let networkProtectionTermsOfServiceSection7List = "The service is in beta, and we are regularly changing it.\n\nService coverage, speed, server locations, and quality may vary without warning." + + static let networkProtectionTermsOfServiceSection8Title = "We need your feedback." + static let networkProtectionTermsOfServiceSection8List = "You may be asked during the beta period to provide feedback about your experience. Doing so is optional and your feedback may be used to improve the service.\n\nIf you have enabled notifications for the DuckDuckGo app, we may use notifications to ask about your experience. You can disable notifications if you do not want to receive them." + +} +// swiftlint:enable line_length diff --git a/DuckDuckGo/VPNWaitlistView.swift b/DuckDuckGo/VPNWaitlistView.swift new file mode 100644 index 0000000000..3e5bb91b92 --- /dev/null +++ b/DuckDuckGo/VPNWaitlistView.swift @@ -0,0 +1,387 @@ +// +// VPNWaitlistView.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if NETWORK_PROTECTION + +import SwiftUI +import Core +import Waitlist +import DesignResourcesKit + +@available(iOS 15.0, *) +struct VPNWaitlistView: View { + + @EnvironmentObject var viewModel: WaitlistViewModel + + var body: some View { + switch viewModel.viewState { + case .notJoinedQueue: + VPNWaitlistSignUpView(requestInFlight: false) { action in + Task { await viewModel.perform(action: action) } + } + case .joiningQueue: + VPNWaitlistSignUpView(requestInFlight: true) { action in + Task { await viewModel.perform(action: action) } + } + case .joinedQueue(let state): + VPNWaitlistJoinedWaitlistView(notificationState: state) { action in + Task { await viewModel.perform(action: action) } + } + case .invited: + VPNWaitlistInvitedView { action in + Task { await viewModel.perform(action: action) } + } + case .waitlistRemoved: + fatalError("State not supported for VPN waitlists") + } + } +} + +@available(iOS 15.0, *) +struct VPNWaitlistSignUpView: View { + + let requestInFlight: Bool + + let action: WaitlistViewActionHandler + + var body: some View { + GeometryReader { proxy in + ScrollView { + VStack(alignment: .center, spacing: 8) { + HeaderView(imageName: "JoinVPNWaitlist", title: UserText.networkProtectionWaitlistJoinTitle) + + Text(UserText.networkProtectionWaitlistJoinSubtitle1) + .daxBodyRegular() + .foregroundColor(.waitlistTextSecondary) + .multilineTextAlignment(.center) + .lineSpacing(6) + + Text(UserText.networkProtectionWaitlistJoinSubtitle2) + .daxBodyRegular() + .foregroundColor(.waitlistTextSecondary) + .multilineTextAlignment(.center) + .lineSpacing(6) + .padding(.top, 18) + + Button(UserText.networkProtectionWaitlistButtonJoinWaitlist, action: { action(.joinQueue) }) + .buttonStyle(RoundedButtonStyle(enabled: !requestInFlight)) + .padding(.top, 24) + + Button(UserText.networkProtectionWaitlistButtonExistingInviteCode, action: { + action(.custom(.openNetworkProtectionInviteCodeScreen)) + }) + .buttonStyle(RoundedButtonStyle(enabled: true, style: .bordered)) + .padding(.top, 18) + + if requestInFlight { + HStack { + Text(UserText.waitlistJoining) + .daxSubheadRegular() + .foregroundColor(.waitlistTextSecondary) + + ActivityIndicator(style: .medium) + } + .padding(.top, 14) + } + + Text(UserText.networkProtectionWaitlistAvailabilityDisclaimer) + .font(.footnote) + .foregroundStyle(Color.secondary) + .padding(.top, 24) + + Spacer() + } + .padding([.leading, .trailing], 24) + .frame(minHeight: proxy.size.height) + } + } + } + +} + +// MARK: - Joined Waitlist Views + +@available(iOS 15.0, *) +struct VPNWaitlistJoinedWaitlistView: View { + + let notificationState: WaitlistViewModel.NotificationPermissionState + + let action: (WaitlistViewModel.ViewAction) -> Void + + var body: some View { + VStack(spacing: 16) { + HeaderView(imageName: "JoinedVPNWaitlist", title: UserText.networkProtectionWaitlistJoinedTitle) + + switch notificationState { + case .notificationAllowed: + Text(UserText.networkProtectionWaitlistJoinedWithNotificationsSubtitle1) + .daxBodyRegular() + .foregroundColor(.waitlistTextSecondary) + .lineSpacing(6) + + Text(UserText.networkProtectionWaitlistJoinedWithNotificationsSubtitle2) + .daxBodyRegular() + .foregroundColor(.waitlistTextSecondary) + .lineSpacing(6) + + default: + Text(UserText.networkProtectionWaitlistJoinedWithNotificationsSubtitle1) + .daxBodyRegular() + .foregroundColor(.waitlistTextSecondary) + .lineSpacing(6) + + if notificationState == .notificationsDisabled { + AllowNotificationsView(action: action) + .padding(.top, 4) + } else { + Button(UserText.waitlistNotifyMe) { + action(.requestNotificationPermission) + } + .buttonStyle(RoundedButtonStyle(enabled: true)) + .padding(.top, 32) + } + } + + Spacer() + } + .padding([.leading, .trailing], 24) + .multilineTextAlignment(.center) + } + +} + +@available(iOS 15.0, *) +private struct AllowNotificationsView: View { + + let action: (WaitlistViewModel.ViewAction) -> Void + + var body: some View { + + VStack(spacing: 20) { + + Text(UserText.waitlistNotificationDisabled) + .daxBodyRegular() + .foregroundColor(.waitlistTextSecondary) + .fixMultilineScrollableText() + .lineSpacing(5) + + Button(UserText.waitlistAllowNotifications) { + action(.openNotificationSettings) + } + .buttonStyle(RoundedButtonStyle(enabled: true)) + + } + .padding(24) + .background(Color.waitlistNotificationBackground) + .cornerRadius(8) + .shadow(color: .black.opacity(0.05), radius: 12, x: 0, y: 4) + + } + +} + +// MARK: - Invite Available Views + +private struct ShareButtonFramePreferenceKey: PreferenceKey { + static var defaultValue: CGRect = .zero + static func reduce(value: inout CGRect, nextValue: () -> CGRect) {} +} + +@available(iOS 15.0, *) +struct VPNWaitlistInvitedView: View { + + let benefitsList: [VPNWaitlistBenefit] = [ + .init( + imageName: "Shield-16", + title: UserText.networkProtectionWaitlistInvitedSection1Title, + subtitle: UserText.networkProtectionWaitlistInvitedSection1Subtitle + ), + .init( + imageName: "Rocket-16", + title: UserText.networkProtectionWaitlistInvitedSection2Title, + subtitle: UserText.networkProtectionWaitlistInvitedSection2Subtitle + ), + .init( + imageName: "Card-16", + title: UserText.networkProtectionWaitlistInvitedSection3Title, + subtitle: UserText.networkProtectionWaitlistInvitedSection3Subtitle + ), + ] + + let action: WaitlistViewActionHandler + + @State private var shareButtonFrame: CGRect = .zero + + var body: some View { + GeometryReader { proxy in + ScrollView { + VStack(alignment: .center, spacing: 0) { + HeaderView(imageName: "InvitedVPNWaitlist", title: UserText.networkProtectionWaitlistInvitedTitle) + + Text(UserText.networkProtectionWaitlistInvitedSubtitle) + .daxBodyRegular() + .foregroundColor(.waitlistTextSecondary) + .padding(.top, 16) + .lineSpacing(6) + .fixedSize(horizontal: false, vertical: true) + + VStack(spacing: 16.0) { + ForEach(benefitsList) { WaitlistListEntryView(viewData: $0) } + } + .padding(.top, 24) + + Button(UserText.networkProtectionWaitlistGetStarted, action: { action(.custom(.openNetworkProtectionPrivacyPolicyScreen)) }) + .buttonStyle(RoundedButtonStyle(enabled: true)) + .padding(.top, 32) + + Text(UserText.networkProtectionWaitlistAvailabilityDisclaimer) + .font(.footnote) + .foregroundStyle(Color.secondary) + .padding(.top, 24) + + Spacer() + + } + .frame(maxWidth: .infinity, minHeight: proxy.size.height) + .padding([.leading, .trailing], 18) + .multilineTextAlignment(.center) + } + } + } +} + +@available(iOS 15.0, *) +struct VPNWaitlistPrivacyPolicyView: View { + + let action: WaitlistViewActionHandler + + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 0) { + Text(UserText.networkProtectionPrivacyPolicyTitle) + .font(.system(size: 17, weight: .bold)) + .multilineTextAlignment(.leading) + + Group { + Text(VPNWaitlistUserText.networkProtectionPrivacyPolicySection1Title).titleStyle() + + Text(.init(VPNWaitlistUserText.networkProtectionPrivacyPolicySection1ListMarkdown)).bodyStyle() + Text(VPNWaitlistUserText.networkProtectionPrivacyPolicySection2Title).titleStyle() + Text(VPNWaitlistUserText.networkProtectionPrivacyPolicySection2List).bodyStyle() + Text(VPNWaitlistUserText.networkProtectionPrivacyPolicySection3Title).titleStyle() + Text(VPNWaitlistUserText.networkProtectionPrivacyPolicySection3List).bodyStyle() + Text(VPNWaitlistUserText.networkProtectionPrivacyPolicySection4Title).titleStyle() + Text(VPNWaitlistUserText.networkProtectionPrivacyPolicySection4List).bodyStyle() + Text(VPNWaitlistUserText.networkProtectionPrivacyPolicySection5Title).titleStyle() + Text(VPNWaitlistUserText.networkProtectionPrivacyPolicySection5List).bodyStyle() + } + + Text(VPNWaitlistUserText.networkProtectionTermsOfServiceTitle) + .font(.system(size: 17, weight: .bold)) + .multilineTextAlignment(.leading) + .padding(.top, 28) + .padding(.bottom, 14) + + Group { + Text(VPNWaitlistUserText.networkProtectionTermsOfServiceSection1Title).titleStyle(topPadding: 0) + Text(VPNWaitlistUserText.networkProtectionTermsOfServiceSection1List).bodyStyle() + Text(VPNWaitlistUserText.networkProtectionTermsOfServiceSection2Title).titleStyle() + Text(.init(VPNWaitlistUserText.networkProtectionTermsOfServiceSection2ListMarkdown)).bodyStyle() + Text(VPNWaitlistUserText.networkProtectionTermsOfServiceSection3Title).titleStyle() + Text(VPNWaitlistUserText.networkProtectionTermsOfServiceSection3List).bodyStyle() + Text(VPNWaitlistUserText.networkProtectionTermsOfServiceSection4Title).titleStyle() + Text(VPNWaitlistUserText.networkProtectionTermsOfServiceSection4List).bodyStyle() + Text(VPNWaitlistUserText.networkProtectionTermsOfServiceSection5Title).titleStyle() + Text(VPNWaitlistUserText.networkProtectionTermsOfServiceSection5List).bodyStyle() + } + + Group { + Text(VPNWaitlistUserText.networkProtectionTermsOfServiceSection6Title).titleStyle() + Text(VPNWaitlistUserText.networkProtectionTermsOfServiceSection6List).bodyStyle() + Text(VPNWaitlistUserText.networkProtectionTermsOfServiceSection7Title).titleStyle() + Text(VPNWaitlistUserText.networkProtectionTermsOfServiceSection7List).bodyStyle() + Text(VPNWaitlistUserText.networkProtectionTermsOfServiceSection8Title).titleStyle() + Text(VPNWaitlistUserText.networkProtectionTermsOfServiceSection8List).bodyStyle() + } + + Button(UserText.networkProtectionWaitlistAgreeAndContinue, action: { action(.custom(.acceptNetworkProtectionTerms)) }) + .buttonStyle(RoundedButtonStyle(enabled: true)) + .padding(.top, 24) + } + .padding(.all, 20) + } + } + +} + +struct VPNWaitlistBenefit: Identifiable { + let id = UUID() + let imageName: String + let title: String + let subtitle: String +} + +private struct WaitlistListEntryView: View { + + let viewData: VPNWaitlistBenefit + + var body: some View { + HStack(alignment: .center, spacing: 16) { + Image(viewData.imageName) + .frame(maxWidth: 16, maxHeight: 16) + + VStack(alignment: .leading, spacing: 2) { + Text(viewData.title) + .font(.system(size: 13, weight: .bold)) + .opacity(0.8) + .multilineTextAlignment(.leading) + .frame(maxWidth: .infinity, alignment: .leading) + + Text(viewData.subtitle) + .font(.system(size: 13)) + .opacity(0.6) + .multilineTextAlignment(.leading) + .frame(maxWidth: .infinity, alignment: .leading) + } + .frame(maxWidth: .infinity) + + Spacer() + } + } + +} + +private extension Text { + + func titleStyle(topPadding: CGFloat = 24, bottomPadding: CGFloat = 14) -> some View { + self + .font(.system(size: 13, weight: .bold)) + .multilineTextAlignment(.leading) + .padding(.top, topPadding) + .padding(.bottom, bottomPadding) + } + + func bodyStyle() -> some View { + self + .font(.system(size: 13)) + } + +} + +#endif diff --git a/DuckDuckGo/VPNWaitlistViewController.swift b/DuckDuckGo/VPNWaitlistViewController.swift new file mode 100644 index 0000000000..465a47e34e --- /dev/null +++ b/DuckDuckGo/VPNWaitlistViewController.swift @@ -0,0 +1,153 @@ +// +// VPNWaitlistViewController.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if NETWORK_PROTECTION + +import UIKit +import SwiftUI +import Core +import Waitlist + +@available(iOS 15.0, *) +final class VPNWaitlistViewController: UIViewController { + + private let viewModel: WaitlistViewModel + + override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { + self.viewModel = WaitlistViewModel(waitlist: VPNWaitlist.shared) + super.init(nibName: nil, bundle: nil) + self.viewModel.delegate = self + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + + title = UserText.networkProtectionWaitlistJoinTitle + + addHostingControllerToViewHierarchy() + + NotificationCenter.default.addObserver(self, + selector: #selector(updateViewState), + name: UIApplication.didBecomeActiveNotification, + object: nil) + + NotificationCenter.default.addObserver(self, + selector: #selector(updateViewState), + name: WaitlistKeychainStore.inviteCodeDidChangeNotification, + object: VPNWaitlist.identifier) + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + Task { + await self.viewModel.updateViewState() + } + } + + @objc + private func updateViewState() { + Task { + await self.viewModel.updateViewState() + } + } + + private func addHostingControllerToViewHierarchy() { + let waitlistView = VPNWaitlistView().environmentObject(viewModel) + let waitlistViewController = UIHostingController(rootView: waitlistView) + waitlistViewController.view.backgroundColor = UIColor(designSystemColor: .background) + + addChild(waitlistViewController) + waitlistViewController.view.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(waitlistViewController.view) + waitlistViewController.didMove(toParent: self) + + NSLayoutConstraint.activate([ + waitlistViewController.view.widthAnchor.constraint(equalTo: view.widthAnchor), + waitlistViewController.view.heightAnchor.constraint(equalTo: view.heightAnchor), + waitlistViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), + waitlistViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor) + ]) + } + +} + +@available(iOS 15.0, *) +extension VPNWaitlistViewController: WaitlistViewModelDelegate { + + func waitlistViewModelDidAskToReceiveJoinedNotification(_ viewModel: WaitlistViewModel) async -> Bool { + return await withCheckedContinuation { continuation in + let alertController = UIAlertController(title: UserText.networkProtectionNotificationPromptTitle, + message: UserText.networkProtectionNotificationPromptDescription, + preferredStyle: .alert) + alertController.overrideUserInterfaceStyle() + + alertController.addAction(title: UserText.waitlistNoThanks) { + continuation.resume(returning: false) + } + let notifyMeAction = UIAlertAction(title: UserText.waitlistNotifyMe, style: .default) { _ in + continuation.resume(returning: true) + } + + alertController.addAction(notifyMeAction) + alertController.preferredAction = notifyMeAction + + present(alertController, animated: true) + } + } + + func waitlistViewModelDidJoinQueueWithNotificationsAllowed(_ viewModel: WaitlistViewModel) { + VPNWaitlist.shared.scheduleBackgroundRefreshTask() + } + + func waitlistViewModel(_ viewModel: WaitlistViewModel, didTriggerCustomAction action: WaitlistViewModel.ViewCustomAction) { + if action == .openNetworkProtectionInviteCodeScreen { + let networkProtectionViewController = NetworkProtectionRootViewController { [weak self] in + guard let self = self, let rootViewController = self.navigationController?.viewControllers.first else { + assertionFailure("Failed to show NetP status view") + return + } + + let networkProtectionRootViewController = NetworkProtectionRootViewController() + self.navigationController?.setViewControllers([rootViewController, networkProtectionRootViewController], animated: true) + } + + self.navigationController?.pushViewController(networkProtectionViewController, animated: true) + } + + if action == .openNetworkProtectionPrivacyPolicyScreen { + let termsAndConditionsViewController = VPNWaitlistTermsAndConditionsViewController() + self.navigationController?.pushViewController(termsAndConditionsViewController, animated: true) + } + } + + func waitlistViewModelDidOpenInviteCodeShareSheet(_ viewModel: WaitlistViewModel, inviteCode: String, senderFrame: CGRect) { + // The VPN waitlist doesn't support the share sheet + } + + func waitlistViewModelDidOpenDownloadURLShareSheet(_ viewModel: WaitlistViewModel, senderFrame: CGRect) { + // The VPN waitlist doesn't support the share sheet + } + +} + +#endif diff --git a/DuckDuckGo/ViewExtension.swift b/DuckDuckGo/ViewExtension.swift index 83c7078b90..d7f45ec31c 100644 --- a/DuckDuckGo/ViewExtension.swift +++ b/DuckDuckGo/ViewExtension.swift @@ -33,3 +33,41 @@ extension View { } } } + +/* + These exensions are needed to provide the UI styling specs for Network Protection + However, at time of writing, they are not supported in iOS <=14. As Network Protection + is not supporting iOS <=14, these are being kept separate. + */ + +@available(iOS 15, *) +extension View { + @ViewBuilder + func applyInsetGroupedListStyle() -> some View { + self + .listStyle(.insetGrouped) + .hideScrollContentBackground() + .background( + Rectangle().ignoresSafeArea().foregroundColor(Color(designSystemColor: .background)) + ) + } + + @ViewBuilder + func increaseHeaderProminence() -> some View { + self.headerProminence(.increased) + } + + @ViewBuilder + private func hideScrollContentBackground() -> some View { + if #available(iOS 16, *) { + self.scrollContentBackground(.hidden) + } else { + let originalBackgroundColor = UITableView.appearance().backgroundColor + self.onAppear { + UITableView.appearance().backgroundColor = .clear + }.onDisappear { + UITableView.appearance().backgroundColor = originalBackgroundColor + } + } + } +} diff --git a/DuckDuckGo/WaitlistViews.swift b/DuckDuckGo/WaitlistViews.swift index fcede8ef56..0ced5a5e9f 100644 --- a/DuckDuckGo/WaitlistViews.swift +++ b/DuckDuckGo/WaitlistViews.swift @@ -23,11 +23,11 @@ import Waitlist struct WaitlistDownloadBrowserContentView: View { let action: WaitlistViewActionHandler - let constants: BrowserDowloadLinkConstants + let constants: BrowserDownloadLinkConstants - init(platform: BrowserDowloadLink, action: @escaping WaitlistViewActionHandler) { + init(platform: BrowserDownloadLink, action: @escaping WaitlistViewActionHandler) { self.action = action - self.constants = BrowserDowloadLinkConstants(platform: platform) + self.constants = BrowserDownloadLinkConstants(platform: platform) } @State private var shareButtonFrame: CGRect = .zero @@ -115,13 +115,13 @@ private struct ShareButtonFramePreferenceKey: PreferenceKey { static func reduce(value: inout CGRect, nextValue: () -> CGRect) {} } -enum BrowserDowloadLink { +enum BrowserDownloadLink { case windows case mac } -struct BrowserDowloadLinkConstants { - let platform: BrowserDowloadLink +struct BrowserDownloadLinkConstants { + let platform: BrowserDownloadLink var imageName: String { switch platform { diff --git a/DuckDuckGo/WindowsBrowserWaitlist.swift b/DuckDuckGo/WindowsBrowserWaitlist.swift index 1cab06d50a..ad628591e1 100644 --- a/DuckDuckGo/WindowsBrowserWaitlist.swift +++ b/DuckDuckGo/WindowsBrowserWaitlist.swift @@ -32,7 +32,7 @@ final class WindowsBrowserWaitlist: Waitlist { static let backgroundTaskName = "Windows Browser Waitlist Status Task" static let backgroundRefreshTaskIdentifier = "com.duckduckgo.app.windowsBrowserWaitlistStatus" - static let notificationIdentitier = "com.duckduckgo.ios.windows-browser.invite-code-available" + static let notificationIdentifier = "com.duckduckgo.ios.windows-browser.invite-code-available" static let inviteAvailableNotificationTitle = UserText.windowsWaitlistAvailableNotificationTitle static let inviteAvailableNotificationBody = UserText.waitlistAvailableNotificationBody diff --git a/DuckDuckGo/bg.lproj/Feedback.strings b/DuckDuckGo/bg.lproj/Feedback.strings index d48f78ba7b..a207c7cead 100644 --- a/DuckDuckGo/bg.lproj/Feedback.strings +++ b/DuckDuckGo/bg.lproj/Feedback.strings @@ -28,9 +28,6 @@ /* Class = "UIBarButtonItem"; title = "Back"; ObjectID = "JF8-Ow-zXL"; */ "JF8-Ow-zXL.title" = "Обратно"; -/* Class = "UIBarButtonItem"; title = "Submit"; ObjectID = "jr6-Al-4fr"; */ -"jr6-Al-4fr.title" = "Изпрати"; - /* Class = "UIButton"; normalTitle = "Submit"; ObjectID = "lsB-Kq-U8p"; */ "lsB-Kq-U8p.normalTitle" = "Изпрати"; @@ -58,9 +55,6 @@ /* Class = "UINavigationItem"; title = "Report Broken Site"; ObjectID = "WjT-HM-yim"; */ "WjT-HM-yim.title" = "Подаване на сигнал за повреден сайт"; -/* Class = "UILabel"; text = "Broken Site Reason"; ObjectID = "Wx3-Vb-ItS"; */ -"Wx3-Vb-ItS.text" = "Причина за нефункциониращ сайт"; - /* Class = "UINavigationItem"; title = "Share Feedback"; ObjectID = "Xb0-Zu-opM"; */ "Xb0-Zu-opM.title" = "Споделяне на отзив"; diff --git a/DuckDuckGo/bg.lproj/Localizable.strings b/DuckDuckGo/bg.lproj/Localizable.strings index 0745f2aa1a..ebb2fec0f5 100644 --- a/DuckDuckGo/bg.lproj/Localizable.strings +++ b/DuckDuckGo/bg.lproj/Localizable.strings @@ -1,6 +1,9 @@ /* No comment provided by engineer. */ "%@" = "%@"; +/* No comment provided by engineer. */ +"%@ [%@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)" = "%1$@ [%2$@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)"; + /* Buton label for Edit action */ "action.generic.edit" = "Редактиране"; @@ -103,6 +106,12 @@ /* Share action */ "action.title.share" = "Споделяне"; +/* Settings label for bottom position for the address bar */ +"address.bar.bottom" = "Отдолу"; + +/* Settings label for top position for the address bar */ +"address.bar.top" = "Най-горе"; + /* No comment provided by engineer. */ "addWidget.button" = "Добави приспособлението"; @@ -142,6 +151,225 @@ /* Shown on authentication screen */ "app.authentication.unlock" = "Отключване на DuckDuckGo."; +/* First part of about page content (note the trailing space) */ +"appTP.about.content1" = "You’ve probably heard about companies like Google and Facebook tracking you behind the scenes on third-party websites. But did you know they also track your personal information through apps on your device?\n\nIn 2022, DuckDuckGo found that "; + +/* Second part of about page content (note the trailing space) */ +"appTP.about.content2" = "over 85% of free iOS apps tested contained hidden trackers from other companies."; + +/* Third part of about page content (note the leading space) */ +"appTP.about.content3" = " Of the 395 apps tested, 60% sent data to Google. This happens even while you’re not using your device.\n\nTrackers in apps may have access to a lot more information than their website tracker cousins, such as your location down to which floor of a building you're on, how often you play games while at work, and when and how long you sleep each day. Even if you haven’t given apps explicit permission to collect data, they can still take it without your knowledge.\n\nTracking networks like Facebook and Google use these little pieces of information to build a digital profile about you. With it, tracking networks can manipulate what you see online and allow advertisers to bid on access to you based on your data.\n\nTrackers in apps is a BIG problem for privacy. But DuckDuckGo has a solution that can help.\n\nWhen enabled in the DuckDuckGo Privacy Browser app, App Tracking Protection blocks many trackers in other apps, not just the trackers we find on websites when you browse. These dual layers of protection reduce what companies know about you overall, so you can use your apps with more peace of mind, knowing you’re more protected."; + +/* Navigation Title for AppTP about page */ +"appTP.about.navTitle" = "About App Trackers"; + +/* Title for AppTP about page */ +"appTP.about.title" = "What Are App Trackers?"; + +/* Title for 'Report an Issue' button in the activity view. */ +"appTP.activityView.reportIssue" = "Report Issue"; + +/* Text label for switch that turns blocking on or off for a tracker */ +"appTP.blockTrackerText" = "Block this Tracker"; + +/* Detail string describing what AppTP is */ +"appTP.cell.detail" = "Блокиране на тракерите на Вашето устройство"; + +/* String indicating AppTP is disabled when viewed from the settings screen */ +"appTP.cell.disabled" = "Деактивирано"; + +/* String indicating AppTP is enabled when viewed from the settings screen */ +"appTP.cell.enabled" = "Активирано"; + +/* Info string informing the user what App Tracking Protection does. */ +"appTP.empty.disabled.info" = "Активирайте функцията App Tracking Protection, за да можем да блокираме досадните тракери в други приложения."; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.heading" = "We’re blocking hidden trackers"; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.info" = "Come back soon to see a list of all the app trackers we’ve blocked."; + +/* First answer for AppTP FAQ page */ +"appTP.faq.answer1" = "App Tracking Protection blocks app trackers from other companies, like when Facebook tries to track you in a banking app. Companies may still track you in apps they own."; + +/* Second answer for AppTP FAQ page */ +"appTP.faq.answer2" = "Yes! App Tracking Protection works across all apps on your device to block the most common hidden trackers we find trying to collect your personal info."; + +/* Third answer for AppTP FAQ page */ +"appTP.faq.answer3" = "We currently only block the most common trackers that we find on iOS. This helps us to comprehensively test App Tracking Protection and lower frequency of app breakage, while blocking up to 70% of all tracking requests."; + +/* Fourth answer for AppTP FAQ page */ +"appTP.faq.answer4" = "You’ll be asked to set up a virtual private network (VPN) connection, but you don't need to install a VPN app for App Tracking Protection to work.\n\nThis permission, which works only on your device, allows App Tracking Protection to monitor network traffic so that it can block known trackers."; + +/* Fifth answer for AppTP FAQ page */ +"appTP.faq.answer5" = "You can use App Tracking Protection at the same time as using an IKEv2 protocol VPN app on an iOS device. You won’t be able to use App Tracking Protection on an iOS device if you’re using a VPN app that uses a different type of protocol, like WireGuard or OpenVPN type VPNs."; + +/* Sixth answer for AppTP FAQ page */ +"appTP.faq.answer6" = "A VPN sends your data from the device to its own server, where it secures and anonymizes your data from prying eyes. However, this allows the VPN company to see your network traffic.\n\nApp Tracking Protection is different. Instead of sending your data to a VPN server, App Tracking Protection works only on your device, sitting between your apps and the servers they talk to.\n\nWhenever App Tracking Protection recognizes a known tracker, it blocks the tracker from sending personal information (such as your IP address, activity, and device details) off your device. All other traffic reaches its destination, so your apps work normally."; + +/* Seventh answer for AppTP FAQ page */ +"appTP.faq.answer7" = "App Tracking Protection works only on your device and doesn’t send your data off your device to DuckDuckGo. We don’t collect or store any data from your apps."; + +/* First question for AppTP FAQ page */ +"appTP.faq.question1" = "How does App Tracking Protection work?"; + +/* Second question for AppTP FAQ page */ +"appTP.faq.question2" = "Does App Tracking Protection block trackers in all apps on my device?"; + +/* Third question for AppTP FAQ page */ +"appTP.faq.question3" = "Does App Tracking Protection block all app trackers?"; + +/* Fourth question for AppTP FAQ page */ +"appTP.faq.question4" = "Why does App Tracking Protection use a VPN connection?"; + +/* Fifth question for AppTP FAQ page */ +"appTP.faq.question5" = "Will App Tracking Protection work if I also use a VPN app?"; + +/* Sixth question for AppTP FAQ page */ +"appTP.faq.question6" = "How is App Tracking Protection different from a VPN?"; + +/* Seventh question for AppTP FAQ page */ +"appTP.faq.question7" = "Is my data private?"; + +/* Title for AppTP FAQ page */ +"appTP.faq.title" = "App Tracking Protection FAQ"; + +/* Do not translate. StringsDict entry -- Count part of string 'App Tracking Protection blocked x tracking attempts today' */ +"appTP.home.blockedCount" = "appTP.home.blockedCount"; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the trailing space) */ +"appTP.home.blockedPrefix" = "App Tracking Protection блокира "; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the leading space) */ +"appTP.home.blockedSuffix" = " във Вашите приложения днес."; + +/* Prefix of string 'App Tracking Protection disabled. Tap to re-enable.' (note the trailing space) */ +"appTP.home.disabledPrefix" = "App Tracking Protection disabled. "; + +/* Suffix of string 'App Tracking Protection disabled. Tap to re-enable.' */ +"appTP.home.disabledSuffix" = "Tap to continue blocking tracking attempts across your apps."; + +/* Text indicating the tracking event occured 'just now'. Example: Last attempt 'just now' */ +"appTP.justNow" = "Току-що"; + +/* View to manage trackers for AppTP. Allows the user to turn trackers on or off. */ +"appTP.manageTrackers" = "Управление на тракери"; + +/* Button title for AppTP onboarding */ +"appTP.onboarding.continueButton" = "Continue"; + +/* Button title for AppTP onboarding to enable AppTP */ +"appTP.onboarding.enableeButton" = "Enable App Tracking Protection"; + +/* Button title for AppTP onboarding to learn more about AppTP */ +"appTP.onboarding.learnMoreButton" = "Learn More"; + +/* First part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info1" = "Over 85% of free iOS apps"; + +/* Second part of info on the first AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page1Info2" = " we’ve tested allow other companies to track your personal information, even when you’re sleeping."; + +/* Third part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info3" = "See who we catch trying to track you in your apps and take back control."; + +/* First part of info on the second AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page2Info1" = "App Tracking Protection "; + +/* Second part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info2" = "detects and blocks app trackers from other companies,"; + +/* Third part of info on the second AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page2Info3" = " like when Google attempts to track you in a health app."; + +/* Fourth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info4" = "It’s free,"; + +/* Fifth part of info on the second AppTP onboarding page (note the leading and trailing space) */ +"appTP.onboarding.page2Info5" = " and you can enjoy your apps as you normally would. Working in the background, it helps "; + +/* Sixth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info6" = "protect you night and day."; + +/* First part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info1" = "App Tracking Protection is not a VPN."; + +/* Second part of info on the third AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page3Info2" = " However, your device will recognize it as one. This is because it uses a local VPN connection to work."; + +/* Third part of info on the third AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page3Info3" = "App Tracking Protection is different. "; + +/* Fourth part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info4" = "It never routes app data through an external server."; + +/* Title for first AppTP onboarding page */ +"appTP.onboarding.title1" = "One easy step for better app privacy!"; + +/* Title for second AppTP onboarding page */ +"appTP.onboarding.title2" = "How does it work?"; + +/* Title for third AppTP onboarding page */ +"appTP.onboarding.title3" = "Who sees your data?"; + +/* Breakage report app name label */ +"appTP.report.appLabel" = "Which app is having issues?"; + +/* Breakage report app name placeholder */ +"appTP.report.appPlaceholder" = "App name"; + +/* Breakage report category label */ +"appTP.report.categoryLabel" = "Какво се случва?"; + +/* Breakage report comment label */ +"appTP.report.commentLabel" = "Comments"; + +/* Breakage report comment placeholder */ +"appTP.report.commentPlaceholder" = "Add additional details"; + +/* Breakage report footer explaining what is collected in the breakage report */ +"appTP.report.footer" = "In addition to the details entered into this form, your app issue report will contain:\n• A list of trackers blocked in the last 10 min\n• Whether App Tracking Protection is enabled\n• Aggregate DuckDuckGo app diagnostics"; + +/* Breakage report submit button */ +"appTP.report.submit" = "Изпрати"; + +/* Breakage report form title */ +"appTP.report.title" = "Report Issue"; + +/* Breakage report succcess message */ +"appTP.report.toast" = "Благодарим ви! Отзивът ви е изпратен."; + +/* Cancel button for 'Report an Issue' alert. */ +"appTP.reportAlert.cancel" = "Не сега"; + +/* Confirm button for 'Report an Issue' alert. */ +"appTP.reportAlert.confirm" = "Докладване на проблем"; + +/* Message for 'Report an Issue' alert. */ +"appTP.reportAlert.message" = "Let us know if you disabled App Tracking Protection for this specific tracker because it caused app issues. Your feedback helps us improve!"; + +/* Title for 'Report an Issue' alert. */ +"appTP.reportAlert.title" = "Report Issue?"; + +/* Toast notification diplayed after restoring the blocklist to default settings */ +"appTP.restoreDefaultsToast" = "Default settings restored"; + +/* Button to restore the blocklist to its default state. */ +"appTP.restoreDefualts" = "Възстановяване на настройките по подразбиране"; + +/* Title for the App Tracking Protection feature */ +"appTP.title" = "Защита от проследяване в приложения"; + +/* Text indicating when the tracker was last allowed. Example: Last attempt allowed (timeString) */ +"appTP.trackerAllowedTimestamp" = "Последен разрешен опит %@"; + +/* Text indicating when the tracker was last blocked. Example: Last attempt blocked (timeString) */ +"appTP.trackerBlockedTimestamp" = "Последен блокиран опит %@"; + +/* Do not translate. StringsDict entry -- Subtitle for tracking attempts in App Tracking Protection Activity View. Example: (count) tracking attempts */ +"appTP.trackingattempts" = "appTP.trackingattempts"; + /* Authentication Alert Sign In Button */ "auth.alert.login.button" = "Влизане"; @@ -301,6 +529,18 @@ /* Toast message when a login item without a title is deleted */ "autofill.logins.list.login-deleted-message-no-title" = "Данните за вход са изтрити"; +/* Title for a button that allows a user to reset their list of never saved sites */ +"autofill.logins.list.never.saved" = "Нулиране на изключените сайтове"; + +/* Cancel button for resetting list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.cancel" = "Отмени"; + +/* Confirm button to reset list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.confirm" = "Нулиране на изключените сайтове"; + +/* Alert title */ +"autofill.logins.list.never.saved.reset.action.title" = "Ако нулирате изключените сайтове, при следващото влизане в някой от тези сайтове ще бъдете подканени да запазите данните си за вход."; + /* Placeholder for search field on autofill login listing */ "autofill.logins.list.search-placeholder" = "Търсене на данни за вход"; @@ -335,7 +575,7 @@ "autofill.logins.prompt.password.button.title" = "Парола за %@"; /* Title for autofill login prompt */ -"autofill.logins.prompt.title" = "Да се използват ли запазените данни за вход?"; +"autofill.logins.prompt.title" = "Използване на запазена парола?"; /* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ "autofill.logins.search.no-results.subtitle" = "за '%@'"; @@ -385,27 +625,24 @@ /* Title for the alert dialog telling the user an updated username is no longer a private email address */ "autofill.removed.duck.address.title" = "Потребителското име на личен Duck Address беше премахнато"; +/* CTA displayed on modal asking if the user never wants to be prompted to save a login for this website agin */ +"autofill.save-login.never-prompt.CTA" = "Никога не питай за този сайт"; + /* Message displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.message" = "Данните за вход се съхраняват на сигурно място в менюто Данни за вход на Вашето устройство."; +"autofill.save-login.new-user.message" = "Паролите се съхраняват на сигурно място в менюто Данни за вход на Вашето устройство."; /* Title displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.title" = "Искате ли DuckDuckGo да запази данните за вход?"; +"autofill.save-login.new-user.title" = "Искате ли DuckDuckGo да запази Вашата парола?"; /* Cancel CTA displayed on modal asking for the user to save the login */ "autofill.save-login.not-now.CTA" = "Не записвай"; -/* Confirm CTA displayed on modal asking for the user to save the login */ -"autofill.save-login.save.CTA" = "Запазване на данните за вход"; - /* Title displayed on modal asking for the user to save the login */ "autofill.save-login.title" = "Запазване на данните за вход?"; /* Confirm CTA displayed on modal asking for the user to save the password */ "autofill.save-password.save.CTA" = "Запазване на паролата"; -/* Title displayed on modal asking for the user to save the password */ -"autofill.save-password.title" = "Запазване на паролата?"; - /* Accessibility title for a Show Password button displaying actial password instead of ***** */ "autofill.show-password" = "Показване на паролата"; @@ -413,7 +650,7 @@ "autofill.signin.to.manage" = "%@ за управление на Вашите Duck Address на това устройство."; /* Message displayed on modal asking for the user to update the password */ -"autofill.update-password.message" = "DuckDuckGo ще актуализира запазените данни за вход във Вашето устройство."; +"autofill.update-password.message" = "DuckDuckGo ще актуализира запазената парола във Вашето устройство."; /* Confirm CTA displayed on modal asking for the user to update the password */ "autofill.update-password.save.CTA" = "Актуализиране на паролата"; @@ -547,9 +784,21 @@ /* Broken Site Category */ "brokensite.category.videos" = "Видеото не се възпроизвежда"; +/* Broken Site Category Placeholder */ +"brokensite.categoryPlaceholder" = "Изберете проблем от списъка..."; + +/* Broken Site Category Section Title */ +"brokensite.categoryTitle" = "ИЗБЕРЕТЕ КАТЕГОРИЯ"; + +/* Broken Site Comment Placeholder */ +"brokensite.commentPlaceholder" = "Ако споделите повече подробности, може да ни помогне да се справим с този проблем"; + /* Broken Site Section Title */ "brokensite.sectionTitle" = "ОПИШЕТЕ ПРОБЛЕМА"; +/* No comment provided by engineer. */ +"bucket: %@" = "контейнер: %@"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Миналия месец"; @@ -781,6 +1030,39 @@ /* Empty search placeholder on bookmarks search */ "empty.search" = "Няма намерени съвпадения"; +/* No comment provided by engineer. */ +"Error" = "Грешка"; + +/* Button title to Sign In */ +"error.email-protection-sign-in.action" = "Влизане"; + +/* Alert message */ +"error.email-protection-sign-in.body" = "Съжаляваме, моля, влезте отново, за да активирате повторно функциите на Email Protection в този браузър."; + +/* Alert title */ +"error.email-protection-sign-in.title" = "Грешка в Email Protection"; + +/* Button title to open device settings */ +"error.insufficient-disk-space.action" = "Отваряне на настройки"; + +/* Alert message */ +"error.insufficient-disk-space.body" = "Изглежда, че няма свободно място за съхранение на Вашето устройство. Моля, освободете място, за да продължите."; + +/* Alert title */ +"error.insufficient-disk-space.title" = "Няма достатъчно място за съхранение"; + +/* Button title that is shutting down the app */ +"error.preemptive-crash.action" = "Затваряне на приложението"; + +/* Alert message */ +"error.preemptive-crash.body" = "Изглежда, е възникнал проблем с приложението и то трябва да бъде затворено. Моля, отворете отново, за да продължите."; + +/* Alert title */ +"error.preemptive-crash.title" = "Открит е проблем с приложението"; + +/* Generic error message on a dialog for when the cause is not known. */ +"error.unknown.try.again" = "An unknown error has occurred"; + /* No comment provided by engineer. */ "favorite" = "Любим"; @@ -1000,6 +1282,18 @@ /* Home tab title */ "homeTab.title" = "Начален екран"; +/* OK title for invite screen alert dismissal button */ +"invite.alert.ok.button" = "OK"; + +/* Continue button on an invite dialog */ +"invite.dialog.continue.button" = "Continue"; + +/* Get Started button on an invite dialog */ +"invite.dialog.get.started.button" = "Get Started"; + +/* Message to show after user enters an unrecognized invite code */ +"invite.dialog.unrecognized.code.message" = "We didn’t recognize this Invite Code."; + /* No comment provided by engineer. */ "keyCommandAddBookmark" = "Добави отметка"; @@ -1066,15 +1360,15 @@ /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "DuckDuckGo за Mac притежава необходимата скорост, както и очакваните от Вас функции за сърфиране, и предлага най-добрите в този клас елементи за защита."; -/* Body text for the macOS waitlist notification */ -"mac-waitlist.available.notification.body" = "Отворете поканата"; - /* Title for the macOS waitlist notification */ "mac-waitlist.available.notification.title" = "Приложението DuckDuckGo за Mac е готово!"; /* Title for the copy action */ "mac-waitlist.copy" = "Копиране"; +/* Description text above the Share Link button */ +"mac-waitlist.join-waitlist-screen.on-your-computer-go-to" = "На Вашия компютър с Windows отидете на:"; + /* Description text above the Share Link button */ "mac-waitlist.join-waitlist-screen.on-your-mac-go-to" = "На Вашия Mac отидете на:"; @@ -1087,6 +1381,9 @@ /* Disclaimer for the Join Waitlist screen */ "mac-waitlist.join-waitlist-screen.windows" = "Очаквайте скоро и за Windows!"; +/* Title for the macOS waitlist button redirecting to Windows waitlist */ +"mac-waitlist.join-waitlist-screen.windows-waitlist" = "Търсите версията за Windows?"; + /* Title for the settings subtitle */ "mac-waitlist.settings.browse-privately" = "Сърфирайте поверително с нашето приложение за Mac"; @@ -1097,7 +1394,7 @@ "mac-waitlist.share-sheet.title" = "DuckDuckGo за Mac"; /* Title for the Mac Waitlist feature */ -"mac-waitlist.title" = "Приложение DuckDuckGo за настолен компютър"; +"mac-waitlist.title" = "Приложение DuckDuckGo за Mac"; /* No comment provided by engineer. */ "menu.button.hint" = "Меню за сърфиране"; @@ -1114,9 +1411,81 @@ /* Edit button */ "navigation.title.edit" = "Редактиране"; +/* String indicating NetP is connected when viewed from the settings screen */ +"netP.cell.connected" = "Connected"; + +/* String indicating NetP is disconnected when viewed from the settings screen */ +"netP.cell.disconnected" = "Not connected"; + +/* Title for the Network Protection feature */ +"netP.title" = "Network Protection"; + +/* Message for the network protection invite dialog */ +"network.protection.invite.dialog.message" = "Enter your invite code to get started."; + +/* Title for the network protection invite screen */ +"network.protection.invite.dialog.title" = "You're invited to try Network Protection"; + +/* Prompt for the network protection invite code text field */ +"network.protection.invite.field.prompt" = "Invite Code"; + +/* Message explaining that netP is invite only */ +"network.protection.invite.only.message" = "DuckDuckGo Network Protection is currently invite-only."; + +/* Message for the network protection invite success view */ +"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; + +/* Title for the network protection invite success view */ +"network.protection.invite.success.title" = "Success! You’re in."; + +/* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ +"network.protection.status.connected.format" = "Connected - %@"; + +/* The label for the NetP VPN when connecting */ +"network.protection.status.connecting" = "Connecting..."; + +/* The label for the NetP VPN when disconnected */ +"network.protection.status.disconnected" = "Not connected"; + +/* The label for the NetP VPN when disconnecting */ +"network.protection.status.disconnecting" = "Disconnecting..."; + +/* Message label text for the netP status view */ +"network.protection.status.header.message" = "DuckDuckGo's VPN secures all of your device's Internet traffic anytime, anywhere."; + +/* Header title label text for the status view when netP is disconnected */ +"network.protection.status.header.title.off" = "Network Protection is Off"; + +/* Header title label text for the status view when netP is connected */ +"network.protection.status.header.title.on" = "Network Protection is On"; + +/* The status view 'Share Feedback' button which is shown inline on the status view after the \(netPInviteOnlyMessage) text */ +"network.protection.status.menu.share.feedback" = "Share Feedback"; + +/* Connection details label shown in NetworkProtection's status view. */ +"network.protection.status.view.connection.details" = "Connection Details"; + +/* Generic connection failed error message shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.message" = "Please try again later."; + +/* Generic connection failed error title shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; + +/* IP Address label shown in NetworkProtection's status view. */ +"network.protection.status.view.ip.address" = "IP Address"; + +/* Location label shown in NetworkProtection's status view. */ +"network.protection.status.view.location" = "Location"; + +/* Title label text for the status view when netP is disconnected */ +"network.protection.status.view.title" = "Network Protection"; + /* Do not translate - stringsdict entry */ "number.of.tabs" = "number.of.tabs"; +/* No comment provided by engineer. */ +"Ok" = "ОК"; + /* Text displayed on notification appearing in the address bar when the browser dismissed the cookie popup automatically rejecting it */ "omnibar.notification.cookies-managed" = "Бисквитките са настроени"; @@ -1156,6 +1525,18 @@ /* ’Link’ is link on a website */ "open.externally.failed" = "Не е намерено приложението, необходимо за отваряне на тази връзка"; +/* No comment provided by engineer. */ +"Please enter which app on your device is broken." = "Моля, въведете кое приложение на Вашето устройство е повредено."; + +/* Activate button */ +"pm.activate" = "Reactivate"; + +/* Cancel button */ +"pm.cancel" = "Cancel"; + +/* Deactivate button */ +"pm.deactivate" = "Deactivate"; + /* No comment provided by engineer. */ "preserveLogins.domain.list.footer" = "Уеб сайтовете разчитат на бисквитки, за да поддържат сесията ви активна. Когато направите сайта огнеустойчив, бисквитките няма да бъдат изтрити и ще останете вписани, дори след като използвате огнения бутон. Ние все пак блокираме тракери на трети страни, които се намират на огнеустойчиви сайтове."; @@ -1225,8 +1606,8 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Любими"; -/* No comment provided by engineer. */ -"settings.about.text" = "В DuckDuckGo задаваме новия стандарт на доверие онлайн.\n\nБраузърът за поверителност DuckDuckGo осигурява всички необходими елементи за защита на поверителността, докато търсите и сърфирате в мрежата, включително блокиране на тракери, по-интелигентно криптиране и поверително търсене с DuckDuckGo.\n\nВ крайна сметка интернет не трябва да създава чувство на страх, а получаването на поверителността, която заслужавате онлайн, трябва да е толкова просто, колкото пускането на щорите."; +/* about page */ +"settings.about.text" = "DuckDuckGo е независима компания, основана през 2008 г., за защита на личните данни в интернет за всеки, на когото му е омръзнало да бъде проследяван онлайн и иска лесно решение на проблема. Ние сме доказателство, че можете да получите реална и безкомпромисна защита на личните данни онлайн.\n\nБраузърът DuckDuckGo притежава функциите, които очаквате от един основен браузър, като отметки, раздели, пароли и много други, както и [редица мощни защити на поверителността](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/), които не се предлагат в повечето популярни браузъри по подразбиране. Този уникален и комплексен набор от защити на поверителността предлага сигурност при онлайн дейностите – търсене, сърфиране, изпращане на имейли и др.\n\nЗа да работят, нашите защити на поверителността не изискват да имате технически познания или да се извършвате сложни настройки. Необходимо е да използвате браузъра DuckDuckGo на всички Ваши устройства и ще получите поверителност по подразбиране.\n\nНо ако *наистина* искате да надникнете зад кулисите, и да разберете как работят защитите на поверителността на DuckDuckGo, можете да намерите повече информация на нашите [страници за помощ](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/)."; /* Report a Broken Site screen confirmation button */ "siteFeedback.buttonText" = "Изпращане на доклад"; @@ -1246,6 +1627,9 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Кой уебсайт е повреден?"; +/* No comment provided by engineer. */ +"sync.remove-device.message" = "„%@“ вече няма да има достъп до синхронизираните Ви данни."; + /* Accessibility label on remove button */ "tab.close.home" = "Затваряне на началния раздел"; @@ -1318,6 +1702,57 @@ /* Voice-search footer note with on-device privacy warning */ "voiceSearch.footer.note" = "Аудиото се обработва на устройството. То не се съхранява или споделя с никого, включително DuckDuckGo."; +/* Title for the button to enable push notifications in system settings */ +"waitlist.allow-notifications" = "Разрешаване на известия"; + +/* Body text for the waitlist notification */ +"waitlist.available.notification.body" = "Отворете поканата"; + +/* Title for the copy action */ +"waitlist.copy" = "Копиране"; + +/* Label text for the invite code */ +"waitlist.invite-code" = "Код за покана"; + +/* Step title on the invite screen */ +"waitlist.invite-screen.step.title" = "Стъпка %d"; + +/* Title for the invite code screen */ +"waitlist.invite-screen.youre-invited" = "Имате покана!"; + +/* Title for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.join" = "Включване в списък с чакащи за личен адрес"; + +/* Temporary status text for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.joining" = "Включване в списъка с чакащи..."; + +/* Title for the Share Link button */ +"waitlist.join-waitlist-screen.share-link" = "Споделяне на връзка"; + +/* Notification text for the waitlist */ +"waitlist.joined.no-notification.get-notification" = "УВЕДОМЕТЕ МЕ"; + +/* Title for the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.get-notification-confirmation-title" = "Искате ли да получите известие, когато дойде Вашият ред?"; + +/* Cancel button in the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.no-thanks" = "Не, благодаря"; + +/* Text used for the Notifications Disabled state */ +"waitlist.notification.disabled" = "Можем да ви изпратим известие, когато дойде вашият ред, но в момента известията за DuckDuckGo са деактивирани."; + +/* Privacy disclaimer for the Waitlist feature */ +"waitlist.privacy-disclaimer" = "Не е необходимо да споделяте лична информация, за да се включите в списъка с чакащи. Ще си осигурите място на опашката с клеймо за време, което съществува само на Вашето устройство, така можем да Ви уведомим, когато дойде Вашият ред."; + +/* Title for the queue screen */ +"waitlist.queue-screen.on-the-list" = "Включени сте в списъка с чакащи!"; + +/* Title for the settings subtitle */ +"waitlist.settings.download-available" = "Налично за изтегляне"; + +/* Title for the share sheet entry */ +"waitlist.share-sheet.title" = "Имате покана!"; + /* Confirmation message */ "web.url.remove.favorite.done" = "Премахнато от любими"; @@ -1342,3 +1777,51 @@ /* Alert title explaining the message is shown by a website */ "webJSAlert.website-message.format" = "Съобщение от %@:"; +/* No comment provided by engineer. */ +"Welcome to the Duck Side!" = "Добре дошли на наша страна!"; + +/* Title for the Windows waitlist notification */ +"windows-waitlist.available.notification.title" = "Изпробвайте DuckDuckGo за Windows!"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-1.description" = "Посетете този URL адрес на Вашето устройство с Windows, за да изтеглите:"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-2.description" = "Отворете инсталационния модул на DuckDuckGo в папката Изтеглени, изберете Инсталиране и след това въведете кода на поканата."; + +/* Subtitle for the Windows Waitlist Invite screen */ +"windows-waitlist.invite-screen.subtitle" = "Готови ли сте да използвате DuckDuckGo с Windows?"; + +/* Title for the Windows waitlist button redirecting to Mac waitlist */ +"windows-waitlist.join-waitlist-screen.mac-waitlist" = "Търсите версията за Mac?"; + +/* Title for the Join Windows Waitlist screen */ +"windows-waitlist.join-waitlist-screen.try-duckduckgo-for-windows" = "Възползвайте се от ранен достъп, за да изпробвате DuckDuckGo за Windows!"; + +/* Message for the alert to confirm enabling notifications */ +"windows-waitlist.joined.no-notification.get-notification-confirmation-message" = "Ще Ви изпратим известие, когато Вашето копие на DuckDuckGo за Windows е готово за изтегляне. "; + +/* Label text for the Joined Waitlist state with notifications declined */ +"windows-waitlist.joined.notifications-declined" = "Вашата покана за изпробване на DuckDuckGo за Windows ще получите тук. Проверете отново скоро или можем да ви изпратим известие, когато дойде вашият ред."; + +/* Label text for the Joined Waitlist state with notifications enabled */ +"windows-waitlist.joined.notifications-enabled" = "Ще Ви изпратим известие, когато Вашето копие на DuckDuckGo за Windows е готово за изтегляне."; + +/* Title for the settings subtitle */ +"windows-waitlist.settings.browse-privately" = "Сърфирайте поверително с нашето приложение за Windows"; + +/* Message used when sharing to iMessage. Parameter is an eight digit invite code. */ +"windows-waitlist.share-sheet.invite-code-message" = "Имате покана!\n\nГотови ли сте да използвате DuckDuckGo с Windows?Стъпка 1\nПосетете този URL адрес на Вашето устройство с Windows, за да изтеглите:\nhttps://duckduckgo.com/windows\n\nСтъпка 2\nОтворете инсталационния модул на DuckDuckGo в папката Изтеглени, изберете Инсталиране и след това въведете кода на поканата.\n\nКод на поканата: %@"; + +/* Message used when sharing to iMessage */ +"windows-waitlist.share-sheet.message" = "Готови ли сте да започнете поверително сърфиране с Windows?\n\nПосетете този URL адрес на компютър с Windows, за да изтеглите:\nhttps://duckduckgo.com/windows"; + +/* Summary text for the Windows browser waitlist */ +"windows-waitlist.summary" = "DuckDuckGo за Windows разполага с всичко необходимо за поверително сърфиране — поверително търсене, блокиране на тракери, принудително криптиране и блокиране на изскачащи прозорци с бисквитки, както и много от най-добрите в този клас елементи за защита."; + +/* Title for the Windows Waitlist feature */ +"windows-waitlist.title" = "Приложение DuckDuckGo за Windows"; + +/* Title for the Windows browser download link page */ +"windows-waitlist.waitlist-download-screen.try-duckduckgo-for-windows" = "Вземете DuckDuckGo за Windows!"; + diff --git a/DuckDuckGo/bg.lproj/Settings.strings b/DuckDuckGo/bg.lproj/Settings.strings index 5db5187244..961ce72b2a 100644 --- a/DuckDuckGo/bg.lproj/Settings.strings +++ b/DuckDuckGo/bg.lproj/Settings.strings @@ -37,6 +37,9 @@ /* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ "9Ko-0g-T3h.text" = "Размер на текста"; +/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ +"9kt-6R-XiZ.text" = "Заглавие"; + /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Стартиране на приложение"; @@ -73,6 +76,12 @@ /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Глобален контрол на поверителността (GPC)"; +/* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ +"CxT-QK-iVn.title" = "Добавяне"; + +/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ +"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; + /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Изход от приложението, 15 минути без активност"; @@ -151,6 +160,9 @@ /* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ "HLr-R8-xxF.text" = "Прегледи с продължително натискане"; +/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ +"hoT-Nu-KXP.text" = "Сърфирайте поверително с нашето приложение за Windows"; + /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Защитата на поверителността е активирана за всички сайтове"; @@ -163,6 +175,12 @@ /* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ "m23-t6-9cb.text" = "Споделяне на отзив"; +/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ +"mLn-1x-Fl5.title" = "Позиция на адресната лента"; + +/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ +"mqV-pf-NZ1.text" = "Позиция на адресната лента"; + /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Позволете на DuckDuckGo да управлява изскачащите прозорци за съгласие за бисквитки"; @@ -175,15 +193,27 @@ /* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ "oM7-1o-9oY.text" = "Относно DuckDuckGo"; +/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ +"opn-JO-idF.text" = "Най-горе"; + /* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ "OxE-MQ-uJk.headerTitle" = "Още от DuckDuckGo"; +/* Class = "UILabel"; text = "Sync"; ObjectID = "oXN-ez-gct"; */ +"oXN-ez-gct.text" = "Синхронизиране"; + /* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ "P0F-ts-ekd.text" = "Сърфирайте поверително с нашето приложение за Mac "; +/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ +"qah-gb-udB.text" = "Network Protection"; + /* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ "qeN-SV-zy7.text" = "Етикет"; +/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ +"RQ8-H1-Ez1.text" = "Приложение DuckDuckGo за Windows"; + /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Размер на текста"; @@ -232,8 +262,8 @@ /* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ "yvj-LL-MiR.text" = "Добавяне на приложението към панела"; -/* Class = "UILabel"; text = "DuckDuckGo Desktop App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "Приложение DuckDuckGo за настолен компютър"; +/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ +"Yz9-17-qnn.text" = "Приложение DuckDuckGo за Mac"; /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Нов раздел"; diff --git a/DuckDuckGo/cs.lproj/Feedback.strings b/DuckDuckGo/cs.lproj/Feedback.strings index b169dc5150..a9f2cec279 100644 --- a/DuckDuckGo/cs.lproj/Feedback.strings +++ b/DuckDuckGo/cs.lproj/Feedback.strings @@ -28,9 +28,6 @@ /* Class = "UIBarButtonItem"; title = "Back"; ObjectID = "JF8-Ow-zXL"; */ "JF8-Ow-zXL.title" = "Zpět"; -/* Class = "UIBarButtonItem"; title = "Submit"; ObjectID = "jr6-Al-4fr"; */ -"jr6-Al-4fr.title" = "Odeslat"; - /* Class = "UIButton"; normalTitle = "Submit"; ObjectID = "lsB-Kq-U8p"; */ "lsB-Kq-U8p.normalTitle" = "Odeslat"; @@ -58,9 +55,6 @@ /* Class = "UINavigationItem"; title = "Report Broken Site"; ObjectID = "WjT-HM-yim"; */ "WjT-HM-yim.title" = "Nahlásit nefunkční webové stránky"; -/* Class = "UILabel"; text = "Broken Site Reason"; ObjectID = "Wx3-Vb-ItS"; */ -"Wx3-Vb-ItS.text" = "Důvod nefunkční stránky"; - /* Class = "UINavigationItem"; title = "Share Feedback"; ObjectID = "Xb0-Zu-opM"; */ "Xb0-Zu-opM.title" = "Podělte se o zpětnou vazbu"; diff --git a/DuckDuckGo/cs.lproj/Localizable.strings b/DuckDuckGo/cs.lproj/Localizable.strings index 349ab7f5e7..02864ed0e8 100644 --- a/DuckDuckGo/cs.lproj/Localizable.strings +++ b/DuckDuckGo/cs.lproj/Localizable.strings @@ -1,6 +1,9 @@ /* No comment provided by engineer. */ "%@" = "%@"; +/* No comment provided by engineer. */ +"%@ [%@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)" = "%1$@ [%2$@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)"; + /* Buton label for Edit action */ "action.generic.edit" = "Upravit"; @@ -103,6 +106,12 @@ /* Share action */ "action.title.share" = "Sdílet"; +/* Settings label for bottom position for the address bar */ +"address.bar.bottom" = "Dole"; + +/* Settings label for top position for the address bar */ +"address.bar.top" = "Nahoru"; + /* No comment provided by engineer. */ "addWidget.button" = "Přidat widget"; @@ -142,6 +151,225 @@ /* Shown on authentication screen */ "app.authentication.unlock" = "Odemkněte DuckDuckGo."; +/* First part of about page content (note the trailing space) */ +"appTP.about.content1" = "You’ve probably heard about companies like Google and Facebook tracking you behind the scenes on third-party websites. But did you know they also track your personal information through apps on your device?\n\nIn 2022, DuckDuckGo found that "; + +/* Second part of about page content (note the trailing space) */ +"appTP.about.content2" = "over 85% of free iOS apps tested contained hidden trackers from other companies."; + +/* Third part of about page content (note the leading space) */ +"appTP.about.content3" = " Of the 395 apps tested, 60% sent data to Google. This happens even while you’re not using your device.\n\nTrackers in apps may have access to a lot more information than their website tracker cousins, such as your location down to which floor of a building you're on, how often you play games while at work, and when and how long you sleep each day. Even if you haven’t given apps explicit permission to collect data, they can still take it without your knowledge.\n\nTracking networks like Facebook and Google use these little pieces of information to build a digital profile about you. With it, tracking networks can manipulate what you see online and allow advertisers to bid on access to you based on your data.\n\nTrackers in apps is a BIG problem for privacy. But DuckDuckGo has a solution that can help.\n\nWhen enabled in the DuckDuckGo Privacy Browser app, App Tracking Protection blocks many trackers in other apps, not just the trackers we find on websites when you browse. These dual layers of protection reduce what companies know about you overall, so you can use your apps with more peace of mind, knowing you’re more protected."; + +/* Navigation Title for AppTP about page */ +"appTP.about.navTitle" = "About App Trackers"; + +/* Title for AppTP about page */ +"appTP.about.title" = "What Are App Trackers?"; + +/* Title for 'Report an Issue' button in the activity view. */ +"appTP.activityView.reportIssue" = "Report Issue"; + +/* Text label for switch that turns blocking on or off for a tracker */ +"appTP.blockTrackerText" = "Block this Tracker"; + +/* Detail string describing what AppTP is */ +"appTP.cell.detail" = "Blokuj trackery v aplikacích na svém zařízení"; + +/* String indicating AppTP is disabled when viewed from the settings screen */ +"appTP.cell.disabled" = "Zakázáno"; + +/* String indicating AppTP is enabled when viewed from the settings screen */ +"appTP.cell.enabled" = "Povoleno"; + +/* Info string informing the user what App Tracking Protection does. */ +"appTP.empty.disabled.info" = "Když zapneš funkci App Tracking Protection, budeme blokovat otravné trackery v ostatních aplikacích."; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.heading" = "We’re blocking hidden trackers"; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.info" = "Come back soon to see a list of all the app trackers we’ve blocked."; + +/* First answer for AppTP FAQ page */ +"appTP.faq.answer1" = "App Tracking Protection blocks app trackers from other companies, like when Facebook tries to track you in a banking app. Companies may still track you in apps they own."; + +/* Second answer for AppTP FAQ page */ +"appTP.faq.answer2" = "Yes! App Tracking Protection works across all apps on your device to block the most common hidden trackers we find trying to collect your personal info."; + +/* Third answer for AppTP FAQ page */ +"appTP.faq.answer3" = "We currently only block the most common trackers that we find on iOS. This helps us to comprehensively test App Tracking Protection and lower frequency of app breakage, while blocking up to 70% of all tracking requests."; + +/* Fourth answer for AppTP FAQ page */ +"appTP.faq.answer4" = "You’ll be asked to set up a virtual private network (VPN) connection, but you don't need to install a VPN app for App Tracking Protection to work.\n\nThis permission, which works only on your device, allows App Tracking Protection to monitor network traffic so that it can block known trackers."; + +/* Fifth answer for AppTP FAQ page */ +"appTP.faq.answer5" = "You can use App Tracking Protection at the same time as using an IKEv2 protocol VPN app on an iOS device. You won’t be able to use App Tracking Protection on an iOS device if you’re using a VPN app that uses a different type of protocol, like WireGuard or OpenVPN type VPNs."; + +/* Sixth answer for AppTP FAQ page */ +"appTP.faq.answer6" = "A VPN sends your data from the device to its own server, where it secures and anonymizes your data from prying eyes. However, this allows the VPN company to see your network traffic.\n\nApp Tracking Protection is different. Instead of sending your data to a VPN server, App Tracking Protection works only on your device, sitting between your apps and the servers they talk to.\n\nWhenever App Tracking Protection recognizes a known tracker, it blocks the tracker from sending personal information (such as your IP address, activity, and device details) off your device. All other traffic reaches its destination, so your apps work normally."; + +/* Seventh answer for AppTP FAQ page */ +"appTP.faq.answer7" = "App Tracking Protection works only on your device and doesn’t send your data off your device to DuckDuckGo. We don’t collect or store any data from your apps."; + +/* First question for AppTP FAQ page */ +"appTP.faq.question1" = "How does App Tracking Protection work?"; + +/* Second question for AppTP FAQ page */ +"appTP.faq.question2" = "Does App Tracking Protection block trackers in all apps on my device?"; + +/* Third question for AppTP FAQ page */ +"appTP.faq.question3" = "Does App Tracking Protection block all app trackers?"; + +/* Fourth question for AppTP FAQ page */ +"appTP.faq.question4" = "Why does App Tracking Protection use a VPN connection?"; + +/* Fifth question for AppTP FAQ page */ +"appTP.faq.question5" = "Will App Tracking Protection work if I also use a VPN app?"; + +/* Sixth question for AppTP FAQ page */ +"appTP.faq.question6" = "How is App Tracking Protection different from a VPN?"; + +/* Seventh question for AppTP FAQ page */ +"appTP.faq.question7" = "Is my data private?"; + +/* Title for AppTP FAQ page */ +"appTP.faq.title" = "App Tracking Protection FAQ"; + +/* Do not translate. StringsDict entry -- Count part of string 'App Tracking Protection blocked x tracking attempts today' */ +"appTP.home.blockedCount" = "appTP.home.blockedCount"; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the trailing space) */ +"appTP.home.blockedPrefix" = "Díky funkci App Tracking Protection dnes došlo k zablokování "; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the leading space) */ +"appTP.home.blockedSuffix" = " ve tvých aplikacích."; + +/* Prefix of string 'App Tracking Protection disabled. Tap to re-enable.' (note the trailing space) */ +"appTP.home.disabledPrefix" = "App Tracking Protection disabled. "; + +/* Suffix of string 'App Tracking Protection disabled. Tap to re-enable.' */ +"appTP.home.disabledSuffix" = "Tap to continue blocking tracking attempts across your apps."; + +/* Text indicating the tracking event occured 'just now'. Example: Last attempt 'just now' */ +"appTP.justNow" = "Právě teď"; + +/* View to manage trackers for AppTP. Allows the user to turn trackers on or off. */ +"appTP.manageTrackers" = "Správa trackerů"; + +/* Button title for AppTP onboarding */ +"appTP.onboarding.continueButton" = "Continue"; + +/* Button title for AppTP onboarding to enable AppTP */ +"appTP.onboarding.enableeButton" = "Enable App Tracking Protection"; + +/* Button title for AppTP onboarding to learn more about AppTP */ +"appTP.onboarding.learnMoreButton" = "Learn More"; + +/* First part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info1" = "Over 85% of free iOS apps"; + +/* Second part of info on the first AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page1Info2" = " we’ve tested allow other companies to track your personal information, even when you’re sleeping."; + +/* Third part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info3" = "See who we catch trying to track you in your apps and take back control."; + +/* First part of info on the second AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page2Info1" = "App Tracking Protection "; + +/* Second part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info2" = "detects and blocks app trackers from other companies,"; + +/* Third part of info on the second AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page2Info3" = " like when Google attempts to track you in a health app."; + +/* Fourth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info4" = "It’s free,"; + +/* Fifth part of info on the second AppTP onboarding page (note the leading and trailing space) */ +"appTP.onboarding.page2Info5" = " and you can enjoy your apps as you normally would. Working in the background, it helps "; + +/* Sixth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info6" = "protect you night and day."; + +/* First part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info1" = "App Tracking Protection is not a VPN."; + +/* Second part of info on the third AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page3Info2" = " However, your device will recognize it as one. This is because it uses a local VPN connection to work."; + +/* Third part of info on the third AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page3Info3" = "App Tracking Protection is different. "; + +/* Fourth part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info4" = "It never routes app data through an external server."; + +/* Title for first AppTP onboarding page */ +"appTP.onboarding.title1" = "One easy step for better app privacy!"; + +/* Title for second AppTP onboarding page */ +"appTP.onboarding.title2" = "How does it work?"; + +/* Title for third AppTP onboarding page */ +"appTP.onboarding.title3" = "Who sees your data?"; + +/* Breakage report app name label */ +"appTP.report.appLabel" = "Which app is having issues?"; + +/* Breakage report app name placeholder */ +"appTP.report.appPlaceholder" = "App name"; + +/* Breakage report category label */ +"appTP.report.categoryLabel" = "Co se stalo?"; + +/* Breakage report comment label */ +"appTP.report.commentLabel" = "Comments"; + +/* Breakage report comment placeholder */ +"appTP.report.commentPlaceholder" = "Add additional details"; + +/* Breakage report footer explaining what is collected in the breakage report */ +"appTP.report.footer" = "In addition to the details entered into this form, your app issue report will contain:\n• A list of trackers blocked in the last 10 min\n• Whether App Tracking Protection is enabled\n• Aggregate DuckDuckGo app diagnostics"; + +/* Breakage report submit button */ +"appTP.report.submit" = "Odeslat"; + +/* Breakage report form title */ +"appTP.report.title" = "Report Issue"; + +/* Breakage report succcess message */ +"appTP.report.toast" = "Děkujeme! Zpětná vazba odeslána."; + +/* Cancel button for 'Report an Issue' alert. */ +"appTP.reportAlert.cancel" = "Teď ne"; + +/* Confirm button for 'Report an Issue' alert. */ +"appTP.reportAlert.confirm" = "Nahlásit problém"; + +/* Message for 'Report an Issue' alert. */ +"appTP.reportAlert.message" = "Let us know if you disabled App Tracking Protection for this specific tracker because it caused app issues. Your feedback helps us improve!"; + +/* Title for 'Report an Issue' alert. */ +"appTP.reportAlert.title" = "Report Issue?"; + +/* Toast notification diplayed after restoring the blocklist to default settings */ +"appTP.restoreDefaultsToast" = "Default settings restored"; + +/* Button to restore the blocklist to its default state. */ +"appTP.restoreDefualts" = "Obnovit výchozí nastavení"; + +/* Title for the App Tracking Protection feature */ +"appTP.title" = "Ochrana před sledováním aplikace"; + +/* Text indicating when the tracker was last allowed. Example: Last attempt allowed (timeString) */ +"appTP.trackerAllowedTimestamp" = "Poslední povolený pokus %@"; + +/* Text indicating when the tracker was last blocked. Example: Last attempt blocked (timeString) */ +"appTP.trackerBlockedTimestamp" = "Poslední zablokovaný pokus %@"; + +/* Do not translate. StringsDict entry -- Subtitle for tracking attempts in App Tracking Protection Activity View. Example: (count) tracking attempts */ +"appTP.trackingattempts" = "appTP.trackingattempts"; + /* Authentication Alert Sign In Button */ "auth.alert.login.button" = "Přihlásit"; @@ -301,6 +529,18 @@ /* Toast message when a login item without a title is deleted */ "autofill.logins.list.login-deleted-message-no-title" = "Přihlašovací údaje smazány"; +/* Title for a button that allows a user to reset their list of never saved sites */ +"autofill.logins.list.never.saved" = "Obnovit vyloučené stránky"; + +/* Cancel button for resetting list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.cancel" = "Zrušit"; + +/* Confirm button to reset list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.confirm" = "Obnovit vyloučené stránky"; + +/* Alert title */ +"autofill.logins.list.never.saved.reset.action.title" = "Pokud vyloučené weby resetuješ, při příštím přihlašování na některý z nich se ti zobrazí výzva k uložení přihlašovacích údajů."; + /* Placeholder for search field on autofill login listing */ "autofill.logins.list.search-placeholder" = "Hledání přihlašovacích údajů"; @@ -323,7 +563,7 @@ "autofill.logins.prompt.auth.reason" = "Jestli chceš použít uložené přihlašovací údaje, odemkni zařízení"; /* Title for section of autofill logins that are an exact match to the current website */ -"autofill.logins.prompt.exact.match.title" = "Z tohoto webu"; +"autofill.logins.prompt.exact.match.title" = "Z tohoto webu"; /* Button title for autofill login prompt if more options are available */ "autofill.logins.prompt.more-options" = "Další možnosti"; @@ -335,7 +575,7 @@ "autofill.logins.prompt.password.button.title" = "Heslo pro %@"; /* Title for autofill login prompt */ -"autofill.logins.prompt.title" = "Použít uložené přihlášení?"; +"autofill.logins.prompt.title" = "Použít uložené heslo?"; /* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ "autofill.logins.search.no-results.subtitle" = "pro „%@“"; @@ -385,27 +625,24 @@ /* Title for the alert dialog telling the user an updated username is no longer a private email address */ "autofill.removed.duck.address.title" = "Soukromé uživatelské jméno Duck Address je smazané"; +/* CTA displayed on modal asking if the user never wants to be prompted to save a login for this website agin */ +"autofill.save-login.never-prompt.CTA" = "Na téhle stránce už se neptat"; + /* Message displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.message" = "Přihlašovací údaje jsou bezpečně uložené v zařízení v nabídce Přihlášení."; +"autofill.save-login.new-user.message" = "Hesla jsou bezpečně uložená v zařízení v nabídce Přihlášení."; /* Title displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.title" = "Má DuckDuckGo uložit přihlašovací údaje?"; +"autofill.save-login.new-user.title" = "Má DuckDuckGo uložit heslo?"; /* Cancel CTA displayed on modal asking for the user to save the login */ "autofill.save-login.not-now.CTA" = "Neukládat"; -/* Confirm CTA displayed on modal asking for the user to save the login */ -"autofill.save-login.save.CTA" = "Uložit přihlašovací údaje"; - /* Title displayed on modal asking for the user to save the login */ "autofill.save-login.title" = "Uložit přihlašovací údaje?"; /* Confirm CTA displayed on modal asking for the user to save the password */ "autofill.save-password.save.CTA" = "Uložit heslo"; -/* Title displayed on modal asking for the user to save the password */ -"autofill.save-password.title" = "Uložit heslo?"; - /* Accessibility title for a Show Password button displaying actial password instead of ***** */ "autofill.show-password" = "Zobrazit heslo"; @@ -413,7 +650,7 @@ "autofill.signin.to.manage" = "%@ pro správu adres Duck Address na tomto zařízení."; /* Message displayed on modal asking for the user to update the password */ -"autofill.update-password.message" = "DuckDuckGo zaktualizuje toto uložené přihlášení ve tvém zařízení."; +"autofill.update-password.message" = "DuckDuckGo aktualizuje tohle uložené heslo ve tvém zařízení."; /* Confirm CTA displayed on modal asking for the user to update the password */ "autofill.update-password.save.CTA" = "Aktualizuj si heslo"; @@ -547,9 +784,21 @@ /* Broken Site Category */ "brokensite.category.videos" = "Video se nepřehrálo"; +/* Broken Site Category Placeholder */ +"brokensite.categoryPlaceholder" = "Vyber problém ze seznamu..."; + +/* Broken Site Category Section Title */ +"brokensite.categoryTitle" = "VYBER KATEGORII"; + +/* Broken Site Comment Placeholder */ +"brokensite.commentPlaceholder" = "Tento problém snáz vyřešíme, když nám nasdílíš další informace"; + /* Broken Site Section Title */ "brokensite.sectionTitle" = "POPIŠTE, CO SE STALO"; +/* No comment provided by engineer. */ +"bucket: %@" = "bucket: % @"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Minulý měsíc"; @@ -781,6 +1030,39 @@ /* Empty search placeholder on bookmarks search */ "empty.search" = "Nenalezeny žádné shody"; +/* No comment provided by engineer. */ +"Error" = "Chyba"; + +/* Button title to Sign In */ +"error.email-protection-sign-in.action" = "Přihlásit"; + +/* Alert message */ +"error.email-protection-sign-in.body" = "Omlouváme se, ale pro opětovné zapnutí funkce Email Protection v tomhle prohlížeči se musíš znovu přihlásit."; + +/* Alert title */ +"error.email-protection-sign-in.title" = "Chyba funkce Email Protection"; + +/* Button title to open device settings */ +"error.insufficient-disk-space.action" = "Otevřené nastavení"; + +/* Alert message */ +"error.insufficient-disk-space.body" = "Vypadá to, že v úložišti už nemáš žádné volné místo. Pro pokračování musíš nějaké uvolnit."; + +/* Alert title */ +"error.insufficient-disk-space.title" = "Nedostatek místa v úložišti"; + +/* Button title that is shutting down the app */ +"error.preemptive-crash.action" = "Zavřít aplikaci"; + +/* Alert message */ +"error.preemptive-crash.body" = "Vypadá to, že s aplikací je nějaký problém a musí se zavřít. Pro pokračování aplikaci znovu spusť."; + +/* Alert title */ +"error.preemptive-crash.title" = "Vyskytl se problém s aplikací"; + +/* Generic error message on a dialog for when the cause is not known. */ +"error.unknown.try.again" = "An unknown error has occurred"; + /* No comment provided by engineer. */ "favorite" = "Oblíbené"; @@ -1000,6 +1282,18 @@ /* Home tab title */ "homeTab.title" = "Domů"; +/* OK title for invite screen alert dismissal button */ +"invite.alert.ok.button" = "OK"; + +/* Continue button on an invite dialog */ +"invite.dialog.continue.button" = "Continue"; + +/* Get Started button on an invite dialog */ +"invite.dialog.get.started.button" = "Get Started"; + +/* Message to show after user enters an unrecognized invite code */ +"invite.dialog.unrecognized.code.message" = "We didn’t recognize this Invite Code."; + /* No comment provided by engineer. */ "keyCommandAddBookmark" = "Přidat záložku"; @@ -1066,15 +1360,15 @@ /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "DuckDuckGo pro Mac má rychlost, kterou potřebuješ, funkce prohlížeče, které očekáváš, a obsahuje naše nejlepší nástroje na ochranu soukromí."; -/* Body text for the macOS waitlist notification */ -"mac-waitlist.available.notification.body" = "Otevřít pozvánku"; - /* Title for the macOS waitlist notification */ "mac-waitlist.available.notification.title" = "DuckDuckGo pro Mac už čeká!"; /* Title for the copy action */ "mac-waitlist.copy" = "Kopírovat"; +/* Description text above the Share Link button */ +"mac-waitlist.join-waitlist-screen.on-your-computer-go-to" = "V počítači s Windows přejdi na:"; + /* Description text above the Share Link button */ "mac-waitlist.join-waitlist-screen.on-your-mac-go-to" = "Na Macu přejdi na:"; @@ -1087,6 +1381,9 @@ /* Disclaimer for the Join Waitlist screen */ "mac-waitlist.join-waitlist-screen.windows" = "Verze pro Windows bude už brzo."; +/* Title for the macOS waitlist button redirecting to Windows waitlist */ +"mac-waitlist.join-waitlist-screen.windows-waitlist" = "Hledáš verzi pro Windows?"; + /* Title for the settings subtitle */ "mac-waitlist.settings.browse-privately" = "Zajisti si soukromí při prohlížení webu – v naší aplikaci pro Mac"; @@ -1097,7 +1394,7 @@ "mac-waitlist.share-sheet.title" = "DuckDuckGo pro Mac"; /* Title for the Mac Waitlist feature */ -"mac-waitlist.title" = "DuckDuckGo Desktop"; +"mac-waitlist.title" = "Aplikace DuckDuckGo pro Mac"; /* No comment provided by engineer. */ "menu.button.hint" = "Procházení nabídky"; @@ -1114,9 +1411,81 @@ /* Edit button */ "navigation.title.edit" = "Upravit"; +/* String indicating NetP is connected when viewed from the settings screen */ +"netP.cell.connected" = "Connected"; + +/* String indicating NetP is disconnected when viewed from the settings screen */ +"netP.cell.disconnected" = "Not connected"; + +/* Title for the Network Protection feature */ +"netP.title" = "Network Protection"; + +/* Message for the network protection invite dialog */ +"network.protection.invite.dialog.message" = "Enter your invite code to get started."; + +/* Title for the network protection invite screen */ +"network.protection.invite.dialog.title" = "You're invited to try Network Protection"; + +/* Prompt for the network protection invite code text field */ +"network.protection.invite.field.prompt" = "Invite Code"; + +/* Message explaining that netP is invite only */ +"network.protection.invite.only.message" = "DuckDuckGo Network Protection is currently invite-only."; + +/* Message for the network protection invite success view */ +"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; + +/* Title for the network protection invite success view */ +"network.protection.invite.success.title" = "Success! You’re in."; + +/* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ +"network.protection.status.connected.format" = "Connected - %@"; + +/* The label for the NetP VPN when connecting */ +"network.protection.status.connecting" = "Connecting..."; + +/* The label for the NetP VPN when disconnected */ +"network.protection.status.disconnected" = "Not connected"; + +/* The label for the NetP VPN when disconnecting */ +"network.protection.status.disconnecting" = "Disconnecting..."; + +/* Message label text for the netP status view */ +"network.protection.status.header.message" = "DuckDuckGo's VPN secures all of your device's Internet traffic anytime, anywhere."; + +/* Header title label text for the status view when netP is disconnected */ +"network.protection.status.header.title.off" = "Network Protection is Off"; + +/* Header title label text for the status view when netP is connected */ +"network.protection.status.header.title.on" = "Network Protection is On"; + +/* The status view 'Share Feedback' button which is shown inline on the status view after the \(netPInviteOnlyMessage) text */ +"network.protection.status.menu.share.feedback" = "Share Feedback"; + +/* Connection details label shown in NetworkProtection's status view. */ +"network.protection.status.view.connection.details" = "Connection Details"; + +/* Generic connection failed error message shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.message" = "Please try again later."; + +/* Generic connection failed error title shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; + +/* IP Address label shown in NetworkProtection's status view. */ +"network.protection.status.view.ip.address" = "IP Address"; + +/* Location label shown in NetworkProtection's status view. */ +"network.protection.status.view.location" = "Location"; + +/* Title label text for the status view when netP is disconnected */ +"network.protection.status.view.title" = "Network Protection"; + /* Do not translate - stringsdict entry */ "number.of.tabs" = "number.of.tabs"; +/* No comment provided by engineer. */ +"Ok" = "DOBŘE"; + /* Text displayed on notification appearing in the address bar when the browser dismissed the cookie popup automatically rejecting it */ "omnibar.notification.cookies-managed" = "O cookies je postaráno!"; @@ -1156,6 +1525,18 @@ /* ’Link’ is link on a website */ "open.externally.failed" = "Aplikaci nutnou k otevření tohoto odkazu nelze nalézt"; +/* No comment provided by engineer. */ +"Please enter which app on your device is broken." = "Prosíme o informaci, která aplikace ti na zařízení nefunguje."; + +/* Activate button */ +"pm.activate" = "Reactivate"; + +/* Cancel button */ +"pm.cancel" = "Cancel"; + +/* Deactivate button */ +"pm.deactivate" = "Deactivate"; + /* No comment provided by engineer. */ "preserveLogins.domain.list.footer" = "Webové stránky spoléhají na soubory cookie, aby vás udržely přihlášené. Když na webu zapnete ochranu, soubory cookie se nevymažou a zůstanete přihlášeni, a to i po použití tlačítka pro mazání. Stále přitom blokujeme sledovací programy třetích stran nalezené na webových stránkách s ochranou."; @@ -1225,8 +1606,8 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Oblíbené"; -/* No comment provided by engineer. */ -"settings.about.text" = "V DuckDuckGo přinášíme nový standard důvěry online.\n\nDuckDuckGo Privacy Browser poskytuje všechny základní prvky ochrany osobních údajů, které potřebujete k ochraně při vyhledávání a prohlížení webu, včetně blokování sledovacích zařízení, chytřejšího šifrování a soukromého vyhledávání DuckDuckGo.\n\nInternet by v konečném důsledku neměl být tak nebezpečný a získat ochranu osobních údajů, kterou si zasloužíte online, by mělo být stejně jednoduché jako zatáhnout žaluzie."; +/* about page */ +"settings.about.text" = "DuckDuckGo je nezávislá společnost založená v roce 2008, která se zabývá ochranou soukromí pro všechny, kdo už mají dost sledování na internetu a chtějí snadné řešení. Jsme důkazem, že skutečná ochrana soukromí na internetu nemusí mít kompromisy.\n\nProhlížeč DuckDuckGo má všechno, co od prohlížeče očekáváš, jako jsou záložky, karty, hesla a další funkce. A k tomu má víc než [desítku účinných funkcí na ochranu soukromí](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/), které většina oblíbených prohlížečů ve výchozím nastavení nenabízí. Tahle jedinečná komplexní sada na ochranu soukromí pomáhá chránit tvoje aktivity na internetu, ať už zrovna něco vyhledáváš, jen tak surfuješ, nebo posíláš e-maily.\n\nAby naše ochrana soukromí fungovala, nemusíš znát technické detaily ani řešit žádná složitá nastavení. Stačí přejít na prohlížeč DuckDuckGo ve všech zařízeních a dostaneš soukromí pro všechny své aktivity.\n\nJestli chceš nahlédnout pod pokličku, víc informací o tom, jak ochrana soukromí DuckDuckGo funguje, najdeš v naší [nápovědě](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/)."; /* Report a Broken Site screen confirmation button */ "siteFeedback.buttonText" = "Odešlete zprávu"; @@ -1246,6 +1627,9 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Které webové stránky jsou poškozené?"; +/* No comment provided by engineer. */ +"sync.remove-device.message" = "„%@“ už nebude mít přístup k synchronizovaným datům."; + /* Accessibility label on remove button */ "tab.close.home" = "Zavřít kartu Domů"; @@ -1318,6 +1702,57 @@ /* Voice-search footer note with on-device privacy warning */ "voiceSearch.footer.note" = "Zvuk se zpracovává na zařízení. Nikde ho neukládáme a s nikým ho nesdílíme. Ani DuckDuckGo se k němu nedostane."; +/* Title for the button to enable push notifications in system settings */ +"waitlist.allow-notifications" = "Povolit oznámení"; + +/* Body text for the waitlist notification */ +"waitlist.available.notification.body" = "Otevřít pozvánku"; + +/* Title for the copy action */ +"waitlist.copy" = "Kopírovat"; + +/* Label text for the invite code */ +"waitlist.invite-code" = "Zvací kód"; + +/* Step title on the invite screen */ +"waitlist.invite-screen.step.title" = "Krok %d"; + +/* Title for the invite code screen */ +"waitlist.invite-screen.youre-invited" = "Zveme tě!"; + +/* Title for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.join" = "Připojte se k soukromému pořadníku"; + +/* Temporary status text for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.joining" = "Zapiš se do pořadníku..."; + +/* Title for the Share Link button */ +"waitlist.join-waitlist-screen.share-link" = "Sdílet odkaz"; + +/* Notification text for the waitlist */ +"waitlist.joined.no-notification.get-notification" = "UPOZORNĚTE MĚ"; + +/* Title for the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.get-notification-confirmation-title" = "Chceš dostávat upozornění, když jsi na řadě?"; + +/* Cancel button in the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.no-thanks" = "Ne, děkuji"; + +/* Text used for the Notifications Disabled state */ +"waitlist.notification.disabled" = "Můžeme ti dát vědět, až budeš na řadě, ale aktuálně jsou oznámení pro DuckDuckGo vypnutá."; + +/* Privacy disclaimer for the Waitlist feature */ +"waitlist.privacy-disclaimer" = "Kvůli zařazení do pořadníku není nutné sdílet žádné osobní údaje. Tvé místo se určuje podle časového razítka uloženého jen ve tvém zařízení tak, abychom tě mohli upozornit, až budeš na řadě."; + +/* Title for the queue screen */ +"waitlist.queue-screen.on-the-list" = "Jsi v pořadníku!"; + +/* Title for the settings subtitle */ +"waitlist.settings.download-available" = "Ke stažení"; + +/* Title for the share sheet entry */ +"waitlist.share-sheet.title" = "Zveme tě!"; + /* Confirmation message */ "web.url.remove.favorite.done" = "Oblíbená položka odstraněna"; @@ -1342,3 +1777,51 @@ /* Alert title explaining the message is shown by a website */ "webJSAlert.website-message.format" = "Zpráva od %@:"; +/* No comment provided by engineer. */ +"Welcome to the Duck Side!" = "Vítejte na Kačeří straně!"; + +/* Title for the Windows waitlist notification */ +"windows-waitlist.available.notification.title" = "Vyzkoušej DuckDuckGo pro Windows!"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-1.description" = "Aplikaci si do počítače s Windows stáhneš z téhle URL:"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-2.description" = "Otevři si ve Stažených souborech instalační program DuckDuckGo, klikni na Instalovat a pak zadej kód z pozvánky."; + +/* Subtitle for the Windows Waitlist Invite screen */ +"windows-waitlist.invite-screen.subtitle" = "Chceš mít ve Windows DuckDuckGo?"; + +/* Title for the Windows waitlist button redirecting to Mac waitlist */ +"windows-waitlist.join-waitlist-screen.mac-waitlist" = "Hledáš verzi pro Mac?"; + +/* Title for the Join Windows Waitlist screen */ +"windows-waitlist.join-waitlist-screen.try-duckduckgo-for-windows" = "Získej předběžný přístup a vyzkoušej DuckDuckGo pro Windows!"; + +/* Message for the alert to confirm enabling notifications */ +"windows-waitlist.joined.no-notification.get-notification-confirmation-message" = "Dáme ti vědět, až bude tvoje kopie DuckDuckGo pro Windows připravená ke stažení. "; + +/* Label text for the Joined Waitlist state with notifications declined */ +"windows-waitlist.joined.notifications-declined" = "Sem dostaneš pozvánku k vyzkoušení DuckDuckGo pro Windows. Zkus se sem brzy podívat znovu. Nebo ti můžeme dát vědět, až na tebe vyjde řada."; + +/* Label text for the Joined Waitlist state with notifications enabled */ +"windows-waitlist.joined.notifications-enabled" = "Dáme ti vědět, až bude tvoje kopie DuckDuckGo pro Windows připravená ke stažení."; + +/* Title for the settings subtitle */ +"windows-waitlist.settings.browse-privately" = "Zajisti si soukromí na webu – v naší aplikaci pro Windows"; + +/* Message used when sharing to iMessage. Parameter is an eight digit invite code. */ +"windows-waitlist.share-sheet.invite-code-message" = "Zveme tě!\n\nChceš mít na Windows DuckDuckGo?\n\n1. krok\nPro stažení přejdi na počítači s Windows sem:\nhttps://duckduckgo.com/windows\n\n2. krok\nOtevři si ve Stažených souborech instalační program DuckDuckGo, klikni na Instalovat a pak zadej kód z pozvánky.\n\nZvací kód: %@"; + +/* Message used when sharing to iMessage */ +"windows-waitlist.share-sheet.message" = "Cítíš se na to začít si ve Windows prohlížet web v naprostém soukromí?\n\nPro stažení přejdi na počítači s Windows sem:\nhttps://duckduckgo.com/windows"; + +/* Summary text for the Windows browser waitlist */ +"windows-waitlist.summary" = "DuckDuckGo pro Windows nabízí všechno, co potřebuješ k většímu soukromí na webu – soukromé vyhledávání, blokování trackerů, vynucené šifrování, blokování vyskakovacích oken pro souhlas se soubory cookie a další ochranné funkce, které jsou nejlepší ve své třídě."; + +/* Title for the Windows Waitlist feature */ +"windows-waitlist.title" = "Aplikace DuckDuckGo pro Windows"; + +/* Title for the Windows browser download link page */ +"windows-waitlist.waitlist-download-screen.try-duckduckgo-for-windows" = "Pořiď si DuckDuckGo pro Windows!"; + diff --git a/DuckDuckGo/cs.lproj/Settings.strings b/DuckDuckGo/cs.lproj/Settings.strings index 9430d87eaa..bb12c152b4 100644 --- a/DuckDuckGo/cs.lproj/Settings.strings +++ b/DuckDuckGo/cs.lproj/Settings.strings @@ -37,6 +37,9 @@ /* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ "9Ko-0g-T3h.text" = "Velikost textu"; +/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ +"9kt-6R-XiZ.text" = "Název"; + /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Spuštění aplikace"; @@ -73,6 +76,12 @@ /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Globální kontrola ochrany osobních údajů (GPC)"; +/* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ +"CxT-QK-iVn.title" = "Přidat"; + +/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ +"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; + /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Ukončení aplikace, neaktivní po dobu 15 minut"; @@ -151,6 +160,9 @@ /* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ "HLr-R8-xxF.text" = "Náhledy dlouhého stisknutí"; +/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ +"hoT-Nu-KXP.text" = "Zajisti si soukromí na webu – v naší aplikaci pro Windows"; + /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Ochrana osobních údajů povolena pro všechny weby"; @@ -163,6 +175,12 @@ /* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ "m23-t6-9cb.text" = "Podělte se o zpětnou vazbu"; +/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ +"mLn-1x-Fl5.title" = "Pozice adresního řádku"; + +/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ +"mqV-pf-NZ1.text" = "Pozice adresního řádku"; + /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Nechat správu oken pro souhlas s cookies na DuckDuckGo"; @@ -175,15 +193,27 @@ /* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ "oM7-1o-9oY.text" = "O DuckDuckGo"; +/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ +"opn-JO-idF.text" = "Nahoru"; + /* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ "OxE-MQ-uJk.headerTitle" = "Další od DuckDuckGo"; +/* Class = "UILabel"; text = "Sync"; ObjectID = "oXN-ez-gct"; */ +"oXN-ez-gct.text" = "Synchronizace"; + /* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ "P0F-ts-ekd.text" = "Anonymní brouzdání po internetu s naší aplikací pro Mac "; +/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ +"qah-gb-udB.text" = "Network Protection"; + /* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ "qeN-SV-zy7.text" = "Štítek"; +/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ +"RQ8-H1-Ez1.text" = "DuckDuckGo pro Windows"; + /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Velikost textu"; @@ -232,8 +262,8 @@ /* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ "yvj-LL-MiR.text" = "Přidat aplikaci do docku"; -/* Class = "UILabel"; text = "DuckDuckGo Desktop App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "DuckDuckGo Desktop"; +/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ +"Yz9-17-qnn.text" = "DuckDuckGo pro Mac"; /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Nová karta"; diff --git a/DuckDuckGo/da.lproj/Feedback.strings b/DuckDuckGo/da.lproj/Feedback.strings index 03d9c23ca6..8d15d853f7 100644 --- a/DuckDuckGo/da.lproj/Feedback.strings +++ b/DuckDuckGo/da.lproj/Feedback.strings @@ -28,9 +28,6 @@ /* Class = "UIBarButtonItem"; title = "Back"; ObjectID = "JF8-Ow-zXL"; */ "JF8-Ow-zXL.title" = "Tilbage"; -/* Class = "UIBarButtonItem"; title = "Submit"; ObjectID = "jr6-Al-4fr"; */ -"jr6-Al-4fr.title" = "Indsend"; - /* Class = "UIButton"; normalTitle = "Submit"; ObjectID = "lsB-Kq-U8p"; */ "lsB-Kq-U8p.normalTitle" = "Indsend"; @@ -58,9 +55,6 @@ /* Class = "UINavigationItem"; title = "Report Broken Site"; ObjectID = "WjT-HM-yim"; */ "WjT-HM-yim.title" = "Rapporter ødelagt websted"; -/* Class = "UILabel"; text = "Broken Site Reason"; ObjectID = "Wx3-Vb-ItS"; */ -"Wx3-Vb-ItS.text" = "Årsag til ødelagt sted"; - /* Class = "UINavigationItem"; title = "Share Feedback"; ObjectID = "Xb0-Zu-opM"; */ "Xb0-Zu-opM.title" = "Del feedback"; diff --git a/DuckDuckGo/da.lproj/Localizable.strings b/DuckDuckGo/da.lproj/Localizable.strings index 766d5618ee..a0e9bb2c37 100644 --- a/DuckDuckGo/da.lproj/Localizable.strings +++ b/DuckDuckGo/da.lproj/Localizable.strings @@ -106,6 +106,12 @@ /* Share action */ "action.title.share" = "Del"; +/* Settings label for bottom position for the address bar */ +"address.bar.bottom" = "Nederst"; + +/* Settings label for top position for the address bar */ +"address.bar.top" = "Top"; + /* No comment provided by engineer. */ "addWidget.button" = "Tilføj widget"; @@ -523,6 +529,18 @@ /* Toast message when a login item without a title is deleted */ "autofill.logins.list.login-deleted-message-no-title" = "Login slettet"; +/* Title for a button that allows a user to reset their list of never saved sites */ +"autofill.logins.list.never.saved" = "Nulstil ekskluderede websteder"; + +/* Cancel button for resetting list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.cancel" = "Annullér"; + +/* Confirm button to reset list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.confirm" = "Nulstil ekskluderede websteder"; + +/* Alert title */ +"autofill.logins.list.never.saved.reset.action.title" = "Hvis du nulstiller ekskluderede websteder, vil du blive bedt om at gemme dit login, næste gang du logger ind på en af disse sider."; + /* Placeholder for search field on autofill login listing */ "autofill.logins.list.search-placeholder" = "Søg logins"; @@ -557,7 +575,7 @@ "autofill.logins.prompt.password.button.title" = "Adgangskode til %@"; /* Title for autofill login prompt */ -"autofill.logins.prompt.title" = "Vil du bruge et gemt login?"; +"autofill.logins.prompt.title" = "Brug en gemt adgangskode?"; /* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ "autofill.logins.search.no-results.subtitle" = "for '%@'"; @@ -607,27 +625,24 @@ /* Title for the alert dialog telling the user an updated username is no longer a private email address */ "autofill.removed.duck.address.title" = "Privat Duck Address-brugernavn blev fjernet"; +/* CTA displayed on modal asking if the user never wants to be prompted to save a login for this website agin */ +"autofill.save-login.never-prompt.CTA" = "Spørg aldrig på dette websted"; + /* Message displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.message" = "Logins gemmes sikkert på din enhed i menuen Logins."; +"autofill.save-login.new-user.message" = "Adgangskoder gemmes sikkert på din enhed i menuen Logins."; /* Title displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.title" = "Skal DuckDuckGo gemme dit login?"; +"autofill.save-login.new-user.title" = "Skal DuckDuckGo gemme din adgangskode?"; /* Cancel CTA displayed on modal asking for the user to save the login */ "autofill.save-login.not-now.CTA" = "Gem ikke"; -/* Confirm CTA displayed on modal asking for the user to save the login */ -"autofill.save-login.save.CTA" = "Gem login"; - /* Title displayed on modal asking for the user to save the login */ "autofill.save-login.title" = "Gem login?"; /* Confirm CTA displayed on modal asking for the user to save the password */ "autofill.save-password.save.CTA" = "Gem adgangskode"; -/* Title displayed on modal asking for the user to save the password */ -"autofill.save-password.title" = "Gem adgangskode?"; - /* Accessibility title for a Show Password button displaying actial password instead of ***** */ "autofill.show-password" = "Vis adgangskode"; @@ -635,7 +650,7 @@ "autofill.signin.to.manage" = "%@ for at administrere dine Duck-adresser på denne enhed."; /* Message displayed on modal asking for the user to update the password */ -"autofill.update-password.message" = "DuckDuckGo opdaterer dette gemte login på din enhed."; +"autofill.update-password.message" = "DuckDuckGo opdaterer denne gemte adgangskode på din enhed."; /* Confirm CTA displayed on modal asking for the user to update the password */ "autofill.update-password.save.CTA" = "Opdater adgangskode"; @@ -1018,6 +1033,33 @@ /* No comment provided by engineer. */ "Error" = "Fejl"; +/* Button title to Sign In */ +"error.email-protection-sign-in.action" = "Log ind"; + +/* Alert message */ +"error.email-protection-sign-in.body" = "Beklager, log på igen for at genaktivere Email Protection i denne browser."; + +/* Alert title */ +"error.email-protection-sign-in.title" = "Fejl i Email Protection"; + +/* Button title to open device settings */ +"error.insufficient-disk-space.action" = "Åbn Indstillinger"; + +/* Alert message */ +"error.insufficient-disk-space.body" = "Det ser ud til, at din enhed er løbet tør for lagerplads. Frigør plads for at fortsætte."; + +/* Alert title */ +"error.insufficient-disk-space.title" = "Ikke nok lagerplads"; + +/* Button title that is shutting down the app */ +"error.preemptive-crash.action" = "Luk app"; + +/* Alert message */ +"error.preemptive-crash.body" = "Det ser ud til, at der er et problem med appen, og at den skal lukkes. Åbn den igen for at fortsætte."; + +/* Alert title */ +"error.preemptive-crash.title" = "App-problem opdaget"; + /* Generic error message on a dialog for when the cause is not known. */ "error.unknown.try.again" = "An unknown error has occurred"; @@ -1564,8 +1606,8 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Favoritter"; -/* No comment provided by engineer. */ -"settings.about.text" = "Hos DuckDuckGo sætter vi den nye standard for tillid online.\n\nDuckDuckGo Privacy Browser giver dig alle de nødvendige værktøjer til at værne over privatlivets fred, så du kan beskytte dig selv, når du søger og surfer på nettet, herunder tracker-blokering, smartere kryptering og DuckDuckGo-privat søgning.\n\nInternettet bør trods alt ikke føles så skummelt, og det bør være lige så enkelt at få det privatliv, du fortjener online, som at lukke dine persienner ned."; +/* about page */ +"settings.about.text" = "DuckDuckGo er en uafhængig virksomhed, grundlagt i 2008, der beskytter privatlivet på internettet for alle, der er trætte af at blive sporet online og ønsker en nem løsning. Vi er bevis på, at du kan få ægte beskyttelse af privatlivet online uden at gå på kompromis.\n\nDuckDuckGo-browseren kommer med de funktioner, du forventer af en standardbrowser, som bogmærker, faner, adgangskoder og meget mere, plus over [et dusin kraftfulde beskyttelser af privatlivet](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/), som ikke tilbydes i de fleste populære browsere som standard. Dette unikke og omfattende sæt af beskyttelsesfunktioner hjælper med at beskytte dine onlineaktiviteter, fra søgning til browsing, e-mailing og meget mere.\n\nVores beskyttelse af privatlivet fungerer, uden at du behøver at vide noget om de tekniske detaljer eller håndtere komplicerede indstillinger. Det eneste, du skal gøre, er at skifte din browser til DuckDuckGo på alle dine enheder, så får du privatliv som standard.\n\nMen hvis du gerne vil have et kig under motorhjelmen, kan du finde flere oplysninger om, hvordan DuckDuckGos beskyttelse af privatlivet fungerer, på vores [hjælpesider](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/)."; /* Report a Broken Site screen confirmation button */ "siteFeedback.buttonText" = "Indsend rapport"; @@ -1735,6 +1777,9 @@ /* Alert title explaining the message is shown by a website */ "webJSAlert.website-message.format" = "En besked fra %@:"; +/* No comment provided by engineer. */ +"Welcome to the Duck Side!" = "Velkommen til Duck siden"; + /* Title for the Windows waitlist notification */ "windows-waitlist.available.notification.title" = "Prøv DuckDuckGo til Windows!"; diff --git a/DuckDuckGo/da.lproj/Settings.strings b/DuckDuckGo/da.lproj/Settings.strings index b4c9fc06e4..b0c910808b 100644 --- a/DuckDuckGo/da.lproj/Settings.strings +++ b/DuckDuckGo/da.lproj/Settings.strings @@ -37,6 +37,9 @@ /* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ "9Ko-0g-T3h.text" = "Tekststørrelse"; +/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ +"9kt-6R-XiZ.text" = "Titel"; + /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "App-start"; @@ -73,6 +76,12 @@ /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Global Privacy Control (GPC)"; +/* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ +"CxT-QK-iVn.title" = "Tilføj"; + +/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ +"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; + /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "App-afslutning, inaktiv i 15 minutter"; @@ -151,6 +160,9 @@ /* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ "HLr-R8-xxF.text" = "Eksempler med lang tryk"; +/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ +"hoT-Nu-KXP.text" = "Browse privat med vores app til Windows"; + /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Databeskyttelse aktiveret for alle websteder"; @@ -163,6 +175,12 @@ /* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ "m23-t6-9cb.text" = "Del feedback"; +/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ +"mLn-1x-Fl5.title" = "Placering af adresselinje"; + +/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ +"mqV-pf-NZ1.text" = "Placering af adresselinje"; + /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Lad DuckDuckGo administrere pop op-vinduer med samtykke til brug af cookies"; @@ -175,15 +193,27 @@ /* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ "oM7-1o-9oY.text" = "Om DuckDuckGo"; +/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ +"opn-JO-idF.text" = "Top"; + /* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ "OxE-MQ-uJk.headerTitle" = "Mere fra DuckDuckGo"; +/* Class = "UILabel"; text = "Sync"; ObjectID = "oXN-ez-gct"; */ +"oXN-ez-gct.text" = "Synkronisering"; + /* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ "P0F-ts-ekd.text" = "Browse privat med vores app til Mac "; +/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ +"qah-gb-udB.text" = "Network Protection"; + /* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ "qeN-SV-zy7.text" = "Etiket"; +/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ +"RQ8-H1-Ez1.text" = "DuckDuckGo-app til Windows"; + /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Tekststørrelse"; @@ -232,8 +262,8 @@ /* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ "yvj-LL-MiR.text" = "Føj appen til din dock"; -/* Class = "UILabel"; text = "DuckDuckGo Desktop App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "DuckDuckGo skrivebordsapp"; +/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ +"Yz9-17-qnn.text" = "DuckDuckGo-app til Mac"; /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Ny fane"; diff --git a/DuckDuckGo/de.lproj/Feedback.strings b/DuckDuckGo/de.lproj/Feedback.strings index b10f3976b5..5630ec0bc1 100644 --- a/DuckDuckGo/de.lproj/Feedback.strings +++ b/DuckDuckGo/de.lproj/Feedback.strings @@ -28,9 +28,6 @@ /* Class = "UIBarButtonItem"; title = "Back"; ObjectID = "JF8-Ow-zXL"; */ "JF8-Ow-zXL.title" = "Zurück"; -/* Class = "UIBarButtonItem"; title = "Submit"; ObjectID = "jr6-Al-4fr"; */ -"jr6-Al-4fr.title" = "Abschicken"; - /* Class = "UIButton"; normalTitle = "Submit"; ObjectID = "lsB-Kq-U8p"; */ "lsB-Kq-U8p.normalTitle" = "Abschicken"; @@ -58,9 +55,6 @@ /* Class = "UINavigationItem"; title = "Report Broken Site"; ObjectID = "WjT-HM-yim"; */ "WjT-HM-yim.title" = "Fehlerhafte Website melden"; -/* Class = "UILabel"; text = "Broken Site Reason"; ObjectID = "Wx3-Vb-ItS"; */ -"Wx3-Vb-ItS.text" = "Grund für fehlerhafte Website"; - /* Class = "UINavigationItem"; title = "Share Feedback"; ObjectID = "Xb0-Zu-opM"; */ "Xb0-Zu-opM.title" = "Feedback teilen"; diff --git a/DuckDuckGo/de.lproj/Localizable.strings b/DuckDuckGo/de.lproj/Localizable.strings index fbcbc7b098..6f59a19e6e 100644 --- a/DuckDuckGo/de.lproj/Localizable.strings +++ b/DuckDuckGo/de.lproj/Localizable.strings @@ -1,6 +1,9 @@ /* No comment provided by engineer. */ "%@" = "%@"; +/* No comment provided by engineer. */ +"%@ [%@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)" = "%1$@ [%2$@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)"; + /* Buton label for Edit action */ "action.generic.edit" = "Bearbeiten"; @@ -103,6 +106,12 @@ /* Share action */ "action.title.share" = "Teilen"; +/* Settings label for bottom position for the address bar */ +"address.bar.bottom" = "Unten"; + +/* Settings label for top position for the address bar */ +"address.bar.top" = "Nach oben"; + /* No comment provided by engineer. */ "addWidget.button" = "Widget hinzufügen"; @@ -142,6 +151,225 @@ /* Shown on authentication screen */ "app.authentication.unlock" = "DuckDuckGo entsperren."; +/* First part of about page content (note the trailing space) */ +"appTP.about.content1" = "You’ve probably heard about companies like Google and Facebook tracking you behind the scenes on third-party websites. But did you know they also track your personal information through apps on your device?\n\nIn 2022, DuckDuckGo found that "; + +/* Second part of about page content (note the trailing space) */ +"appTP.about.content2" = "over 85% of free iOS apps tested contained hidden trackers from other companies."; + +/* Third part of about page content (note the leading space) */ +"appTP.about.content3" = " Of the 395 apps tested, 60% sent data to Google. This happens even while you’re not using your device.\n\nTrackers in apps may have access to a lot more information than their website tracker cousins, such as your location down to which floor of a building you're on, how often you play games while at work, and when and how long you sleep each day. Even if you haven’t given apps explicit permission to collect data, they can still take it without your knowledge.\n\nTracking networks like Facebook and Google use these little pieces of information to build a digital profile about you. With it, tracking networks can manipulate what you see online and allow advertisers to bid on access to you based on your data.\n\nTrackers in apps is a BIG problem for privacy. But DuckDuckGo has a solution that can help.\n\nWhen enabled in the DuckDuckGo Privacy Browser app, App Tracking Protection blocks many trackers in other apps, not just the trackers we find on websites when you browse. These dual layers of protection reduce what companies know about you overall, so you can use your apps with more peace of mind, knowing you’re more protected."; + +/* Navigation Title for AppTP about page */ +"appTP.about.navTitle" = "About App Trackers"; + +/* Title for AppTP about page */ +"appTP.about.title" = "What Are App Trackers?"; + +/* Title for 'Report an Issue' button in the activity view. */ +"appTP.activityView.reportIssue" = "Report Issue"; + +/* Text label for switch that turns blocking on or off for a tracker */ +"appTP.blockTrackerText" = "Block this Tracker"; + +/* Detail string describing what AppTP is */ +"appTP.cell.detail" = "Blockiere App-Tracker auf deinem Gerät"; + +/* String indicating AppTP is disabled when viewed from the settings screen */ +"appTP.cell.disabled" = "Deaktiviert"; + +/* String indicating AppTP is enabled when viewed from the settings screen */ +"appTP.cell.enabled" = "Aktiviert"; + +/* Info string informing the user what App Tracking Protection does. */ +"appTP.empty.disabled.info" = "Aktiviere App Tracking Protection, damit wir lästige Tracker in anderen Apps blockieren können."; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.heading" = "We’re blocking hidden trackers"; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.info" = "Come back soon to see a list of all the app trackers we’ve blocked."; + +/* First answer for AppTP FAQ page */ +"appTP.faq.answer1" = "App Tracking Protection blocks app trackers from other companies, like when Facebook tries to track you in a banking app. Companies may still track you in apps they own."; + +/* Second answer for AppTP FAQ page */ +"appTP.faq.answer2" = "Yes! App Tracking Protection works across all apps on your device to block the most common hidden trackers we find trying to collect your personal info."; + +/* Third answer for AppTP FAQ page */ +"appTP.faq.answer3" = "We currently only block the most common trackers that we find on iOS. This helps us to comprehensively test App Tracking Protection and lower frequency of app breakage, while blocking up to 70% of all tracking requests."; + +/* Fourth answer for AppTP FAQ page */ +"appTP.faq.answer4" = "You’ll be asked to set up a virtual private network (VPN) connection, but you don't need to install a VPN app for App Tracking Protection to work.\n\nThis permission, which works only on your device, allows App Tracking Protection to monitor network traffic so that it can block known trackers."; + +/* Fifth answer for AppTP FAQ page */ +"appTP.faq.answer5" = "You can use App Tracking Protection at the same time as using an IKEv2 protocol VPN app on an iOS device. You won’t be able to use App Tracking Protection on an iOS device if you’re using a VPN app that uses a different type of protocol, like WireGuard or OpenVPN type VPNs."; + +/* Sixth answer for AppTP FAQ page */ +"appTP.faq.answer6" = "A VPN sends your data from the device to its own server, where it secures and anonymizes your data from prying eyes. However, this allows the VPN company to see your network traffic.\n\nApp Tracking Protection is different. Instead of sending your data to a VPN server, App Tracking Protection works only on your device, sitting between your apps and the servers they talk to.\n\nWhenever App Tracking Protection recognizes a known tracker, it blocks the tracker from sending personal information (such as your IP address, activity, and device details) off your device. All other traffic reaches its destination, so your apps work normally."; + +/* Seventh answer for AppTP FAQ page */ +"appTP.faq.answer7" = "App Tracking Protection works only on your device and doesn’t send your data off your device to DuckDuckGo. We don’t collect or store any data from your apps."; + +/* First question for AppTP FAQ page */ +"appTP.faq.question1" = "How does App Tracking Protection work?"; + +/* Second question for AppTP FAQ page */ +"appTP.faq.question2" = "Does App Tracking Protection block trackers in all apps on my device?"; + +/* Third question for AppTP FAQ page */ +"appTP.faq.question3" = "Does App Tracking Protection block all app trackers?"; + +/* Fourth question for AppTP FAQ page */ +"appTP.faq.question4" = "Why does App Tracking Protection use a VPN connection?"; + +/* Fifth question for AppTP FAQ page */ +"appTP.faq.question5" = "Will App Tracking Protection work if I also use a VPN app?"; + +/* Sixth question for AppTP FAQ page */ +"appTP.faq.question6" = "How is App Tracking Protection different from a VPN?"; + +/* Seventh question for AppTP FAQ page */ +"appTP.faq.question7" = "Is my data private?"; + +/* Title for AppTP FAQ page */ +"appTP.faq.title" = "App Tracking Protection FAQ"; + +/* Do not translate. StringsDict entry -- Count part of string 'App Tracking Protection blocked x tracking attempts today' */ +"appTP.home.blockedCount" = "appTP.home.blockedCount"; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the trailing space) */ +"appTP.home.blockedPrefix" = "App Tracking Protection hat heute x Tracking-Versuche blockiert. "; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the leading space) */ +"appTP.home.blockedSuffix" = " heute in deinen Apps."; + +/* Prefix of string 'App Tracking Protection disabled. Tap to re-enable.' (note the trailing space) */ +"appTP.home.disabledPrefix" = "App Tracking Protection disabled. "; + +/* Suffix of string 'App Tracking Protection disabled. Tap to re-enable.' */ +"appTP.home.disabledSuffix" = "Tap to continue blocking tracking attempts across your apps."; + +/* Text indicating the tracking event occured 'just now'. Example: Last attempt 'just now' */ +"appTP.justNow" = "Gerade jetzt"; + +/* View to manage trackers for AppTP. Allows the user to turn trackers on or off. */ +"appTP.manageTrackers" = "Tracker verwalten"; + +/* Button title for AppTP onboarding */ +"appTP.onboarding.continueButton" = "Continue"; + +/* Button title for AppTP onboarding to enable AppTP */ +"appTP.onboarding.enableeButton" = "Enable App Tracking Protection"; + +/* Button title for AppTP onboarding to learn more about AppTP */ +"appTP.onboarding.learnMoreButton" = "Learn More"; + +/* First part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info1" = "Over 85% of free iOS apps"; + +/* Second part of info on the first AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page1Info2" = " we’ve tested allow other companies to track your personal information, even when you’re sleeping."; + +/* Third part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info3" = "See who we catch trying to track you in your apps and take back control."; + +/* First part of info on the second AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page2Info1" = "App Tracking Protection "; + +/* Second part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info2" = "detects and blocks app trackers from other companies,"; + +/* Third part of info on the second AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page2Info3" = " like when Google attempts to track you in a health app."; + +/* Fourth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info4" = "It’s free,"; + +/* Fifth part of info on the second AppTP onboarding page (note the leading and trailing space) */ +"appTP.onboarding.page2Info5" = " and you can enjoy your apps as you normally would. Working in the background, it helps "; + +/* Sixth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info6" = "protect you night and day."; + +/* First part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info1" = "App Tracking Protection is not a VPN."; + +/* Second part of info on the third AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page3Info2" = " However, your device will recognize it as one. This is because it uses a local VPN connection to work."; + +/* Third part of info on the third AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page3Info3" = "App Tracking Protection is different. "; + +/* Fourth part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info4" = "It never routes app data through an external server."; + +/* Title for first AppTP onboarding page */ +"appTP.onboarding.title1" = "One easy step for better app privacy!"; + +/* Title for second AppTP onboarding page */ +"appTP.onboarding.title2" = "How does it work?"; + +/* Title for third AppTP onboarding page */ +"appTP.onboarding.title3" = "Who sees your data?"; + +/* Breakage report app name label */ +"appTP.report.appLabel" = "Which app is having issues?"; + +/* Breakage report app name placeholder */ +"appTP.report.appPlaceholder" = "App name"; + +/* Breakage report category label */ +"appTP.report.categoryLabel" = "Was passiert?"; + +/* Breakage report comment label */ +"appTP.report.commentLabel" = "Comments"; + +/* Breakage report comment placeholder */ +"appTP.report.commentPlaceholder" = "Add additional details"; + +/* Breakage report footer explaining what is collected in the breakage report */ +"appTP.report.footer" = "In addition to the details entered into this form, your app issue report will contain:\n• A list of trackers blocked in the last 10 min\n• Whether App Tracking Protection is enabled\n• Aggregate DuckDuckGo app diagnostics"; + +/* Breakage report submit button */ +"appTP.report.submit" = "Abschicken"; + +/* Breakage report form title */ +"appTP.report.title" = "Report Issue"; + +/* Breakage report succcess message */ +"appTP.report.toast" = "Vielen Dank! Feedback wurde gesendet."; + +/* Cancel button for 'Report an Issue' alert. */ +"appTP.reportAlert.cancel" = "Jetzt nicht"; + +/* Confirm button for 'Report an Issue' alert. */ +"appTP.reportAlert.confirm" = "Problem melden"; + +/* Message for 'Report an Issue' alert. */ +"appTP.reportAlert.message" = "Let us know if you disabled App Tracking Protection for this specific tracker because it caused app issues. Your feedback helps us improve!"; + +/* Title for 'Report an Issue' alert. */ +"appTP.reportAlert.title" = "Report Issue?"; + +/* Toast notification diplayed after restoring the blocklist to default settings */ +"appTP.restoreDefaultsToast" = "Default settings restored"; + +/* Button to restore the blocklist to its default state. */ +"appTP.restoreDefualts" = "Standardeinstellungen wiederherstellen"; + +/* Title for the App Tracking Protection feature */ +"appTP.title" = "Schutz gegen App-Tracking"; + +/* Text indicating when the tracker was last allowed. Example: Last attempt allowed (timeString) */ +"appTP.trackerAllowedTimestamp" = "Letzter zugelassener Versuch: %@"; + +/* Text indicating when the tracker was last blocked. Example: Last attempt blocked (timeString) */ +"appTP.trackerBlockedTimestamp" = "Letzter blockierter Versuch: %@"; + +/* Do not translate. StringsDict entry -- Subtitle for tracking attempts in App Tracking Protection Activity View. Example: (count) tracking attempts */ +"appTP.trackingattempts" = "appTP.trackingattempts"; + /* Authentication Alert Sign In Button */ "auth.alert.login.button" = "Anmelden"; @@ -301,6 +529,18 @@ /* Toast message when a login item without a title is deleted */ "autofill.logins.list.login-deleted-message-no-title" = "Anmeldedaten gelöscht"; +/* Title for a button that allows a user to reset their list of never saved sites */ +"autofill.logins.list.never.saved" = "Ausgeschlossene Websites zurücksetzen"; + +/* Cancel button for resetting list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.cancel" = "Abbrechen"; + +/* Confirm button to reset list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.confirm" = "Ausgeschlossene Websites zurücksetzen"; + +/* Alert title */ +"autofill.logins.list.never.saved.reset.action.title" = "Wenn du die ausgeschlossenen Websites zurücksetzt, wirst du aufgefordert, deine Anmeldedaten zu speichern, wenn du dich das nächste Mal auf einer dieser Websites anmeldest."; + /* Placeholder for search field on autofill login listing */ "autofill.logins.list.search-placeholder" = "Anmeldedaten suchen"; @@ -335,7 +575,7 @@ "autofill.logins.prompt.password.button.title" = "Passwort für %@"; /* Title for autofill login prompt */ -"autofill.logins.prompt.title" = "Gespeicherte Anmeldedaten verwenden?"; +"autofill.logins.prompt.title" = "Gespeichertes Passwort verwenden?"; /* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ "autofill.logins.search.no-results.subtitle" = "für „%@“"; @@ -385,27 +625,24 @@ /* Title for the alert dialog telling the user an updated username is no longer a private email address */ "autofill.removed.duck.address.title" = "Benutzername von Private Duck Address wurde entfernt"; +/* CTA displayed on modal asking if the user never wants to be prompted to save a login for this website agin */ +"autofill.save-login.never-prompt.CTA" = "Für diese Website niemals fragen"; + /* Message displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.message" = "Anmeldedaten werden sicher auf deinem Gerät im Anmeldedaten-Menü gespeichert."; +"autofill.save-login.new-user.message" = "Passwörter werden sicher auf deinem Gerät im Anmeldedaten-Menü gespeichert."; /* Title displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.title" = "Möchtest du, dass DuckDuckGo deine Anmeldedaten speichert?"; +"autofill.save-login.new-user.title" = "Möchtest du, dass DuckDuckGo dein Passwort speichert?"; /* Cancel CTA displayed on modal asking for the user to save the login */ "autofill.save-login.not-now.CTA" = "Nicht speichern"; -/* Confirm CTA displayed on modal asking for the user to save the login */ -"autofill.save-login.save.CTA" = "Anmeldedaten speichern"; - /* Title displayed on modal asking for the user to save the login */ "autofill.save-login.title" = "Anmeldedaten speichern?"; /* Confirm CTA displayed on modal asking for the user to save the password */ "autofill.save-password.save.CTA" = "Passwort speichern"; -/* Title displayed on modal asking for the user to save the password */ -"autofill.save-password.title" = "Passwort speichern?"; - /* Accessibility title for a Show Password button displaying actial password instead of ***** */ "autofill.show-password" = "Passwort anzeigen"; @@ -413,7 +650,7 @@ "autofill.signin.to.manage" = "%@, um deine Duck Addresses auf diesem Gerät zu verwalten."; /* Message displayed on modal asking for the user to update the password */ -"autofill.update-password.message" = "DuckDuckGo aktualisiert diese gespeicherten Anmeldedaten auf deinem Gerät."; +"autofill.update-password.message" = "DuckDuckGo aktualisiert dieses gespeicherte Passwort auf deinem Gerät."; /* Confirm CTA displayed on modal asking for the user to update the password */ "autofill.update-password.save.CTA" = "Passwort aktualisieren"; @@ -547,9 +784,21 @@ /* Broken Site Category */ "brokensite.category.videos" = "Video wurde nicht abgespielt"; +/* Broken Site Category Placeholder */ +"brokensite.categoryPlaceholder" = "Wähle dein Problem aus der Liste aus..."; + +/* Broken Site Category Section Title */ +"brokensite.categoryTitle" = "EINE KATEGORIE AUSWÄHLEN"; + +/* Broken Site Comment Placeholder */ +"brokensite.commentPlaceholder" = "Das Teilen weiterer Details kann uns helfen, dieses Problem zu lösen"; + /* Broken Site Section Title */ "brokensite.sectionTitle" = "BESCHREIBE, WAS PASSIERT IST"; +/* No comment provided by engineer. */ +"bucket: %@" = "Bucket: %@"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Letzter Monat"; @@ -781,6 +1030,39 @@ /* Empty search placeholder on bookmarks search */ "empty.search" = "Keine Treffer gefunden"; +/* No comment provided by engineer. */ +"Error" = "Fehler"; + +/* Button title to Sign In */ +"error.email-protection-sign-in.action" = "Anmelden"; + +/* Alert message */ +"error.email-protection-sign-in.body" = "Entschuldigung, bitte melde dich erneut an, um die Funktionen von Email Protection in diesem Browser erneut zu aktivieren."; + +/* Alert title */ +"error.email-protection-sign-in.title" = "Fehler bei Email Protection"; + +/* Button title to open device settings */ +"error.insufficient-disk-space.action" = "Einstellungen öffnen"; + +/* Alert message */ +"error.insufficient-disk-space.body" = "Anscheinend ist auf deinem Gerät kein Speicherplatz mehr vorhanden. Bitte gib Speicherplatz frei, um fortzufahren."; + +/* Alert title */ +"error.insufficient-disk-space.title" = "Nicht genug Speicherplatz"; + +/* Button title that is shutting down the app */ +"error.preemptive-crash.action" = "App schließen"; + +/* Alert message */ +"error.preemptive-crash.body" = "Sieht aus, als gäbe es ein Problem mit der App und sie muss geschlossen werden. Bitte öffne sie erneut, um fortzufahren."; + +/* Alert title */ +"error.preemptive-crash.title" = "App-Problem erkannt"; + +/* Generic error message on a dialog for when the cause is not known. */ +"error.unknown.try.again" = "An unknown error has occurred"; + /* No comment provided by engineer. */ "favorite" = "Favorit"; @@ -1000,6 +1282,18 @@ /* Home tab title */ "homeTab.title" = "Startseite"; +/* OK title for invite screen alert dismissal button */ +"invite.alert.ok.button" = "OK"; + +/* Continue button on an invite dialog */ +"invite.dialog.continue.button" = "Continue"; + +/* Get Started button on an invite dialog */ +"invite.dialog.get.started.button" = "Get Started"; + +/* Message to show after user enters an unrecognized invite code */ +"invite.dialog.unrecognized.code.message" = "We didn’t recognize this Invite Code."; + /* No comment provided by engineer. */ "keyCommandAddBookmark" = "Lesezeichen hinzufügen"; @@ -1066,15 +1360,15 @@ /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "DuckDuckGo für Mac hat die Geschwindigkeit, die du brauchst, die Browserfunktionen, die du erwartest, und ist vollgepackt mit unseren grundlegenden Funktionen für den Datenschutz."; -/* Body text for the macOS waitlist notification */ -"mac-waitlist.available.notification.body" = "Öffne deine Einladung"; - /* Title for the macOS waitlist notification */ "mac-waitlist.available.notification.title" = "DuckDuckGo für Mac ist da!"; /* Title for the copy action */ "mac-waitlist.copy" = "Kopieren"; +/* Description text above the Share Link button */ +"mac-waitlist.join-waitlist-screen.on-your-computer-go-to" = "Öffne auf deinem Windows-Computer die folgende Website:"; + /* Description text above the Share Link button */ "mac-waitlist.join-waitlist-screen.on-your-mac-go-to" = "Rufe auf deinem Mac Folgendes auf:"; @@ -1087,6 +1381,9 @@ /* Disclaimer for the Join Waitlist screen */ "mac-waitlist.join-waitlist-screen.windows" = "In Kürze für Windows verfügbar!"; +/* Title for the macOS waitlist button redirecting to Windows waitlist */ +"mac-waitlist.join-waitlist-screen.windows-waitlist" = "Suchst du nach der Windows-Version?"; + /* Title for the settings subtitle */ "mac-waitlist.settings.browse-privately" = "Privat browsen mit unserer App für Mac"; @@ -1097,7 +1394,7 @@ "mac-waitlist.share-sheet.title" = "DuckDuckGo für Mac"; /* Title for the Mac Waitlist feature */ -"mac-waitlist.title" = "DuckDuckGo-Desktop-App"; +"mac-waitlist.title" = "DuckDuckGo-App für Mac"; /* No comment provided by engineer. */ "menu.button.hint" = "Browsing-Optionen"; @@ -1114,9 +1411,81 @@ /* Edit button */ "navigation.title.edit" = "Bearbeiten"; +/* String indicating NetP is connected when viewed from the settings screen */ +"netP.cell.connected" = "Connected"; + +/* String indicating NetP is disconnected when viewed from the settings screen */ +"netP.cell.disconnected" = "Not connected"; + +/* Title for the Network Protection feature */ +"netP.title" = "Network Protection"; + +/* Message for the network protection invite dialog */ +"network.protection.invite.dialog.message" = "Enter your invite code to get started."; + +/* Title for the network protection invite screen */ +"network.protection.invite.dialog.title" = "You're invited to try Network Protection"; + +/* Prompt for the network protection invite code text field */ +"network.protection.invite.field.prompt" = "Invite Code"; + +/* Message explaining that netP is invite only */ +"network.protection.invite.only.message" = "DuckDuckGo Network Protection is currently invite-only."; + +/* Message for the network protection invite success view */ +"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; + +/* Title for the network protection invite success view */ +"network.protection.invite.success.title" = "Success! You’re in."; + +/* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ +"network.protection.status.connected.format" = "Connected - %@"; + +/* The label for the NetP VPN when connecting */ +"network.protection.status.connecting" = "Connecting..."; + +/* The label for the NetP VPN when disconnected */ +"network.protection.status.disconnected" = "Not connected"; + +/* The label for the NetP VPN when disconnecting */ +"network.protection.status.disconnecting" = "Disconnecting..."; + +/* Message label text for the netP status view */ +"network.protection.status.header.message" = "DuckDuckGo's VPN secures all of your device's Internet traffic anytime, anywhere."; + +/* Header title label text for the status view when netP is disconnected */ +"network.protection.status.header.title.off" = "Network Protection is Off"; + +/* Header title label text for the status view when netP is connected */ +"network.protection.status.header.title.on" = "Network Protection is On"; + +/* The status view 'Share Feedback' button which is shown inline on the status view after the \(netPInviteOnlyMessage) text */ +"network.protection.status.menu.share.feedback" = "Share Feedback"; + +/* Connection details label shown in NetworkProtection's status view. */ +"network.protection.status.view.connection.details" = "Connection Details"; + +/* Generic connection failed error message shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.message" = "Please try again later."; + +/* Generic connection failed error title shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; + +/* IP Address label shown in NetworkProtection's status view. */ +"network.protection.status.view.ip.address" = "IP Address"; + +/* Location label shown in NetworkProtection's status view. */ +"network.protection.status.view.location" = "Location"; + +/* Title label text for the status view when netP is disconnected */ +"network.protection.status.view.title" = "Network Protection"; + /* Do not translate - stringsdict entry */ "number.of.tabs" = "number.of.tabs"; +/* No comment provided by engineer. */ +"Ok" = "OK"; + /* Text displayed on notification appearing in the address bar when the browser dismissed the cookie popup automatically rejecting it */ "omnibar.notification.cookies-managed" = "Cookies verwaltet"; @@ -1156,6 +1525,18 @@ /* ’Link’ is link on a website */ "open.externally.failed" = "Die zum Öffnen dieses Links erforderliche App wurde nicht gefunden"; +/* No comment provided by engineer. */ +"Please enter which app on your device is broken." = "Bitte gib an, welche App auf deinem Gerät nicht funktioniert."; + +/* Activate button */ +"pm.activate" = "Reactivate"; + +/* Cancel button */ +"pm.cancel" = "Cancel"; + +/* Deactivate button */ +"pm.deactivate" = "Deactivate"; + /* No comment provided by engineer. */ "preserveLogins.domain.list.footer" = "Websites sorgen mit Cookies dafür, dass du angemeldet bleibst. Wenn du eine Website feuerfest machst, werden Cookies nicht gelöscht und du bleibst angemeldet – auch nach Verwendung der Schaltfläche „Feuer“. Wir blockieren weiterhin auf feuerfest gemachten Websites gefundene Tracker von Dritten."; @@ -1225,8 +1606,8 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Favoriten"; -/* No comment provided by engineer. */ -"settings.about.text" = "Wir bei DuckDuckGo setzen den neuen Vertrauensstandard im Internet.\n\nDer DuckDuckGo Privacy Browser bietet dir alles Grundlegende zum Datenschutz, um dich beim Suchen und Browsen im Internet zu schützen, u. a. eine Tracker-Blockade, eine klügere Verschlüsselung und die private DuckDuckGo-Suche.\n\nSchließlich muss das Internet kein gruseliger Ort sein. Die Online-Privatsphäre zu bekommen, die du verdienst, sollte so einfach sein, wie die Jalousien herunterzulassen."; +/* about page */ +"settings.about.text" = "DuckDuckGo ist die unabhängige Firma für Internet-Datenschutz, die 2008 für alle gegründet wurde, die es leid sind, online getrackt zu werden, und eine einfache Lösung suchen. Wir sind der Beweis, dass du echten Online-Datenschutz ohne Kompromisse erhalten kannst.\n\nDer DuckDuckGo-Browser bietet die Funktionen, die von einem alltagstauglichen Browser erwartet werden, wie Lesezeichen, Tabs, Passwörter und mehr, sowie über [ein Dutzend leistungsstarke Datenschutzmaßnahmen](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/), die in den meisten gängigen Browsern nicht standardmäßig angeboten werden. Dieser einzigartige, umfassende Datenschutz hilft dir, deine Online-Aktivitäten zu schützen – von der Suche über das Browsen bis hin zu E-Mails und vielem mehr.\n\nUnser Datenschutz funktioniert, ohne dass du etwas über die technischen Details wissen oder dich mit komplizierten Einstellungen auseinandersetzen musst. Du musst lediglich den Browser auf allen deinen Geräten auf DuckDuckGo umstellen und schon erhältst du echte Privatsphäre als Standard.\n\nWenn du jedoch einen Blick unter die Haube werfen möchtest, findest du auf unseren [Hilfeseiten](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/) weitere Informationen darüber, wie der Datenschutz bei DuckDuckGo funktioniert."; /* Report a Broken Site screen confirmation button */ "siteFeedback.buttonText" = "Bericht senden"; @@ -1246,6 +1627,9 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Welche Website ist fehlerhaft?"; +/* No comment provided by engineer. */ +"sync.remove-device.message" = "„%@“ kann nicht mehr auf deine synchronisierten Daten zugreifen."; + /* Accessibility label on remove button */ "tab.close.home" = "Startseiten-Tab schließen"; @@ -1318,6 +1702,57 @@ /* Voice-search footer note with on-device privacy warning */ "voiceSearch.footer.note" = "Audio wird auf dem Gerät verarbeitet. Es wird weder gespeichert noch an Dritte weitergegeben, auch nicht an DuckDuckGo."; +/* Title for the button to enable push notifications in system settings */ +"waitlist.allow-notifications" = "Benachrichtigungen zulassen"; + +/* Body text for the waitlist notification */ +"waitlist.available.notification.body" = "Öffne deine Einladung"; + +/* Title for the copy action */ +"waitlist.copy" = "Kopieren"; + +/* Label text for the invite code */ +"waitlist.invite-code" = "Einladungscode"; + +/* Step title on the invite screen */ +"waitlist.invite-screen.step.title" = "Schritt %d"; + +/* Title for the invite code screen */ +"waitlist.invite-screen.youre-invited" = "Du bist eingeladen!"; + +/* Title for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.join" = "Auf die private Warteliste setzen"; + +/* Temporary status text for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.joining" = "Auf Warteliste setzen ..."; + +/* Title for the Share Link button */ +"waitlist.join-waitlist-screen.share-link" = "Link teilen"; + +/* Notification text for the waitlist */ +"waitlist.joined.no-notification.get-notification" = "ICH MÖCHTE BENACHRICHTIGT WERDEN"; + +/* Title for the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.get-notification-confirmation-title" = "Benachrichtigung erhalten, wenn du an der Reihe bist?"; + +/* Cancel button in the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.no-thanks" = "Nein, danke"; + +/* Text used for the Notifications Disabled state */ +"waitlist.notification.disabled" = "Wir können dich benachrichtigen, wenn es soweit ist, aber Benachrichtigungen sind derzeit für DuckDuckGo deaktiviert."; + +/* Privacy disclaimer for the Waitlist feature */ +"waitlist.privacy-disclaimer" = "Um dich in die Warteliste einzutragen, musst du keine persönlichen Daten angeben. Du sicherst deinen Platz mit einem Zeitstempel, der nur auf deinem Gerät vorhanden ist, damit wir dich benachrichtigen können, wenn du an der Reihe bist."; + +/* Title for the queue screen */ +"waitlist.queue-screen.on-the-list" = "Du stehst auf der Liste!"; + +/* Title for the settings subtitle */ +"waitlist.settings.download-available" = "Download verfügbar"; + +/* Title for the share sheet entry */ +"waitlist.share-sheet.title" = "Du bist eingeladen!"; + /* Confirmation message */ "web.url.remove.favorite.done" = "Favorit wurde entfernt"; @@ -1342,3 +1777,51 @@ /* Alert title explaining the message is shown by a website */ "webJSAlert.website-message.format" = "Eine Nachricht von %@:"; +/* No comment provided by engineer. */ +"Welcome to the Duck Side!" = "Willkommen auf der Duck-len Seite!"; + +/* Title for the Windows waitlist notification */ +"windows-waitlist.available.notification.title" = "Teste DuckDuckGo für Windows!"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-1.description" = "Besuche zum Herunterladen diese URL auf deinem Windows-Gerät:"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-2.description" = "Öffne DuckDuckGo Installer in Downloads, wähle Installieren und gib dann deinen Einladungscode ein."; + +/* Subtitle for the Windows Waitlist Invite screen */ +"windows-waitlist.invite-screen.subtitle" = "Bist du bereit, DuckDuckGo auf Windows zu benutzen?"; + +/* Title for the Windows waitlist button redirecting to Mac waitlist */ +"windows-waitlist.join-waitlist-screen.mac-waitlist" = "Suchst du nach der Mac-Version?"; + +/* Title for the Join Windows Waitlist screen */ +"windows-waitlist.join-waitlist-screen.try-duckduckgo-for-windows" = "Erhalte frühen Zugriff, um DuckDuckGo für Windows auszuprobieren!"; + +/* Message for the alert to confirm enabling notifications */ +"windows-waitlist.joined.no-notification.get-notification-confirmation-message" = "Du erhältst eine Benachrichtigung, sobald du DuckDuckGo für Windows herunterladen kannst. "; + +/* Label text for the Joined Waitlist state with notifications declined */ +"windows-waitlist.joined.notifications-declined" = "Deine Einladung zum Ausprobieren von DuckDuckGo für Windows wird hier angezeigt. Schau bald wieder vorbei, oder wir senden dir eine Benachrichtigung, wenn es soweit ist."; + +/* Label text for the Joined Waitlist state with notifications enabled */ +"windows-waitlist.joined.notifications-enabled" = "Du erhältst eine Benachrichtigung, sobald du DuckDuckGo für Windows herunterladen kannst."; + +/* Title for the settings subtitle */ +"windows-waitlist.settings.browse-privately" = "Privat browsen mit unserer App für Windows"; + +/* Message used when sharing to iMessage. Parameter is an eight digit invite code. */ +"windows-waitlist.share-sheet.invite-code-message" = "Du bist eingeladen!\n\nBist du bereit, DuckDuckGo unter Windows zu benutzen?\n\nSchritt 1\nBesuche zum Herunterladen diese URL auf deinem Windows-Gerät:\nhttps://duckduckgo.com/windows\n\nSchritt 2\nÖffne DuckDuckGo Installer in Downloads, wähle Installieren und gib dann deinen Einladungscode ein.\n\nEinladungscode: %@"; + +/* Message used when sharing to iMessage */ +"windows-waitlist.share-sheet.message" = "Bist du bereit, privat unter Windows zu browsen?\n\nBesuche zum Herunterladen diese URL auf deinem Computer:\nhttps://duckduckgo.com/windows"; + +/* Summary text for the Windows browser waitlist */ +"windows-waitlist.summary" = "DuckDuckGo für Windows bietet alles, was du brauchst, um mit mehr Privatsphäre zu browsen – private Suche, Tracker-Blockade, erzwungene Verschlüsselung und Blockierung von Cookie-Pop-ups sowie weitere erstklassige Schutzmaßnahmen."; + +/* Title for the Windows Waitlist feature */ +"windows-waitlist.title" = "DuckDuckGo-App für Windows"; + +/* Title for the Windows browser download link page */ +"windows-waitlist.waitlist-download-screen.try-duckduckgo-for-windows" = "Hol dir DuckDuckGo für Windows!"; + diff --git a/DuckDuckGo/de.lproj/Settings.strings b/DuckDuckGo/de.lproj/Settings.strings index ed5fe46654..739a017f02 100644 --- a/DuckDuckGo/de.lproj/Settings.strings +++ b/DuckDuckGo/de.lproj/Settings.strings @@ -37,6 +37,9 @@ /* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ "9Ko-0g-T3h.text" = "Textgröße"; +/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ +"9kt-6R-XiZ.text" = "Titel"; + /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Start der App"; @@ -73,6 +76,12 @@ /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Global Privacy Control (GPC)"; +/* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ +"CxT-QK-iVn.title" = "Hinzufügen"; + +/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ +"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; + /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Beim Verlassen der App, 15 Minuten lang inaktiv"; @@ -151,6 +160,9 @@ /* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ "HLr-R8-xxF.text" = "Vorschau durch langes Tippen"; +/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ +"hoT-Nu-KXP.text" = "Privat browsen mit unserer App für Windows"; + /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Datenschutz für alle Websites aktiviert"; @@ -163,6 +175,12 @@ /* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ "m23-t6-9cb.text" = "Feedback teilen"; +/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ +"mLn-1x-Fl5.title" = "Position der Adressleiste"; + +/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ +"mqV-pf-NZ1.text" = "Position der Adressleiste"; + /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Lasse Cookie-Zustimmungs-Popups von DuckDuckGo verwalten"; @@ -175,15 +193,27 @@ /* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ "oM7-1o-9oY.text" = "Über DuckDuckGo"; +/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ +"opn-JO-idF.text" = "Nach oben"; + /* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ "OxE-MQ-uJk.headerTitle" = "Mehr von DuckDuckGo"; +/* Class = "UILabel"; text = "Sync"; ObjectID = "oXN-ez-gct"; */ +"oXN-ez-gct.text" = "Synchronisieren"; + /* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ "P0F-ts-ekd.text" = "Privat browsen mit unserer App für Mac "; +/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ +"qah-gb-udB.text" = "Network Protection"; + /* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ "qeN-SV-zy7.text" = "Label"; +/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ +"RQ8-H1-Ez1.text" = "DuckDuckGo-App für Windows"; + /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Textgröße"; @@ -232,8 +262,8 @@ /* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ "yvj-LL-MiR.text" = "App zum Dock hinzufügen"; -/* Class = "UILabel"; text = "DuckDuckGo Desktop App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "DuckDuckGo-Desktop-App"; +/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ +"Yz9-17-qnn.text" = "DuckDuckGo-App für Mac"; /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Neuer Tab"; diff --git a/DuckDuckGo/el.lproj/Feedback.strings b/DuckDuckGo/el.lproj/Feedback.strings index 8ad916ba5f..8bdc855b3f 100644 --- a/DuckDuckGo/el.lproj/Feedback.strings +++ b/DuckDuckGo/el.lproj/Feedback.strings @@ -28,9 +28,6 @@ /* Class = "UIBarButtonItem"; title = "Back"; ObjectID = "JF8-Ow-zXL"; */ "JF8-Ow-zXL.title" = "Πίσω"; -/* Class = "UIBarButtonItem"; title = "Submit"; ObjectID = "jr6-Al-4fr"; */ -"jr6-Al-4fr.title" = "Υποβολή"; - /* Class = "UIButton"; normalTitle = "Submit"; ObjectID = "lsB-Kq-U8p"; */ "lsB-Kq-U8p.normalTitle" = "Υποβολή"; @@ -58,9 +55,6 @@ /* Class = "UINavigationItem"; title = "Report Broken Site"; ObjectID = "WjT-HM-yim"; */ "WjT-HM-yim.title" = "Αναφορά ιστότοπου που δεν λειτουργεί"; -/* Class = "UILabel"; text = "Broken Site Reason"; ObjectID = "Wx3-Vb-ItS"; */ -"Wx3-Vb-ItS.text" = "Αιτία μη λειτουργίας ιστότοπου"; - /* Class = "UINavigationItem"; title = "Share Feedback"; ObjectID = "Xb0-Zu-opM"; */ "Xb0-Zu-opM.title" = "Κοινοποίηση σχολίου"; diff --git a/DuckDuckGo/el.lproj/Localizable.strings b/DuckDuckGo/el.lproj/Localizable.strings index 36e315af41..ff3591fc75 100644 --- a/DuckDuckGo/el.lproj/Localizable.strings +++ b/DuckDuckGo/el.lproj/Localizable.strings @@ -1,6 +1,9 @@ /* No comment provided by engineer. */ "%@" = "%@"; +/* No comment provided by engineer. */ +"%@ [%@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)" = "%1$@ [%2$@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)"; + /* Buton label for Edit action */ "action.generic.edit" = "Επεξεργασία"; @@ -103,6 +106,12 @@ /* Share action */ "action.title.share" = "Κοινή χρήση"; +/* Settings label for bottom position for the address bar */ +"address.bar.bottom" = "Κάτω μέρος"; + +/* Settings label for top position for the address bar */ +"address.bar.top" = "Κορυφή"; + /* No comment provided by engineer. */ "addWidget.button" = "Προσθήκη γραφικού στοιχείου"; @@ -142,6 +151,225 @@ /* Shown on authentication screen */ "app.authentication.unlock" = "Ξεκλείδωμα DuckDuckGo."; +/* First part of about page content (note the trailing space) */ +"appTP.about.content1" = "You’ve probably heard about companies like Google and Facebook tracking you behind the scenes on third-party websites. But did you know they also track your personal information through apps on your device?\n\nIn 2022, DuckDuckGo found that "; + +/* Second part of about page content (note the trailing space) */ +"appTP.about.content2" = "over 85% of free iOS apps tested contained hidden trackers from other companies."; + +/* Third part of about page content (note the leading space) */ +"appTP.about.content3" = " Of the 395 apps tested, 60% sent data to Google. This happens even while you’re not using your device.\n\nTrackers in apps may have access to a lot more information than their website tracker cousins, such as your location down to which floor of a building you're on, how often you play games while at work, and when and how long you sleep each day. Even if you haven’t given apps explicit permission to collect data, they can still take it without your knowledge.\n\nTracking networks like Facebook and Google use these little pieces of information to build a digital profile about you. With it, tracking networks can manipulate what you see online and allow advertisers to bid on access to you based on your data.\n\nTrackers in apps is a BIG problem for privacy. But DuckDuckGo has a solution that can help.\n\nWhen enabled in the DuckDuckGo Privacy Browser app, App Tracking Protection blocks many trackers in other apps, not just the trackers we find on websites when you browse. These dual layers of protection reduce what companies know about you overall, so you can use your apps with more peace of mind, knowing you’re more protected."; + +/* Navigation Title for AppTP about page */ +"appTP.about.navTitle" = "About App Trackers"; + +/* Title for AppTP about page */ +"appTP.about.title" = "What Are App Trackers?"; + +/* Title for 'Report an Issue' button in the activity view. */ +"appTP.activityView.reportIssue" = "Report Issue"; + +/* Text label for switch that turns blocking on or off for a tracker */ +"appTP.blockTrackerText" = "Block this Tracker"; + +/* Detail string describing what AppTP is */ +"appTP.cell.detail" = "Αποκλείστε εφαρμογές παρακολούθησης στη συσκευή σας"; + +/* String indicating AppTP is disabled when viewed from the settings screen */ +"appTP.cell.disabled" = "Απενεργοποιήθηκε"; + +/* String indicating AppTP is enabled when viewed from the settings screen */ +"appTP.cell.enabled" = "Ενεργοποιήθηκε"; + +/* Info string informing the user what App Tracking Protection does. */ +"appTP.empty.disabled.info" = "Ενεργοποιήστε την App Tracking Protection, ώστε να μπορούμε να αποκλείουμε ενοχλητικές εφαρμογές παρακολούθησης σε άλλες εφαρμογές."; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.heading" = "We’re blocking hidden trackers"; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.info" = "Come back soon to see a list of all the app trackers we’ve blocked."; + +/* First answer for AppTP FAQ page */ +"appTP.faq.answer1" = "App Tracking Protection blocks app trackers from other companies, like when Facebook tries to track you in a banking app. Companies may still track you in apps they own."; + +/* Second answer for AppTP FAQ page */ +"appTP.faq.answer2" = "Yes! App Tracking Protection works across all apps on your device to block the most common hidden trackers we find trying to collect your personal info."; + +/* Third answer for AppTP FAQ page */ +"appTP.faq.answer3" = "We currently only block the most common trackers that we find on iOS. This helps us to comprehensively test App Tracking Protection and lower frequency of app breakage, while blocking up to 70% of all tracking requests."; + +/* Fourth answer for AppTP FAQ page */ +"appTP.faq.answer4" = "You’ll be asked to set up a virtual private network (VPN) connection, but you don't need to install a VPN app for App Tracking Protection to work.\n\nThis permission, which works only on your device, allows App Tracking Protection to monitor network traffic so that it can block known trackers."; + +/* Fifth answer for AppTP FAQ page */ +"appTP.faq.answer5" = "You can use App Tracking Protection at the same time as using an IKEv2 protocol VPN app on an iOS device. You won’t be able to use App Tracking Protection on an iOS device if you’re using a VPN app that uses a different type of protocol, like WireGuard or OpenVPN type VPNs."; + +/* Sixth answer for AppTP FAQ page */ +"appTP.faq.answer6" = "A VPN sends your data from the device to its own server, where it secures and anonymizes your data from prying eyes. However, this allows the VPN company to see your network traffic.\n\nApp Tracking Protection is different. Instead of sending your data to a VPN server, App Tracking Protection works only on your device, sitting between your apps and the servers they talk to.\n\nWhenever App Tracking Protection recognizes a known tracker, it blocks the tracker from sending personal information (such as your IP address, activity, and device details) off your device. All other traffic reaches its destination, so your apps work normally."; + +/* Seventh answer for AppTP FAQ page */ +"appTP.faq.answer7" = "App Tracking Protection works only on your device and doesn’t send your data off your device to DuckDuckGo. We don’t collect or store any data from your apps."; + +/* First question for AppTP FAQ page */ +"appTP.faq.question1" = "How does App Tracking Protection work?"; + +/* Second question for AppTP FAQ page */ +"appTP.faq.question2" = "Does App Tracking Protection block trackers in all apps on my device?"; + +/* Third question for AppTP FAQ page */ +"appTP.faq.question3" = "Does App Tracking Protection block all app trackers?"; + +/* Fourth question for AppTP FAQ page */ +"appTP.faq.question4" = "Why does App Tracking Protection use a VPN connection?"; + +/* Fifth question for AppTP FAQ page */ +"appTP.faq.question5" = "Will App Tracking Protection work if I also use a VPN app?"; + +/* Sixth question for AppTP FAQ page */ +"appTP.faq.question6" = "How is App Tracking Protection different from a VPN?"; + +/* Seventh question for AppTP FAQ page */ +"appTP.faq.question7" = "Is my data private?"; + +/* Title for AppTP FAQ page */ +"appTP.faq.title" = "App Tracking Protection FAQ"; + +/* Do not translate. StringsDict entry -- Count part of string 'App Tracking Protection blocked x tracking attempts today' */ +"appTP.home.blockedCount" = "appTP.home.blockedCount"; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the trailing space) */ +"appTP.home.blockedPrefix" = "Η App Tracking Protection απέκλεισε "; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the leading space) */ +"appTP.home.blockedSuffix" = " στις εφαρμογές σας σήμερα."; + +/* Prefix of string 'App Tracking Protection disabled. Tap to re-enable.' (note the trailing space) */ +"appTP.home.disabledPrefix" = "App Tracking Protection disabled. "; + +/* Suffix of string 'App Tracking Protection disabled. Tap to re-enable.' */ +"appTP.home.disabledSuffix" = "Tap to continue blocking tracking attempts across your apps."; + +/* Text indicating the tracking event occured 'just now'. Example: Last attempt 'just now' */ +"appTP.justNow" = "Ακριβώς τώρα"; + +/* View to manage trackers for AppTP. Allows the user to turn trackers on or off. */ +"appTP.manageTrackers" = "Διαχείριση εφαρμογών παρακολούθησης"; + +/* Button title for AppTP onboarding */ +"appTP.onboarding.continueButton" = "Continue"; + +/* Button title for AppTP onboarding to enable AppTP */ +"appTP.onboarding.enableeButton" = "Enable App Tracking Protection"; + +/* Button title for AppTP onboarding to learn more about AppTP */ +"appTP.onboarding.learnMoreButton" = "Learn More"; + +/* First part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info1" = "Over 85% of free iOS apps"; + +/* Second part of info on the first AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page1Info2" = " we’ve tested allow other companies to track your personal information, even when you’re sleeping."; + +/* Third part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info3" = "See who we catch trying to track you in your apps and take back control."; + +/* First part of info on the second AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page2Info1" = "App Tracking Protection "; + +/* Second part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info2" = "detects and blocks app trackers from other companies,"; + +/* Third part of info on the second AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page2Info3" = " like when Google attempts to track you in a health app."; + +/* Fourth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info4" = "It’s free,"; + +/* Fifth part of info on the second AppTP onboarding page (note the leading and trailing space) */ +"appTP.onboarding.page2Info5" = " and you can enjoy your apps as you normally would. Working in the background, it helps "; + +/* Sixth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info6" = "protect you night and day."; + +/* First part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info1" = "App Tracking Protection is not a VPN."; + +/* Second part of info on the third AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page3Info2" = " However, your device will recognize it as one. This is because it uses a local VPN connection to work."; + +/* Third part of info on the third AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page3Info3" = "App Tracking Protection is different. "; + +/* Fourth part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info4" = "It never routes app data through an external server."; + +/* Title for first AppTP onboarding page */ +"appTP.onboarding.title1" = "One easy step for better app privacy!"; + +/* Title for second AppTP onboarding page */ +"appTP.onboarding.title2" = "How does it work?"; + +/* Title for third AppTP onboarding page */ +"appTP.onboarding.title3" = "Who sees your data?"; + +/* Breakage report app name label */ +"appTP.report.appLabel" = "Which app is having issues?"; + +/* Breakage report app name placeholder */ +"appTP.report.appPlaceholder" = "App name"; + +/* Breakage report category label */ +"appTP.report.categoryLabel" = "Τι συμβαίνει;"; + +/* Breakage report comment label */ +"appTP.report.commentLabel" = "Comments"; + +/* Breakage report comment placeholder */ +"appTP.report.commentPlaceholder" = "Add additional details"; + +/* Breakage report footer explaining what is collected in the breakage report */ +"appTP.report.footer" = "In addition to the details entered into this form, your app issue report will contain:\n• A list of trackers blocked in the last 10 min\n• Whether App Tracking Protection is enabled\n• Aggregate DuckDuckGo app diagnostics"; + +/* Breakage report submit button */ +"appTP.report.submit" = "Υποβολή"; + +/* Breakage report form title */ +"appTP.report.title" = "Report Issue"; + +/* Breakage report succcess message */ +"appTP.report.toast" = "Σας ευχαριστούμε! Το σχόλιο υποβλήθηκε."; + +/* Cancel button for 'Report an Issue' alert. */ +"appTP.reportAlert.cancel" = "Όχι τώρα"; + +/* Confirm button for 'Report an Issue' alert. */ +"appTP.reportAlert.confirm" = "Αναφορά προβλήματος"; + +/* Message for 'Report an Issue' alert. */ +"appTP.reportAlert.message" = "Let us know if you disabled App Tracking Protection for this specific tracker because it caused app issues. Your feedback helps us improve!"; + +/* Title for 'Report an Issue' alert. */ +"appTP.reportAlert.title" = "Report Issue?"; + +/* Toast notification diplayed after restoring the blocklist to default settings */ +"appTP.restoreDefaultsToast" = "Default settings restored"; + +/* Button to restore the blocklist to its default state. */ +"appTP.restoreDefualts" = "Επαναφορά προεπιλογών"; + +/* Title for the App Tracking Protection feature */ +"appTP.title" = "Προστασία παρακολούθησης από εφαρμογές"; + +/* Text indicating when the tracker was last allowed. Example: Last attempt allowed (timeString) */ +"appTP.trackerAllowedTimestamp" = "Τελευταία επιτρεπόμενη προσπάθεια %@"; + +/* Text indicating when the tracker was last blocked. Example: Last attempt blocked (timeString) */ +"appTP.trackerBlockedTimestamp" = "Τελευταία αποκλεισμένη προσπάθεια %@"; + +/* Do not translate. StringsDict entry -- Subtitle for tracking attempts in App Tracking Protection Activity View. Example: (count) tracking attempts */ +"appTP.trackingattempts" = "appTP.trackingattempts"; + /* Authentication Alert Sign In Button */ "auth.alert.login.button" = "Είσοδος"; @@ -301,6 +529,18 @@ /* Toast message when a login item without a title is deleted */ "autofill.logins.list.login-deleted-message-no-title" = "Η σύνδεση διαγράφηκε"; +/* Title for a button that allows a user to reset their list of never saved sites */ +"autofill.logins.list.never.saved" = "Επαναφορά αποκλεισμένων ιστότοπων"; + +/* Cancel button for resetting list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.cancel" = "Ακύρωση"; + +/* Confirm button to reset list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.confirm" = "Επαναφορά αποκλεισμένων ιστότοπων"; + +/* Alert title */ +"autofill.logins.list.never.saved.reset.action.title" = "Εάν κάνετε επαναφορά των αποκλεισμένων ιστότοπων, θα σας ζητηθεί να αποθηκεύσετε τη σύνδεσή σας την επόμενη φορά που θα συνδεθείτε σε οποιονδήποτε από τους ιστότοπους αυτούς."; + /* Placeholder for search field on autofill login listing */ "autofill.logins.list.search-placeholder" = "Αναζήτηση στοιχείων σύνδεσης"; @@ -335,7 +575,7 @@ "autofill.logins.prompt.password.button.title" = "Κωδικός πρόσβασης για %@"; /* Title for autofill login prompt */ -"autofill.logins.prompt.title" = "Χρήση αποθηκευμένων στοιχείων σύνδεσης;"; +"autofill.logins.prompt.title" = "Χρήση αποθηκευμένου κωδικού πρόσβασης;"; /* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ "autofill.logins.search.no-results.subtitle" = "για '%@'"; @@ -385,27 +625,24 @@ /* Title for the alert dialog telling the user an updated username is no longer a private email address */ "autofill.removed.duck.address.title" = "Το ιδιωτικό όνομα χρήστη Duck Address καταργήθηκε"; +/* CTA displayed on modal asking if the user never wants to be prompted to save a login for this website agin */ +"autofill.save-login.never-prompt.CTA" = "Μην ζητάτε ποτέ αυτόν τον ιστότοπο"; + /* Message displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.message" = "Οι συνδέσεις αποθηκεύονται με ασφάλεια στη συσκευή σας στο μενού Συνδέσεις."; +"autofill.save-login.new-user.message" = "Οι κωδικοί πρόσβασης αποθηκεύονται με ασφάλεια στη συσκευή σας στο μενού Συνδέσεις."; /* Title displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.title" = "Θέλετε να αποθηκεύσει το DuckDuckGo τη σύνδεσή σας;"; +"autofill.save-login.new-user.title" = "Θέλετε το DuckDuckGo να αποθηκεύσει τον κωδικό πρόσβασής σας;"; /* Cancel CTA displayed on modal asking for the user to save the login */ "autofill.save-login.not-now.CTA" = "Να μην αποθηκευτεί"; -/* Confirm CTA displayed on modal asking for the user to save the login */ -"autofill.save-login.save.CTA" = "Αποθήκευση σύνδεσης"; - /* Title displayed on modal asking for the user to save the login */ "autofill.save-login.title" = "Αποθήκευση σύνδεσης;"; /* Confirm CTA displayed on modal asking for the user to save the password */ "autofill.save-password.save.CTA" = "Αποθήκευση κωδικού πρόσβασης"; -/* Title displayed on modal asking for the user to save the password */ -"autofill.save-password.title" = "Αποθήκευση κωδικού πρόσβασης;"; - /* Accessibility title for a Show Password button displaying actial password instead of ***** */ "autofill.show-password" = "Εμφάνιση κωδικού πρόσβασης"; @@ -413,7 +650,7 @@ "autofill.signin.to.manage" = "%@ για διαχείριση των διευθύνσεών σας Duck Address σε αυτήν τη συσκευή."; /* Message displayed on modal asking for the user to update the password */ -"autofill.update-password.message" = "Το DuckDuckGo θα ενημερώσει αυτήν την αποθηκευμένη σύνδεση στη συσκευή σας."; +"autofill.update-password.message" = "Το DuckDuckGo θα ενημερώσει αυτόν τον αποθηκευμένο κωδικό πρόσβασης στη συσκευή σας."; /* Confirm CTA displayed on modal asking for the user to update the password */ "autofill.update-password.save.CTA" = "Ενημέρωση κωδικού πρόσβασης"; @@ -547,9 +784,21 @@ /* Broken Site Category */ "brokensite.category.videos" = "Το βίντεο δεν αναπαράχθηκε"; +/* Broken Site Category Placeholder */ +"brokensite.categoryPlaceholder" = "Επιλέξτε το πρόβλημα που αντιμετωπίζετε, από τη λίστα..."; + +/* Broken Site Category Section Title */ +"brokensite.categoryTitle" = "ΕΠΙΛΕΞΤΕ ΚΑΤΗΓΟΡΙΑ"; + +/* Broken Site Comment Placeholder */ +"brokensite.commentPlaceholder" = "Η κοινοποίηση περισσότερων λεπτομερειών μπορεί να μας βοηθήσει να επιλύσουμε το πρόβλημα αυτό"; + /* Broken Site Section Title */ "brokensite.sectionTitle" = "ΠΕΡΙΓΡΑΨΤΕ ΤΙ ΣΥΝΕΒΗ"; +/* No comment provided by engineer. */ +"bucket: %@" = "κάδος: %@"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Προηγούμενος μήνας"; @@ -781,6 +1030,39 @@ /* Empty search placeholder on bookmarks search */ "empty.search" = "Δεν βρέθηκαν αντιστοιχίες"; +/* No comment provided by engineer. */ +"Error" = "Σφάλμα"; + +/* Button title to Sign In */ +"error.email-protection-sign-in.action" = "Είσοδος"; + +/* Alert message */ +"error.email-protection-sign-in.body" = "Ζητούμε συγγνώμη. Συνδεθείτε ξανά για να ενεργοποιήσετε εκ νέου τις λειτουργίες του Email Protection σε αυτό το πρόγραμμα περιήγησης."; + +/* Alert title */ +"error.email-protection-sign-in.title" = "Σφάλμα προστασίας Email Protection"; + +/* Button title to open device settings */ +"error.insufficient-disk-space.action" = "Άνοιγμα ρυθμίσεων"; + +/* Alert message */ +"error.insufficient-disk-space.body" = "Φαίνεται ότι ο χώρος αποθήκευσης της συσκευής σας έχει εξαντληθεί. Απελευθερώστε χώρο για να συνεχίσετε."; + +/* Alert title */ +"error.insufficient-disk-space.title" = "Δεν υπάρχει αρκετός χώρος αποθήκευσης"; + +/* Button title that is shutting down the app */ +"error.preemptive-crash.action" = "Κλείστε την εφαρμογή"; + +/* Alert message */ +"error.preemptive-crash.body" = "Φαίνεται ότι υπάρχει κάποιο πρόβλημα με την εφαρμογή και χρειάζεται να κλείσει. Ανοίξτε τη ξανά για να συνεχίσετε."; + +/* Alert title */ +"error.preemptive-crash.title" = "Ανιχνεύθηκε πρόβλημα με την εφαρμογή"; + +/* Generic error message on a dialog for when the cause is not known. */ +"error.unknown.try.again" = "An unknown error has occurred"; + /* No comment provided by engineer. */ "favorite" = "Αγαπημένο"; @@ -1000,6 +1282,18 @@ /* Home tab title */ "homeTab.title" = "Αρχική σελίδα"; +/* OK title for invite screen alert dismissal button */ +"invite.alert.ok.button" = "OK"; + +/* Continue button on an invite dialog */ +"invite.dialog.continue.button" = "Continue"; + +/* Get Started button on an invite dialog */ +"invite.dialog.get.started.button" = "Get Started"; + +/* Message to show after user enters an unrecognized invite code */ +"invite.dialog.unrecognized.code.message" = "We didn’t recognize this Invite Code."; + /* No comment provided by engineer. */ "keyCommandAddBookmark" = "Προσθήκη σελιδοδείκτη"; @@ -1066,15 +1360,15 @@ /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "Το DuckDuckGo για Mac έχει την ταχύτητα που χρειάζεστε, τις λειτουργίες περιήγησης που αναμένετε και περιλαμβάνει τις καλύτερες δυνατότητες απορρήτου στην κατηγορία μας."; -/* Body text for the macOS waitlist notification */ -"mac-waitlist.available.notification.body" = "Ανοίξτε την πρόσκλησή σας"; - /* Title for the macOS waitlist notification */ "mac-waitlist.available.notification.title" = "Το DuckDuckGo για Mac είναι έτοιμο!"; /* Title for the copy action */ "mac-waitlist.copy" = "Αντιγραφή"; +/* Description text above the Share Link button */ +"mac-waitlist.join-waitlist-screen.on-your-computer-go-to" = "Στον υπολογιστή σας με Windows, μεταβείτε στη διεύθυνση:"; + /* Description text above the Share Link button */ "mac-waitlist.join-waitlist-screen.on-your-mac-go-to" = "Στον Mac σας, μεταβείτε στη διεύθυνση:"; @@ -1087,6 +1381,9 @@ /* Disclaimer for the Join Waitlist screen */ "mac-waitlist.join-waitlist-screen.windows" = "Έρχεται σύντομα για Windows!"; +/* Title for the macOS waitlist button redirecting to Windows waitlist */ +"mac-waitlist.join-waitlist-screen.windows-waitlist" = "Αναζητάτε την έκδοση των Windows;"; + /* Title for the settings subtitle */ "mac-waitlist.settings.browse-privately" = "Περιηγηθείτε ιδιωτικά με την εφαρμογή μας για Mac"; @@ -1097,7 +1394,7 @@ "mac-waitlist.share-sheet.title" = "DuckDuckGo για Mac"; /* Title for the Mac Waitlist feature */ -"mac-waitlist.title" = "Εφαρμογή DuckDuckGo για υπολογιστές"; +"mac-waitlist.title" = "Εφαρμογή DuckDuckGo για Mac"; /* No comment provided by engineer. */ "menu.button.hint" = "Μενού περιήγησης"; @@ -1114,9 +1411,81 @@ /* Edit button */ "navigation.title.edit" = "Επεξεργασία"; +/* String indicating NetP is connected when viewed from the settings screen */ +"netP.cell.connected" = "Connected"; + +/* String indicating NetP is disconnected when viewed from the settings screen */ +"netP.cell.disconnected" = "Not connected"; + +/* Title for the Network Protection feature */ +"netP.title" = "Network Protection"; + +/* Message for the network protection invite dialog */ +"network.protection.invite.dialog.message" = "Enter your invite code to get started."; + +/* Title for the network protection invite screen */ +"network.protection.invite.dialog.title" = "You're invited to try Network Protection"; + +/* Prompt for the network protection invite code text field */ +"network.protection.invite.field.prompt" = "Invite Code"; + +/* Message explaining that netP is invite only */ +"network.protection.invite.only.message" = "DuckDuckGo Network Protection is currently invite-only."; + +/* Message for the network protection invite success view */ +"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; + +/* Title for the network protection invite success view */ +"network.protection.invite.success.title" = "Success! You’re in."; + +/* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ +"network.protection.status.connected.format" = "Connected - %@"; + +/* The label for the NetP VPN when connecting */ +"network.protection.status.connecting" = "Connecting..."; + +/* The label for the NetP VPN when disconnected */ +"network.protection.status.disconnected" = "Not connected"; + +/* The label for the NetP VPN when disconnecting */ +"network.protection.status.disconnecting" = "Disconnecting..."; + +/* Message label text for the netP status view */ +"network.protection.status.header.message" = "DuckDuckGo's VPN secures all of your device's Internet traffic anytime, anywhere."; + +/* Header title label text for the status view when netP is disconnected */ +"network.protection.status.header.title.off" = "Network Protection is Off"; + +/* Header title label text for the status view when netP is connected */ +"network.protection.status.header.title.on" = "Network Protection is On"; + +/* The status view 'Share Feedback' button which is shown inline on the status view after the \(netPInviteOnlyMessage) text */ +"network.protection.status.menu.share.feedback" = "Share Feedback"; + +/* Connection details label shown in NetworkProtection's status view. */ +"network.protection.status.view.connection.details" = "Connection Details"; + +/* Generic connection failed error message shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.message" = "Please try again later."; + +/* Generic connection failed error title shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; + +/* IP Address label shown in NetworkProtection's status view. */ +"network.protection.status.view.ip.address" = "IP Address"; + +/* Location label shown in NetworkProtection's status view. */ +"network.protection.status.view.location" = "Location"; + +/* Title label text for the status view when netP is disconnected */ +"network.protection.status.view.title" = "Network Protection"; + /* Do not translate - stringsdict entry */ "number.of.tabs" = "number.of.tabs"; +/* No comment provided by engineer. */ +"Ok" = "Εντάξει"; + /* Text displayed on notification appearing in the address bar when the browser dismissed the cookie popup automatically rejecting it */ "omnibar.notification.cookies-managed" = "Διαχειριζόμενα cookies"; @@ -1156,6 +1525,18 @@ /* ’Link’ is link on a website */ "open.externally.failed" = "Δεν είναι δυνατή η εύρεση της εφαρμογής που απαιτείται για άνοιγμα αυτού του συνδέσμου"; +/* No comment provided by engineer. */ +"Please enter which app on your device is broken." = "Εισαγάγετε ποια εφαρμογή δεν λειτουργεί σωστά στη συσκευή σας."; + +/* Activate button */ +"pm.activate" = "Reactivate"; + +/* Cancel button */ +"pm.cancel" = "Cancel"; + +/* Deactivate button */ +"pm.deactivate" = "Deactivate"; + /* No comment provided by engineer. */ "preserveLogins.domain.list.footer" = "Οι ιστότοποι βασίζονται σε cookies για να σας διατηρούν συνδεδεμένους. Όταν ενεργοποιείτε τη διαγραφή δραστηριότητας για έναν ιστότοπο, τα cookies δεν διαγράφονται και παραμένετε συνδεδεμένοι, ακόμα και μετά τη χρήση του Κουμπιού διαγραφής δραστηριότητας. Εξακολουθούμε να αποκλείουμε εφαρμογές παρακολούθησης από τρίτους που υπάρχουν σε ιστότοπους με διαγραφή δραστηριότητας."; @@ -1225,8 +1606,8 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Αγαπημένα"; -/* No comment provided by engineer. */ -"settings.about.text" = "Στην DuckDuckGo, θέτουμε τα νέα πρότυπα σε ό,τι αφορά την αξιοπιστία του διαδικτύου.\n\nΤο πρόγραμμα περιήγησης Προστασίας προσωπικών δεδομένων DuckDuckGo παρέχει όλα όσα χρειάζεστε για να προστατεύετε τον εαυτό σας όταν πραγματοποιείτε αναζήτηση και περιήγηση στο διαδίκτυο, συμπεριλαμβανομένου του αποκλεισμού παρακολούθησης, πιο έξυπνης κρυπτογράφησης και ιδιωτικής αναζήτησης DuckDuckGo.\n\nΆλλωστε, στο διαδίκτυο δεν θα πρέπει να αισθάνεστε άβολα, ενώ το να απολαμβάνετε την ιδιωτικότητα που σας αξίζει στο διαδίκτυο θα πρέπει να είναι τόσο απλό όσο το κλείσιμο μιας πόρτας."; +/* about page */ +"settings.about.text" = "Το DuckDuckGo αποτελεί την ανεξάρτητη εταιρεία προστασίας προσωπικών δεδομένων στο διαδίκτυο που ιδρύθηκε το 2008 για όσους έχουν βαρεθεί να παρακολουθούνται στο διαδίκτυο και επιθυμούν μια εύκολη λύση. Αποτελούμε τρανή απόδειξη ότι μπορείτε να αποκτήσετε πραγματική προστασία του απορρήτου σας στο διαδίκτυο χωρίς συμβιβασμούς.\n\nο πρόγραμμα περιήγησης DuckDuckGo διαθέτει τις λειτουργίες που περιμένετε από ένα πρόγραμμα περιήγησης, όπως σελιδοδείκτες, καρτέλες, κωδικούς πρόσβασης και πολλά άλλα, καθώς και πάνω από [δώδεκα ισχυρές λειτουργίες προστασίες απορρήτου](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/) που δεν προσφέρονται από τα περισσότερα δημοφιλή προγράμματα περιήγησης βάσει προεπιλογής. Αυτό το μοναδικά ολοκληρωμένο σύνολο προστασίας του απορρήτου συμβάλλει στην προστασία των διαδικτυακών δραστηριοτήτων σας, από αναζήτηση έως περιήγηση, αποστολή email και πολλά άλλα.\n\nΗ προστασία απορρήτου μας λειτουργεί χωρίς να χρειάζεται να γνωρίζετε τις τεχνικές λεπτομέρειες ή να ασχολείστε με περίπλοκες ρυθμίσεις. Το μόνο που έχετε να κάνετε είναι να αλλάξετε το πρόγραμμα περιήγησής σας σε DuckDuckGo σε όλες τις συσκευές σας και θα έχετε ιδιωτικότητα βάσει προεπιλογής.\n\nΑλλά αν *θέλετε* να ρίξετε μια αναλυτική ματιά, μπορείτε να βρείτε περισσότερες πληροφορίες σχετικά με το πώς λειτουργούν οι προστασίες απορρήτου του DuckDuckGo στις [σελίδες της ενότητας Βοήθεια](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/)."; /* Report a Broken Site screen confirmation button */ "siteFeedback.buttonText" = "Υποβολή αναφοράς"; @@ -1246,6 +1627,9 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Ποιος ιστότοπος είναι κατεστραμμένος;"; +/* No comment provided by engineer. */ +"sync.remove-device.message" = "Το «%@» δεν θα μπορεί πλέον να έχει πρόσβαση στα συγχρονισμένα δεδομένα σας."; + /* Accessibility label on remove button */ "tab.close.home" = "Κλείσιμο αρχικής καρτέλας"; @@ -1318,6 +1702,57 @@ /* Voice-search footer note with on-device privacy warning */ "voiceSearch.footer.note" = "Η επεξεργασία του ήχου πραγματοποιείται στη συσκευή. Δεν αποθηκεύεται ούτε κοινοποιείται σε οποιονδήποτε, συμπεριλαμβανομένου του DuckDuckGo."; +/* Title for the button to enable push notifications in system settings */ +"waitlist.allow-notifications" = "Επιτρέψτε ειδοποιήσεις"; + +/* Body text for the waitlist notification */ +"waitlist.available.notification.body" = "Ανοίξτε την πρόσκλησή σας"; + +/* Title for the copy action */ +"waitlist.copy" = "Αντιγραφή"; + +/* Label text for the invite code */ +"waitlist.invite-code" = "Κωδικός πρόσκλησης"; + +/* Step title on the invite screen */ +"waitlist.invite-screen.step.title" = "Βήμα %d"; + +/* Title for the invite code screen */ +"waitlist.invite-screen.youre-invited" = "Έχετε προσκληθεί!"; + +/* Title for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.join" = "Εγγραφείτε στην Ιδιωτική λίστα αναμονής"; + +/* Temporary status text for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.joining" = "Εγγραφή στη λίστα αναμονής..."; + +/* Title for the Share Link button */ +"waitlist.join-waitlist-screen.share-link" = "Κοινή χρήση συνδέσμου"; + +/* Notification text for the waitlist */ +"waitlist.joined.no-notification.get-notification" = "ΝΑ ΕΙΔΟΠΟΙΗΘΩ"; + +/* Title for the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.get-notification-confirmation-title" = "Θα θέλατε να λάβετε ειδοποίηση όταν έρθει η σειρά σας;"; + +/* Cancel button in the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.no-thanks" = "Όχι, ευχαριστώ"; + +/* Text used for the Notifications Disabled state */ +"waitlist.notification.disabled" = "Μπορούμε να σας ειδοποιήσουμε όταν έρθει η σειρά σας, ωστόσο οι ειδοποιήσεις είναι προς το παρόν απενεργοποιημένες για το DuckDuckGo."; + +/* Privacy disclaimer for the Waitlist feature */ +"waitlist.privacy-disclaimer" = "Δεν θα χρειαστεί να κοινοποιήσετε προσωπικά στοιχεία για εγγραφή στη λίστα αναμονής. Θα εξασφαλίσετε τη θέση σας στη λίστα αναμονής βάσει χρονικής σήμανσης που υπάρχει αποκλειστικά στη συσκευή σας, ώστε να μπορούμε να σας ειδοποιήσουμε μόλις έρθει η σειρά σας."; + +/* Title for the queue screen */ +"waitlist.queue-screen.on-the-list" = "Βρίσκεστε στη λίστα!"; + +/* Title for the settings subtitle */ +"waitlist.settings.download-available" = "Διαθέσιμη λήψη"; + +/* Title for the share sheet entry */ +"waitlist.share-sheet.title" = "Έχετε προσκληθεί!"; + /* Confirmation message */ "web.url.remove.favorite.done" = "Το Αγαπημένο αφαιρέθηκε"; @@ -1342,3 +1777,51 @@ /* Alert title explaining the message is shown by a website */ "webJSAlert.website-message.format" = "Μήνυμα από %@:"; +/* No comment provided by engineer. */ +"Welcome to the Duck Side!" = "Καλωσορίσατε στην πλευρά της Πάπιας!"; + +/* Title for the Windows waitlist notification */ +"windows-waitlist.available.notification.title" = "Δοκιμάστε το DuckDuckGo για Windows!"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-1.description" = "Επισκεφτείτε αυτήν τη διεύθυνση URL στη συσκευή Windows που διαθέτετε για να κάνετε λήψη:"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-2.description" = "Ανοίξτε το πρόγραμμα εγκατάστασης του DuckDuckGo στις Λήψεις, επιλέξτε Εγκατάσταση και έπειτα εισαγάγετε τον κωδικό πρόσκλησής σας."; + +/* Subtitle for the Windows Waitlist Invite screen */ +"windows-waitlist.invite-screen.subtitle" = "Είστε έτοιμοι να χρησιμοποιήσετε το DuckDuckGo σε Windows;"; + +/* Title for the Windows waitlist button redirecting to Mac waitlist */ +"windows-waitlist.join-waitlist-screen.mac-waitlist" = "Αναζητάτε την έκδοση των Mac;"; + +/* Title for the Join Windows Waitlist screen */ +"windows-waitlist.join-waitlist-screen.try-duckduckgo-for-windows" = "Αποκτήστε πρόωρη πρόσβαση για να δοκιμάσετε το DuckDuckGo για Windows!"; + +/* Message for the alert to confirm enabling notifications */ +"windows-waitlist.joined.no-notification.get-notification-confirmation-message" = "Θα σας στείλουμε ειδοποίηση όταν θα είναι έτοιμο προς λήψη το αντίγραφό σας DuckDuckGo για Windows. "; + +/* Label text for the Joined Waitlist state with notifications declined */ +"windows-waitlist.joined.notifications-declined" = "Η πρόσκλησή σας για να δοκιμάσετε το DuckDuckGo για Windows θα σταλεί εδώ. Ελέγξτε πάλι σύντομα. Διαφορετικά, μπορούμε να σας στείλουμε ειδοποίηση όταν έρθει η σειρά σας."; + +/* Label text for the Joined Waitlist state with notifications enabled */ +"windows-waitlist.joined.notifications-enabled" = "Θα σας στείλουμε ειδοποίηση όταν θα είναι έτοιμο προς λήψη το αντίγραφό σας DuckDuckGo για Windows."; + +/* Title for the settings subtitle */ +"windows-waitlist.settings.browse-privately" = "Περιηγηθείτε ιδιωτικά με την εφαρμογή μας για Windows"; + +/* Message used when sharing to iMessage. Parameter is an eight digit invite code. */ +"windows-waitlist.share-sheet.invite-code-message" = "Έχετε προσκληθεί!\n\nΕίστε έτοιμοι να χρησιμοποιήσετε το DuckDuckGo σε Windows;\n\nΒήμα 1\nΕπισκεφθείτε αυτήν τη διεύθυνση URL στη συσκευή σας Windows για να πραγματοποιήσετε λήψη:\nhttps://duckduckgo.com/windows\n\nΒήμα 2\nΑνοίξτε το πρόγραμμα εγκατάστασης του DuckDuckGo στις Λήψεις, επιλέξτε Εγκατάσταση και έπειτα εισαγάγετε τον κωδικό πρόσκλησής σας.\n\nΚωδικός πρόσκλησης: %@"; + +/* Message used when sharing to iMessage */ +"windows-waitlist.share-sheet.message" = "Είστε έτοιμοι να αρχίσετε μια ιδιωτική περιήγηση σε Windows;\n\nΕπισκεφθείτε αυτήν τη διεύθυνση URL στον υπολογιστή σας για να πραγματοποιήσετε λήψη:\nhttps://duckduckgo.com/windows"; + +/* Summary text for the Windows browser waitlist */ +"windows-waitlist.summary" = "Το DuckDuckGo για Windows έχει ό,τι χρειάζεστε για να περιηγηθείτε με περισσότερη ιδιωτικότητα — ιδιωτική αναζήτηση, αποκλεισμός εφαρμογών παρακολούθησης, αναγκαστική κρυπτογράφηση και αποκλεισμός αναδυόμενων παραθύρων για cookie, ενώ έρχονται σύντομα και περισσότερες κορυφαίες προστασίες στην κατηγορία τους."; + +/* Title for the Windows Waitlist feature */ +"windows-waitlist.title" = "Εφαρμογή DuckDuckGo για Windows"; + +/* Title for the Windows browser download link page */ +"windows-waitlist.waitlist-download-screen.try-duckduckgo-for-windows" = "Αποκτήστε το DuckDuckGo για Windows!"; + diff --git a/DuckDuckGo/el.lproj/Settings.strings b/DuckDuckGo/el.lproj/Settings.strings index ead408dd74..a05763b68c 100644 --- a/DuckDuckGo/el.lproj/Settings.strings +++ b/DuckDuckGo/el.lproj/Settings.strings @@ -37,6 +37,9 @@ /* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ "9Ko-0g-T3h.text" = "Μέγεθος κειμένου"; +/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ +"9kt-6R-XiZ.text" = "Τίτλος"; + /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Εκκίνηση εφαρμογής"; @@ -73,6 +76,12 @@ /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Παγκόσμιος έλεγχος απορρήτου (GPC)"; +/* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ +"CxT-QK-iVn.title" = "Προσθήκη"; + +/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ +"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; + /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Έξοδος εφαρμογής, ανενεργή για 15 λεπτά"; @@ -151,6 +160,9 @@ /* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ "HLr-R8-xxF.text" = "Προεπισκοπήσεις με παρατεταμένο πάτημα"; +/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ +"hoT-Nu-KXP.text" = "Περιηγηθείτε ιδιωτικά με την εφαρμογή μας για Windows"; + /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Η προστασία προσωπικών δεδομένων είναι ενεργοποιημένη για όλους τους ιστότοπους"; @@ -163,6 +175,12 @@ /* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ "m23-t6-9cb.text" = "Κοινοποίηση σχολίου"; +/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ +"mLn-1x-Fl5.title" = "Θέση γραμμής διευθύνσεων"; + +/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ +"mqV-pf-NZ1.text" = "Θέση γραμμής διευθύνσεων"; + /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Επιτρέψτε στο DuckDuckGo να διαχειρίζεται τα αναδυόμενα παράθυρα συναίνεσης για cookies"; @@ -175,15 +193,27 @@ /* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ "oM7-1o-9oY.text" = "Σχετικά με το DuckDuckGo"; +/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ +"opn-JO-idF.text" = "Κορυφή"; + /* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ "OxE-MQ-uJk.headerTitle" = "Περισσότερα από το DuckDuckGo"; +/* Class = "UILabel"; text = "Sync"; ObjectID = "oXN-ez-gct"; */ +"oXN-ez-gct.text" = "Συγχρονισμός"; + /* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ "P0F-ts-ekd.text" = "Περιηγηθείτε ιδιωτικά με την εφαρμογή μας για Mac "; +/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ +"qah-gb-udB.text" = "Network Protection"; + /* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ "qeN-SV-zy7.text" = "Label"; +/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ +"RQ8-H1-Ez1.text" = "Εφαρμογή DuckDuckGo Windows"; + /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Μέγεθος κειμένου"; @@ -232,8 +262,8 @@ /* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ "yvj-LL-MiR.text" = "Προσθήκη εφαρμογής στο Dock σας"; -/* Class = "UILabel"; text = "DuckDuckGo Desktop App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "Εφαρμογή DuckDuckGo για υπολογιστές"; +/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ +"Yz9-17-qnn.text" = "Εφαρμογή DuckDuckGo Mac"; /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Νέα καρτέλα"; diff --git a/DuckDuckGo/en.lproj/Localizable.strings b/DuckDuckGo/en.lproj/Localizable.strings index ae15b576b9..f10a4c5602 100644 --- a/DuckDuckGo/en.lproj/Localizable.strings +++ b/DuckDuckGo/en.lproj/Localizable.strings @@ -100,6 +100,12 @@ /* Share action */ "action.title.share" = "Share"; +/* Settings label for bottom position for the address bar */ +"address.bar.bottom" = "Bottom"; + +/* Settings label for top position for the address bar */ +"address.bar.top" = "Top"; + /* No comment provided by engineer. */ "addWidget.button" = "Add Widget"; @@ -121,6 +127,24 @@ /* No comment provided by engineer. */ "alert.message.bookmarkAll" = "Existing bookmarks will not be duplicated."; +/* Description for alert shown when sync bookmarks paused for too many items */ +"alert.sync-bookmarks-paused-description" = "You have exceeded the bookmarks sync limit. Try deleting some bookmarks. Until this is resolved your bookmarks will not be backed up."; + +/* Title for alert shown when sync bookmarks paused for too many items */ +"alert.sync-bookmarks-paused-title" = "Bookmarks Sync is Paused"; + +/* Description for alert shown when sync credentials paused for too many items */ +"alert.sync-credentials-paused-description" = "You have exceeded the passwords sync limit. Try deleting some passwords. Until this is resolved your passwords will not be backed up."; + +/* Title for alert shown when sync credentials paused for too many items */ +"alert.sync-credentials-paused-title" = "Passwords Sync is Paused"; + +/* Learn more button in alert */ +"alert.sync-paused-alert-learn-more-button" = "Learn More"; + +/* Confirmation button in alert */ +"alert.sync-paused-alert-ok-button" = "OK"; + /* Question from confirmation dialog */ "alert.title.bookmarkAll" = "Bookmark All Tabs?"; @@ -520,6 +544,18 @@ /* Toast message when a login item without a title is deleted */ "autofill.logins.list.login-deleted-message-no-title" = "Login deleted"; +/* Title for a button that allows a user to reset their list of never saved sites */ +"autofill.logins.list.never.saved" = "Reset Excluded Sites"; + +/* Cancel button for resetting list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.cancel" = "Cancel"; + +/* Confirm button to reset list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.confirm" = "Reset Excluded Sites"; + +/* Alert title */ +"autofill.logins.list.never.saved.reset.action.title" = "If you reset excluded sites, you will be prompted to save your Login next time you sign in to any of these sites."; + /* Placeholder for search field on autofill login listing */ "autofill.logins.list.search-placeholder" = "Search Logins"; @@ -554,7 +590,7 @@ "autofill.logins.prompt.password.button.title" = "Password for %@"; /* Title for autofill login prompt */ -"autofill.logins.prompt.title" = "Use a saved Login?"; +"autofill.logins.prompt.title" = "Use a saved password?"; /* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ "autofill.logins.search.no-results.subtitle" = "for '%@'"; @@ -604,27 +640,24 @@ /* Title for the alert dialog telling the user an updated username is no longer a private email address */ "autofill.removed.duck.address.title" = "Private Duck Address username was removed"; +/* CTA displayed on modal asking if the user never wants to be prompted to save a login for this website agin */ +"autofill.save-login.never-prompt.CTA" = "Never Ask for This Site"; + /* Message displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.message" = "Logins are stored securely on your device in the Logins menu."; +"autofill.save-login.new-user.message" = "Passwords are stored securely on your device in the Logins menu."; /* Title displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.title" = "Do you want DuckDuckGo to save your Login?"; +"autofill.save-login.new-user.title" = "Do you want DuckDuckGo to save your password?"; /* Cancel CTA displayed on modal asking for the user to save the login */ "autofill.save-login.not-now.CTA" = "Don’t Save"; -/* Confirm CTA displayed on modal asking for the user to save the login */ -"autofill.save-login.save.CTA" = "Save Login"; - /* Title displayed on modal asking for the user to save the login */ "autofill.save-login.title" = "Save Login?"; /* Confirm CTA displayed on modal asking for the user to save the password */ "autofill.save-password.save.CTA" = "Save Password"; -/* Title displayed on modal asking for the user to save the password */ -"autofill.save-password.title" = "Save Password?"; - /* Accessibility title for a Show Password button displaying actial password instead of ***** */ "autofill.show-password" = "Show Password"; @@ -632,7 +665,7 @@ "autofill.signin.to.manage" = "%@ to manage your Duck Addresses on this device."; /* Message displayed on modal asking for the user to update the password */ -"autofill.update-password.message" = "DuckDuckGo will update this stored Login on your device."; +"autofill.update-password.message" = "DuckDuckGo will update this stored password on your device."; /* Confirm CTA displayed on modal asking for the user to update the password */ "autofill.update-password.save.CTA" = "Update Password"; @@ -1006,6 +1039,33 @@ /* Empty search placeholder on bookmarks search */ "empty.search" = "No matches found"; +/* Button title to Sign In */ +"error.email-protection-sign-in.action" = "Sign In"; + +/* Alert message */ +"error.email-protection-sign-in.body" = "Sorry, please sign in again to re-enable Email Protection features on this browser."; + +/* Alert title */ +"error.email-protection-sign-in.title" = "Email Protection Error"; + +/* Button title to open device settings */ +"error.insufficient-disk-space.action" = "Open Settings"; + +/* Alert message */ +"error.insufficient-disk-space.body" = "Looks like your device has run out of storage space. Please free up space to continue."; + +/* Alert title */ +"error.insufficient-disk-space.title" = "Not enough storage"; + +/* Button title that is shutting down the app */ +"error.preemptive-crash.action" = "Close App"; + +/* Alert message */ +"error.preemptive-crash.body" = "Looks like there's an issue with the app and it needs to close. Please reopen to continue."; + +/* Alert title */ +"error.preemptive-crash.title" = "App issue detected"; + /* Generic error message on a dialog for when the cause is not known. */ "error.unknown.try.again" = "An unknown error has occurred"; @@ -1018,6 +1078,18 @@ /* No comment provided by engineer. */ "favorite.menu.remove" = "Remove"; +/* Display Mode for favorites */ +"favorites.settings.all-devices" = "All Device Favorites"; + +/* Footer of the favorites settings table */ +"favorites.settings.footer" = "Choose which favorites to display on a new tab based on their origin."; + +/* Header of the favorites settings table */ +"favorites.settings.header" = "Display Preferences"; + +/* Display Mode for favorites */ +"favorites.settings.mobile-only" = "Mobile Favorites Only"; + /* No comment provided by engineer. */ "feedback.browserFeatures.ads" = "Ad and pop-up blocking"; @@ -1369,11 +1441,101 @@ https://duckduckgo.com/mac"; /* Title for the Network Protection feature */ "netP.title" = "Network Protection"; +/* Privacy Policy title for Network Protection */ +"network-protection.privacy-policy.title" = "Privacy Policy"; + +/* Title text for the Network Protection terms and conditions accept button */ +"network-protection.waitlist.agree-and-continue" = "Agree and Continue"; + +/* Availability disclaimer for Network Protection join waitlist screen */ +"network-protection.waitlist.availability-disclaimer" = "Network Protection is free to use during early access."; + +/* Agree and Continue button for Network Protection join waitlist screen */ +"network-protection.waitlist.button.agree-and-continue" = "Agree and Continue"; + +/* Enable Notifications button for Network Protection joined waitlist screen */ +"network-protection.waitlist.button.enable-notifications" = "Enable Notifications"; + +/* Button title for users who already have an invite code */ +"network-protection.waitlist.button.existing-invite-code" = "I Have an Invite Code"; + +/* Join Waitlist button for Network Protection join waitlist screen */ +"network-protection.waitlist.button.join-waitlist" = "Join the Waitlist"; + +/* Button title text for the Network Protection waitlist confirmation prompt */ +"network-protection.waitlist.get-started" = "Get Started"; + +/* Subtitle for section 1 of the Network Protection invited screen */ +"network-protection.waitlist.invited.section-1.subtitle" = "Encrypt online traffic across your browsers and apps."; + +/* Title for section 1 of the Network Protection invited screen */ +"network-protection.waitlist.invited.section-1.title" = "Full-device coverage"; + +/* Subtitle for section 2 of the Network Protection invited screen */ +"network-protection.waitlist.invited.section-2.subtitle" = "No need for a separate app. Connect in one click and see your connection status at a glance."; + +/* Title for section 2 of the Network Protection invited screen */ +"network-protection.waitlist.invited.section-2.title" = "Fast, reliable, and easy to use"; + +/* Subtitle for section 3 of the Network Protection invited screen */ +"network-protection.waitlist.invited.section-3.subtitle" = "We do not log or save any data that can connect you to your online activity."; + +/* Title for section 3 of the Network Protection invited screen */ +"network-protection.waitlist.invited.section-3.title" = "Strict no-logging policy"; + +/* Subtitle for Network Protection invited screen */ +"network-protection.waitlist.invited.subtitle" = "Get an extra layer of protection online with the VPN built for speed and simplicity. Encrypt your internet connection across your entire device and hide your location and IP address from sites you visit."; + +/* Title for Network Protection invited screen */ +"network-protection.waitlist.invited.title" = "You’re invited to try\nNetwork Protection early access!"; + +/* First subtitle for Network Protection join waitlist screen */ +"network-protection.waitlist.join.subtitle.1" = "Secure your connection anytime, anywhere with Network Protection, the VPN from DuckDuckGo."; + +/* Second subtitle for Network Protection join waitlist screen */ +"network-protection.waitlist.join.subtitle.2" = "Join the waitlist, and we’ll notify you when it’s your turn."; + +/* Title for Network Protection join waitlist screen */ +"network-protection.waitlist.join.title" = "Network Protection Early Access"; + +/* Title for Network Protection joined waitlist screen */ +"network-protection.waitlist.joined.title" = "You’re on the list!"; + +/* Subtitle 1 for Network Protection joined waitlist screen when notifications are enabled */ +"network-protection.waitlist.joined.with-notifications.subtitle.1" = "New invites are sent every few days, on a first come, first served basis."; + +/* Subtitle 2 for Network Protection joined waitlist screen when notifications are enabled */ +"network-protection.waitlist.joined.with-notifications.subtitle.2" = "We’ll notify you when your invite is ready."; + +/* Body text for the alert to enable notifications */ +"network-protection.waitlist.notification-alert.description" = "We’ll send you a notification when your invite to test Network Protection is ready."; + +/* Subtitle for the alert to confirm enabling notifications */ +"network-protection.waitlist.notification-prompt-description" = "Get a notification when your copy of Network Protection early access is ready."; + +/* Title for the alert to confirm enabling notifications */ +"network-protection.waitlist.notification-prompt-title" = "Know the instant you're invited"; + +/* Title for Network Protection waitlist notification */ +"network-protection.waitlist.notification.text" = "Open your invite"; + +/* Title for Network Protection waitlist notification */ +"network-protection.waitlist.notification.title" = "Network Protection is ready!"; + +/* Subtitle text for the Network Protection settings row */ +"network-protection.waitlist.settings-subtitle.joined-and-invited" = "Your invite is ready!"; + +/* Subtitle text for the Network Protection settings row */ +"network-protection.waitlist.settings-subtitle.joined-but-not-invited" = "You’re on the list!"; + +/* Subtitle text for the Network Protection settings row */ +"network-protection.waitlist.settings-subtitle.waitlist-not-joined" = "Join the private waitlist"; + /* Message for the network protection invite dialog */ "network.protection.invite.dialog.message" = "Enter your invite code to get started."; /* Title for the network protection invite screen */ -"network.protection.invite.dialog.title" = "You're invited to try Network Protection"; +"network.protection.invite.dialog.title" = "You’re invited to try Network Protection"; /* Prompt for the network protection invite code text field */ "network.protection.invite.field.prompt" = "Invite Code"; @@ -1387,6 +1549,9 @@ https://duckduckgo.com/mac"; /* Title for the network protection invite success view */ "network.protection.invite.success.title" = "Success! You’re in."; +/* Title text for an iOS quick action that opens VPN settings */ +"network.protection.quick-action.open-vpn" = "Open VPN"; + /* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ "network.protection.status.connected.format" = "Connected - %@"; @@ -1426,9 +1591,66 @@ https://duckduckgo.com/mac"; /* Location label shown in NetworkProtection's status view. */ "network.protection.status.view.location" = "Location"; +/* Label shown on the title of the settings section in NetworkProtection's status view. */ +"network.protection.status.view.settings.section.title" = "Manage"; + /* Title label text for the status view when netP is disconnected */ "network.protection.status.view.title" = "Network Protection"; +/* Title for the button to link to the iOS app settings and enable notifications app-wide. */ +"network.protection.turn.on.notifications.button.title" = "Turn on Notifications"; + +/* Footer text under the button to link to the iOS app settings and enable notifications app-wide. */ +"network.protection.turn.on.notifications.section.footer" = "Allow DuckDuckGo to notify you if your connection drops or VPN status changes."; + +/* List section footer for the toggle for VPN alerts. */ +"network.protection.vpn.alerts.toggle.section.footer" = "Get notified if your connection drops or VPN status changes."; + +/* Title for the toggle for VPN alerts. */ +"network.protection.vpn.alerts.toggle.title" = "VPN Alerts"; + +/* Footer text for the Always on VPN setting item. */ +"network.protection.vpn.always.on.setting.footer" = "Automatically restore a VPN connection after interruption."; + +/* Title for the Always on VPN setting item. */ +"network.protection.vpn.always.on.setting.title" = "Always On"; + +/* Title for the VPN Location screen's All Countries section. */ +"network.protection.vpn.location.all.countries.section.title" = "All Countries"; + +/* Subtitle of countries item when there are multiple cities, example : */ +"network.protection.vpn.location.country.item.formatted.cities.count" = "%d cities"; + +/* Title for the VPN Location screen's Nearest Available selection item. */ +"network.protection.vpn.location.nearest.available.item.title" = "Nearest Available"; + +/* Footer describing the VPN Location screen's Recommended section which just has Nearest Available. */ +"network.protection.vpn.location.recommended.section.footer" = "Automatically connect to the nearest server we can find"; + +/* Title for the VPN Location screen's Recommended section. */ +"network.protection.vpn.location.recommended.section.title" = "Recommended"; + +/* Title for the VPN Location screen. */ +"network.protection.vpn.location.title" = "VPN Location"; + +/* Title for the VPN Notifications management screen. */ +"network.protection.vpn.notifications.title" = "VPN Notifications"; + +/* Label for the Preferred Location VPN Settings item when the nearest available location is selected. */ +"network.protection.vpn.preferred.location.nearest" = "Nearest Available"; + +/* Title for the Preferred Location VPN Settings item. */ +"network.protection.vpn.preferred.location.title" = "Preferred Location"; + +/* Footer text for the Always on VPN setting item. */ +"network.protection.vpn.secure.dns.setting.footer" = "Network Protection prevents DNS leaks to your Internet Service Provider by routing DNS queries though the VPN tunnel to our own resolver."; + +/* Title for the Always on VPN setting item. */ +"network.protection.vpn.secure.dns.setting.title" = "Secure DNS"; + +/* Title for the VPN Settings screen. */ +"network.protection.vpn.settings.title" = "VPN Settings"; + /* Do not translate - stringsdict entry */ "number.of.tabs" = "number.of.tabs"; @@ -1549,8 +1771,14 @@ https://duckduckgo.com/mac"; /* No comment provided by engineer. */ "section.title.favorites" = "Favorites"; -/* No comment provided by engineer. */ -"settings.about.text" = "At DuckDuckGo, we’re setting the new standard of trust online.\n\nDuckDuckGo Privacy Browser provides all the privacy essentials you need to protect yourself as you search and browse the web, including tracker blocking, smarter encryption, and DuckDuckGo private search.\n\nAfter all, the Internet shouldn’t feel so creepy, and getting the privacy you deserve online should be as simple as closing the blinds."; +/* about page */ +"settings.about.text" = "DuckDuckGo is the independent Internet privacy company founded in 2008 for anyone who’s tired of being tracked online and wants an easy solution. We’re proof you can get real privacy protection online without tradeoffs. + +The DuckDuckGo browser comes with the features you expect from a go-to browser, like bookmarks, tabs, passwords, and more, plus over [a dozen powerful privacy protections](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/) not offered in most popular browsers by default. This uniquely comprehensive set of privacy protections helps protect your online activities, from searching to browsing, emailing, and more. + +Our privacy protections work without having to know anything about the technical details or deal with complicated settings. All you have to do is switch your browser to DuckDuckGo across all your devices and you get privacy by default. + +But if you *do* want a peek under the hood, you can find more information about how DuckDuckGo privacy protections work on our [help pages](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/)."; /* Report a Broken Site screen confirmation button */ "siteFeedback.buttonText" = "Submit Report"; diff --git a/DuckDuckGo/es.lproj/Feedback.strings b/DuckDuckGo/es.lproj/Feedback.strings index dc67d6553d..a81befc8d5 100644 --- a/DuckDuckGo/es.lproj/Feedback.strings +++ b/DuckDuckGo/es.lproj/Feedback.strings @@ -28,9 +28,6 @@ /* Class = "UIBarButtonItem"; title = "Back"; ObjectID = "JF8-Ow-zXL"; */ "JF8-Ow-zXL.title" = "Volver"; -/* Class = "UIBarButtonItem"; title = "Submit"; ObjectID = "jr6-Al-4fr"; */ -"jr6-Al-4fr.title" = "Enviar"; - /* Class = "UIButton"; normalTitle = "Submit"; ObjectID = "lsB-Kq-U8p"; */ "lsB-Kq-U8p.normalTitle" = "Enviar"; @@ -58,9 +55,6 @@ /* Class = "UINavigationItem"; title = "Report Broken Site"; ObjectID = "WjT-HM-yim"; */ "WjT-HM-yim.title" = "Informar de sitio web dañado"; -/* Class = "UILabel"; text = "Broken Site Reason"; ObjectID = "Wx3-Vb-ItS"; */ -"Wx3-Vb-ItS.text" = "Motivo de página dañada"; - /* Class = "UINavigationItem"; title = "Share Feedback"; ObjectID = "Xb0-Zu-opM"; */ "Xb0-Zu-opM.title" = "Compartir opiniones"; diff --git a/DuckDuckGo/es.lproj/Localizable.strings b/DuckDuckGo/es.lproj/Localizable.strings index 68c300fbe1..9262ddf95a 100644 --- a/DuckDuckGo/es.lproj/Localizable.strings +++ b/DuckDuckGo/es.lproj/Localizable.strings @@ -1,6 +1,9 @@ /* No comment provided by engineer. */ "%@" = "%@"; +/* No comment provided by engineer. */ +"%@ [%@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)" = "%1$@ [%2$@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)"; + /* Buton label for Edit action */ "action.generic.edit" = "Editar"; @@ -103,6 +106,12 @@ /* Share action */ "action.title.share" = "Compartir"; +/* Settings label for bottom position for the address bar */ +"address.bar.bottom" = "Inferior"; + +/* Settings label for top position for the address bar */ +"address.bar.top" = "Arriba"; + /* No comment provided by engineer. */ "addWidget.button" = "Añadir widget"; @@ -142,6 +151,225 @@ /* Shown on authentication screen */ "app.authentication.unlock" = "Desbloquear DuckDuckGo."; +/* First part of about page content (note the trailing space) */ +"appTP.about.content1" = "You’ve probably heard about companies like Google and Facebook tracking you behind the scenes on third-party websites. But did you know they also track your personal information through apps on your device?\n\nIn 2022, DuckDuckGo found that "; + +/* Second part of about page content (note the trailing space) */ +"appTP.about.content2" = "over 85% of free iOS apps tested contained hidden trackers from other companies."; + +/* Third part of about page content (note the leading space) */ +"appTP.about.content3" = " Of the 395 apps tested, 60% sent data to Google. This happens even while you’re not using your device.\n\nTrackers in apps may have access to a lot more information than their website tracker cousins, such as your location down to which floor of a building you're on, how often you play games while at work, and when and how long you sleep each day. Even if you haven’t given apps explicit permission to collect data, they can still take it without your knowledge.\n\nTracking networks like Facebook and Google use these little pieces of information to build a digital profile about you. With it, tracking networks can manipulate what you see online and allow advertisers to bid on access to you based on your data.\n\nTrackers in apps is a BIG problem for privacy. But DuckDuckGo has a solution that can help.\n\nWhen enabled in the DuckDuckGo Privacy Browser app, App Tracking Protection blocks many trackers in other apps, not just the trackers we find on websites when you browse. These dual layers of protection reduce what companies know about you overall, so you can use your apps with more peace of mind, knowing you’re more protected."; + +/* Navigation Title for AppTP about page */ +"appTP.about.navTitle" = "About App Trackers"; + +/* Title for AppTP about page */ +"appTP.about.title" = "What Are App Trackers?"; + +/* Title for 'Report an Issue' button in the activity view. */ +"appTP.activityView.reportIssue" = "Report Issue"; + +/* Text label for switch that turns blocking on or off for a tracker */ +"appTP.blockTrackerText" = "Block this Tracker"; + +/* Detail string describing what AppTP is */ +"appTP.cell.detail" = "Bloquea los rastreadores de aplicaciones en tu dispositivo"; + +/* String indicating AppTP is disabled when viewed from the settings screen */ +"appTP.cell.disabled" = "Desactivado"; + +/* String indicating AppTP is enabled when viewed from the settings screen */ +"appTP.cell.enabled" = "Activado"; + +/* Info string informing the user what App Tracking Protection does. */ +"appTP.empty.disabled.info" = "Habilita App Tracking Protection para que podamos bloquear rastreadores molestos en otras aplicaciones."; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.heading" = "We’re blocking hidden trackers"; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.info" = "Come back soon to see a list of all the app trackers we’ve blocked."; + +/* First answer for AppTP FAQ page */ +"appTP.faq.answer1" = "App Tracking Protection blocks app trackers from other companies, like when Facebook tries to track you in a banking app. Companies may still track you in apps they own."; + +/* Second answer for AppTP FAQ page */ +"appTP.faq.answer2" = "Yes! App Tracking Protection works across all apps on your device to block the most common hidden trackers we find trying to collect your personal info."; + +/* Third answer for AppTP FAQ page */ +"appTP.faq.answer3" = "We currently only block the most common trackers that we find on iOS. This helps us to comprehensively test App Tracking Protection and lower frequency of app breakage, while blocking up to 70% of all tracking requests."; + +/* Fourth answer for AppTP FAQ page */ +"appTP.faq.answer4" = "You’ll be asked to set up a virtual private network (VPN) connection, but you don't need to install a VPN app for App Tracking Protection to work.\n\nThis permission, which works only on your device, allows App Tracking Protection to monitor network traffic so that it can block known trackers."; + +/* Fifth answer for AppTP FAQ page */ +"appTP.faq.answer5" = "You can use App Tracking Protection at the same time as using an IKEv2 protocol VPN app on an iOS device. You won’t be able to use App Tracking Protection on an iOS device if you’re using a VPN app that uses a different type of protocol, like WireGuard or OpenVPN type VPNs."; + +/* Sixth answer for AppTP FAQ page */ +"appTP.faq.answer6" = "A VPN sends your data from the device to its own server, where it secures and anonymizes your data from prying eyes. However, this allows the VPN company to see your network traffic.\n\nApp Tracking Protection is different. Instead of sending your data to a VPN server, App Tracking Protection works only on your device, sitting between your apps and the servers they talk to.\n\nWhenever App Tracking Protection recognizes a known tracker, it blocks the tracker from sending personal information (such as your IP address, activity, and device details) off your device. All other traffic reaches its destination, so your apps work normally."; + +/* Seventh answer for AppTP FAQ page */ +"appTP.faq.answer7" = "App Tracking Protection works only on your device and doesn’t send your data off your device to DuckDuckGo. We don’t collect or store any data from your apps."; + +/* First question for AppTP FAQ page */ +"appTP.faq.question1" = "How does App Tracking Protection work?"; + +/* Second question for AppTP FAQ page */ +"appTP.faq.question2" = "Does App Tracking Protection block trackers in all apps on my device?"; + +/* Third question for AppTP FAQ page */ +"appTP.faq.question3" = "Does App Tracking Protection block all app trackers?"; + +/* Fourth question for AppTP FAQ page */ +"appTP.faq.question4" = "Why does App Tracking Protection use a VPN connection?"; + +/* Fifth question for AppTP FAQ page */ +"appTP.faq.question5" = "Will App Tracking Protection work if I also use a VPN app?"; + +/* Sixth question for AppTP FAQ page */ +"appTP.faq.question6" = "How is App Tracking Protection different from a VPN?"; + +/* Seventh question for AppTP FAQ page */ +"appTP.faq.question7" = "Is my data private?"; + +/* Title for AppTP FAQ page */ +"appTP.faq.title" = "App Tracking Protection FAQ"; + +/* Do not translate. StringsDict entry -- Count part of string 'App Tracking Protection blocked x tracking attempts today' */ +"appTP.home.blockedCount" = "appTP.home.blockedCount"; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the trailing space) */ +"appTP.home.blockedPrefix" = "App Tracking Protection ha bloqueado "; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the leading space) */ +"appTP.home.blockedSuffix" = " en tus aplicaciones hoy."; + +/* Prefix of string 'App Tracking Protection disabled. Tap to re-enable.' (note the trailing space) */ +"appTP.home.disabledPrefix" = "App Tracking Protection disabled. "; + +/* Suffix of string 'App Tracking Protection disabled. Tap to re-enable.' */ +"appTP.home.disabledSuffix" = "Tap to continue blocking tracking attempts across your apps."; + +/* Text indicating the tracking event occured 'just now'. Example: Last attempt 'just now' */ +"appTP.justNow" = "Justo ahora"; + +/* View to manage trackers for AppTP. Allows the user to turn trackers on or off. */ +"appTP.manageTrackers" = "Gestionar rastreadores"; + +/* Button title for AppTP onboarding */ +"appTP.onboarding.continueButton" = "Continue"; + +/* Button title for AppTP onboarding to enable AppTP */ +"appTP.onboarding.enableeButton" = "Enable App Tracking Protection"; + +/* Button title for AppTP onboarding to learn more about AppTP */ +"appTP.onboarding.learnMoreButton" = "Learn More"; + +/* First part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info1" = "Over 85% of free iOS apps"; + +/* Second part of info on the first AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page1Info2" = " we’ve tested allow other companies to track your personal information, even when you’re sleeping."; + +/* Third part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info3" = "See who we catch trying to track you in your apps and take back control."; + +/* First part of info on the second AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page2Info1" = "App Tracking Protection "; + +/* Second part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info2" = "detects and blocks app trackers from other companies,"; + +/* Third part of info on the second AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page2Info3" = " like when Google attempts to track you in a health app."; + +/* Fourth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info4" = "It’s free,"; + +/* Fifth part of info on the second AppTP onboarding page (note the leading and trailing space) */ +"appTP.onboarding.page2Info5" = " and you can enjoy your apps as you normally would. Working in the background, it helps "; + +/* Sixth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info6" = "protect you night and day."; + +/* First part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info1" = "App Tracking Protection is not a VPN."; + +/* Second part of info on the third AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page3Info2" = " However, your device will recognize it as one. This is because it uses a local VPN connection to work."; + +/* Third part of info on the third AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page3Info3" = "App Tracking Protection is different. "; + +/* Fourth part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info4" = "It never routes app data through an external server."; + +/* Title for first AppTP onboarding page */ +"appTP.onboarding.title1" = "One easy step for better app privacy!"; + +/* Title for second AppTP onboarding page */ +"appTP.onboarding.title2" = "How does it work?"; + +/* Title for third AppTP onboarding page */ +"appTP.onboarding.title3" = "Who sees your data?"; + +/* Breakage report app name label */ +"appTP.report.appLabel" = "Which app is having issues?"; + +/* Breakage report app name placeholder */ +"appTP.report.appPlaceholder" = "App name"; + +/* Breakage report category label */ +"appTP.report.categoryLabel" = "¿Qué pasa?"; + +/* Breakage report comment label */ +"appTP.report.commentLabel" = "Comments"; + +/* Breakage report comment placeholder */ +"appTP.report.commentPlaceholder" = "Add additional details"; + +/* Breakage report footer explaining what is collected in the breakage report */ +"appTP.report.footer" = "In addition to the details entered into this form, your app issue report will contain:\n• A list of trackers blocked in the last 10 min\n• Whether App Tracking Protection is enabled\n• Aggregate DuckDuckGo app diagnostics"; + +/* Breakage report submit button */ +"appTP.report.submit" = "Enviar"; + +/* Breakage report form title */ +"appTP.report.title" = "Report Issue"; + +/* Breakage report succcess message */ +"appTP.report.toast" = "Gracias, se han enviado tus opiniones."; + +/* Cancel button for 'Report an Issue' alert. */ +"appTP.reportAlert.cancel" = "Ahora no"; + +/* Confirm button for 'Report an Issue' alert. */ +"appTP.reportAlert.confirm" = "Informar de un problema"; + +/* Message for 'Report an Issue' alert. */ +"appTP.reportAlert.message" = "Let us know if you disabled App Tracking Protection for this specific tracker because it caused app issues. Your feedback helps us improve!"; + +/* Title for 'Report an Issue' alert. */ +"appTP.reportAlert.title" = "Report Issue?"; + +/* Toast notification diplayed after restoring the blocklist to default settings */ +"appTP.restoreDefaultsToast" = "Default settings restored"; + +/* Button to restore the blocklist to its default state. */ +"appTP.restoreDefualts" = "Restaurar valores predeterminados"; + +/* Title for the App Tracking Protection feature */ +"appTP.title" = "Protección frente al rastreo de aplicaciones"; + +/* Text indicating when the tracker was last allowed. Example: Last attempt allowed (timeString) */ +"appTP.trackerAllowedTimestamp" = "Último intento permitido % @"; + +/* Text indicating when the tracker was last blocked. Example: Last attempt blocked (timeString) */ +"appTP.trackerBlockedTimestamp" = "Último intento bloqueado %@"; + +/* Do not translate. StringsDict entry -- Subtitle for tracking attempts in App Tracking Protection Activity View. Example: (count) tracking attempts */ +"appTP.trackingattempts" = "appTP.trackingattempts"; + /* Authentication Alert Sign In Button */ "auth.alert.login.button" = "Iniciar sesión"; @@ -301,6 +529,18 @@ /* Toast message when a login item without a title is deleted */ "autofill.logins.list.login-deleted-message-no-title" = "Inicio de sesión eliminado"; +/* Title for a button that allows a user to reset their list of never saved sites */ +"autofill.logins.list.never.saved" = "Restablecer sitios excluidos"; + +/* Cancel button for resetting list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.cancel" = "Cancelar"; + +/* Confirm button to reset list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.confirm" = "Restablecer sitios excluidos"; + +/* Alert title */ +"autofill.logins.list.never.saved.reset.action.title" = "Si restableces los sitios excluidos, se te pedirá que guardes tus datos de inicio de sesión la próxima vez que accedas a cualquiera de estos sitios."; + /* Placeholder for search field on autofill login listing */ "autofill.logins.list.search-placeholder" = "Buscar inicios de sesión"; @@ -335,7 +575,7 @@ "autofill.logins.prompt.password.button.title" = "Contraseña para %@"; /* Title for autofill login prompt */ -"autofill.logins.prompt.title" = "¿Usar un inicio de sesión guardado?"; +"autofill.logins.prompt.title" = "¿Usar una contraseña guardada?"; /* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ "autofill.logins.search.no-results.subtitle" = "para '%@'"; @@ -385,27 +625,24 @@ /* Title for the alert dialog telling the user an updated username is no longer a private email address */ "autofill.removed.duck.address.title" = "Se ha eliminado el nombre de usuario de la Duck Address privada"; +/* CTA displayed on modal asking if the user never wants to be prompted to save a login for this website agin */ +"autofill.save-login.never-prompt.CTA" = "No preguntar nunca para esta página"; + /* Message displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.message" = "Los inicios de sesión se almacenan de forma segura en tu dispositivo en el menú Inicios de sesión."; +"autofill.save-login.new-user.message" = "Las contraseñas se almacenan de forma segura en tu dispositivo en el menú Inicios de sesión."; /* Title displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.title" = "¿Quieres que DuckDuckGo guarde tu inicio de sesión?"; +"autofill.save-login.new-user.title" = "¿Quieres que DuckDuckGo guarde tu contraseña?"; /* Cancel CTA displayed on modal asking for the user to save the login */ "autofill.save-login.not-now.CTA" = "No guardar"; -/* Confirm CTA displayed on modal asking for the user to save the login */ -"autofill.save-login.save.CTA" = "Guardar inicio de sesión"; - /* Title displayed on modal asking for the user to save the login */ "autofill.save-login.title" = "¿Guardar inicio de sesión?"; /* Confirm CTA displayed on modal asking for the user to save the password */ "autofill.save-password.save.CTA" = "Guardar contraseña"; -/* Title displayed on modal asking for the user to save the password */ -"autofill.save-password.title" = "¿Guardar contraseña?"; - /* Accessibility title for a Show Password button displaying actial password instead of ***** */ "autofill.show-password" = "Mostrar contraseña"; @@ -413,7 +650,7 @@ "autofill.signin.to.manage" = "%@ para gestionar tus Duck Addresses en este dispositivo."; /* Message displayed on modal asking for the user to update the password */ -"autofill.update-password.message" = "DuckDuckGo actualizará este inicio de sesión almacenado en tu dispositivo."; +"autofill.update-password.message" = "DuckDuckGo actualizará esta contraseña almacenada en tu dispositivo."; /* Confirm CTA displayed on modal asking for the user to update the password */ "autofill.update-password.save.CTA" = "Actualizar contraseña"; @@ -547,9 +784,21 @@ /* Broken Site Category */ "brokensite.category.videos" = "No se ha reproducido el vídeo"; +/* Broken Site Category Placeholder */ +"brokensite.categoryPlaceholder" = "Elige tu problema en la lista..."; + +/* Broken Site Category Section Title */ +"brokensite.categoryTitle" = "SELECCIONA UNA CATEGORÍA"; + +/* Broken Site Comment Placeholder */ +"brokensite.commentPlaceholder" = "Si compartes más detalles, nos ayudarás a resolver esta incidencia"; + /* Broken Site Section Title */ "brokensite.sectionTitle" = "DESCRIBE QUÉ PASÓ"; +/* No comment provided by engineer. */ +"bucket: %@" = "bucket: %@"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Mes pasado"; @@ -781,6 +1030,39 @@ /* Empty search placeholder on bookmarks search */ "empty.search" = "No se han encontrado coincidencias"; +/* No comment provided by engineer. */ +"Error" = "Error"; + +/* Button title to Sign In */ +"error.email-protection-sign-in.action" = "Iniciar sesión"; + +/* Alert message */ +"error.email-protection-sign-in.body" = "Lo sentimos, vuelve a iniciar sesión para reactivar las funciones de Email Protection en este navegador."; + +/* Alert title */ +"error.email-protection-sign-in.title" = "Error de Email Protection"; + +/* Button title to open device settings */ +"error.insufficient-disk-space.action" = "Abrir ajustes"; + +/* Alert message */ +"error.insufficient-disk-space.body" = "Parece que tu dispositivo se ha quedado sin espacio de almacenamiento. Libera espacio para continuar."; + +/* Alert title */ +"error.insufficient-disk-space.title" = "No hay suficiente espacio de almacenamiento"; + +/* Button title that is shutting down the app */ +"error.preemptive-crash.action" = "Cerrar aplicación"; + +/* Alert message */ +"error.preemptive-crash.body" = "Parece que se ha producido un problema con la aplicación y debe cerrarse. Vuelve a abrirla para continuar."; + +/* Alert title */ +"error.preemptive-crash.title" = "Problema detectado en la aplicación"; + +/* Generic error message on a dialog for when the cause is not known. */ +"error.unknown.try.again" = "An unknown error has occurred"; + /* No comment provided by engineer. */ "favorite" = "Favorito"; @@ -1000,6 +1282,18 @@ /* Home tab title */ "homeTab.title" = "Inicio"; +/* OK title for invite screen alert dismissal button */ +"invite.alert.ok.button" = "OK"; + +/* Continue button on an invite dialog */ +"invite.dialog.continue.button" = "Continue"; + +/* Get Started button on an invite dialog */ +"invite.dialog.get.started.button" = "Get Started"; + +/* Message to show after user enters an unrecognized invite code */ +"invite.dialog.unrecognized.code.message" = "We didn’t recognize this Invite Code."; + /* No comment provided by engineer. */ "keyCommandAddBookmark" = "Añadir marcador"; @@ -1066,15 +1360,15 @@ /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "DuckDuckGo para Mac tiene la velocidad que necesitas, las funciones de navegación que esperas y viene repleta de nuestros mejores Privacy Essentials."; -/* Body text for the macOS waitlist notification */ -"mac-waitlist.available.notification.body" = "Abrir tu invitación"; - /* Title for the macOS waitlist notification */ "mac-waitlist.available.notification.title" = "¡DuckDuckGo para Mac ya está listo!"; /* Title for the copy action */ "mac-waitlist.copy" = "Copiar"; +/* Description text above the Share Link button */ +"mac-waitlist.join-waitlist-screen.on-your-computer-go-to" = "En tu ordenador con Windows, ve a:"; + /* Description text above the Share Link button */ "mac-waitlist.join-waitlist-screen.on-your-mac-go-to" = "En tu Mac, ve a:"; @@ -1087,6 +1381,9 @@ /* Disclaimer for the Join Waitlist screen */ "mac-waitlist.join-waitlist-screen.windows" = "¡Próximamente en Windows!"; +/* Title for the macOS waitlist button redirecting to Windows waitlist */ +"mac-waitlist.join-waitlist-screen.windows-waitlist" = "¿Buscas la versión para Windows?"; + /* Title for the settings subtitle */ "mac-waitlist.settings.browse-privately" = "Navega de forma privada con nuestra aplicación para Mac"; @@ -1097,7 +1394,7 @@ "mac-waitlist.share-sheet.title" = "DuckDuckGo para Mac"; /* Title for the Mac Waitlist feature */ -"mac-waitlist.title" = "Aplicación de escritorio DuckDuckGo"; +"mac-waitlist.title" = "Aplicación DuckDuckGo para Mac"; /* No comment provided by engineer. */ "menu.button.hint" = "Menú de navegación"; @@ -1114,9 +1411,81 @@ /* Edit button */ "navigation.title.edit" = "Editar"; +/* String indicating NetP is connected when viewed from the settings screen */ +"netP.cell.connected" = "Connected"; + +/* String indicating NetP is disconnected when viewed from the settings screen */ +"netP.cell.disconnected" = "Not connected"; + +/* Title for the Network Protection feature */ +"netP.title" = "Network Protection"; + +/* Message for the network protection invite dialog */ +"network.protection.invite.dialog.message" = "Enter your invite code to get started."; + +/* Title for the network protection invite screen */ +"network.protection.invite.dialog.title" = "You're invited to try Network Protection"; + +/* Prompt for the network protection invite code text field */ +"network.protection.invite.field.prompt" = "Invite Code"; + +/* Message explaining that netP is invite only */ +"network.protection.invite.only.message" = "DuckDuckGo Network Protection is currently invite-only."; + +/* Message for the network protection invite success view */ +"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; + +/* Title for the network protection invite success view */ +"network.protection.invite.success.title" = "Success! You’re in."; + +/* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ +"network.protection.status.connected.format" = "Connected - %@"; + +/* The label for the NetP VPN when connecting */ +"network.protection.status.connecting" = "Connecting..."; + +/* The label for the NetP VPN when disconnected */ +"network.protection.status.disconnected" = "Not connected"; + +/* The label for the NetP VPN when disconnecting */ +"network.protection.status.disconnecting" = "Disconnecting..."; + +/* Message label text for the netP status view */ +"network.protection.status.header.message" = "DuckDuckGo's VPN secures all of your device's Internet traffic anytime, anywhere."; + +/* Header title label text for the status view when netP is disconnected */ +"network.protection.status.header.title.off" = "Network Protection is Off"; + +/* Header title label text for the status view when netP is connected */ +"network.protection.status.header.title.on" = "Network Protection is On"; + +/* The status view 'Share Feedback' button which is shown inline on the status view after the \(netPInviteOnlyMessage) text */ +"network.protection.status.menu.share.feedback" = "Share Feedback"; + +/* Connection details label shown in NetworkProtection's status view. */ +"network.protection.status.view.connection.details" = "Connection Details"; + +/* Generic connection failed error message shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.message" = "Please try again later."; + +/* Generic connection failed error title shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; + +/* IP Address label shown in NetworkProtection's status view. */ +"network.protection.status.view.ip.address" = "IP Address"; + +/* Location label shown in NetworkProtection's status view. */ +"network.protection.status.view.location" = "Location"; + +/* Title label text for the status view when netP is disconnected */ +"network.protection.status.view.title" = "Network Protection"; + /* Do not translate - stringsdict entry */ "number.of.tabs" = "number.of.tabs"; +/* No comment provided by engineer. */ +"Ok" = "De acuerdo"; + /* Text displayed on notification appearing in the address bar when the browser dismissed the cookie popup automatically rejecting it */ "omnibar.notification.cookies-managed" = "Cookies gestionadas"; @@ -1156,6 +1525,18 @@ /* ’Link’ is link on a website */ "open.externally.failed" = "No se puede encontrar la aplicación necesaria para abrir ese enlace"; +/* No comment provided by engineer. */ +"Please enter which app on your device is broken." = "Indica la aplicación de tu dispositivo que no funciona."; + +/* Activate button */ +"pm.activate" = "Reactivate"; + +/* Cancel button */ +"pm.cancel" = "Cancel"; + +/* Deactivate button */ +"pm.deactivate" = "Deactivate"; + /* No comment provided by engineer. */ "preserveLogins.domain.list.footer" = "Los sitios web dependen de cookies para mantener tu sesión iniciada. Cuando marcas una web como «a prueba de fuego» no se borrarán las cookies y tu sesión permanecerá iniciada, incluso después de utilizar el botón Fuego. Seguiremos bloqueando rastreadores de terceros que se encuentren en sitios web a prueba de fuego."; @@ -1225,8 +1606,8 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Favoritos"; -/* No comment provided by engineer. */ -"settings.about.text" = "En DuckDuckGo, estamos estableciendo el nuevo estándar de confianza en línea.\n\nEl navegador privado de DuckDuckGo ofrece todos los Privacy Essentials que necesitas para protegerte mientras realizas búsquedas y navegas en Internet, incluyendo bloqueo de rastreadores, cifrado más inteligente y búsquedas privadas de DuckDuckGo.\n\nDespués de todo, Internet no tiene por qué ser tan horrible y disfrutar de la privacidad en línea que mereces debería ser lo más fácil posible."; +/* about page */ +"settings.about.text" = "DuckDuckGo es la empresa de privacidad en internet independiente fundada en 2008 para quienes estén cansados de ser rastreados en línea y quieran una solución sencilla. Somos la prueba de que es posible obtener protección de privacidad real en línea sin concesiones.\n\nEl navegador DuckDuckGo incluye las funciones que esperas de un navegador de referencia, como marcadores, pestañas, contraseñas y más, así como más de [una docena de potentes funciones de protección de privacidad](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/) no se ofrece en la mayoría de los navegadores populares de forma predeterminada. Este conjunto completo de funciones de protección de privacidad ayuda a proteger tu actividad en línea, desde la búsqueda hasta la navegación, el correo electrónico y mucho más.\n\nNuestra protección de privacidad funciona sin necesidad de conocimientos técnicos y sin tener que lidiar con configuraciones complicadas. Lo único que tienes que hacer es cambiar el navegador a DuckDuckGo en todos tus dispositivos y obtener privacidad de forma predeterminada.\n\nSin embargo, si quieres conocer los entresijos, puedes encontrar más información sobre cómo funcionan las protecciones de privacidad de DuckDuckGo en nuestras [páginas de ayuda](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/)."; /* Report a Broken Site screen confirmation button */ "siteFeedback.buttonText" = "Enviar informe"; @@ -1246,6 +1627,9 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "¿Qué sitio web no funciona?"; +/* No comment provided by engineer. */ +"sync.remove-device.message" = "\"%@\" ya no podrá acceder a tus datos sincronizados."; + /* Accessibility label on remove button */ "tab.close.home" = "Cerrar la pestaña inicio"; @@ -1318,6 +1702,57 @@ /* Voice-search footer note with on-device privacy warning */ "voiceSearch.footer.note" = "El audio se procesa en el dispositivo. No se almacena ni comparte con nadie, incluyendo DuckDuckGo."; +/* Title for the button to enable push notifications in system settings */ +"waitlist.allow-notifications" = "Permitir notificaciones"; + +/* Body text for the waitlist notification */ +"waitlist.available.notification.body" = "Abrir tu invitación"; + +/* Title for the copy action */ +"waitlist.copy" = "Copiar"; + +/* Label text for the invite code */ +"waitlist.invite-code" = "Código de invitación"; + +/* Step title on the invite screen */ +"waitlist.invite-screen.step.title" = "Paso %d"; + +/* Title for the invite code screen */ +"waitlist.invite-screen.youre-invited" = "¡Has recibido una invitación!"; + +/* Title for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.join" = "Unirse a la lista de espera privada"; + +/* Temporary status text for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.joining" = "Uniéndote a la lista de espera..."; + +/* Title for the Share Link button */ +"waitlist.join-waitlist-screen.share-link" = "Compartir enlace"; + +/* Notification text for the waitlist */ +"waitlist.joined.no-notification.get-notification" = "NOTIFICARME"; + +/* Title for the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.get-notification-confirmation-title" = "¿Quieres recibir una notificación cuando sea tu turno?"; + +/* Cancel button in the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.no-thanks" = "No, gracias"; + +/* Text used for the Notifications Disabled state */ +"waitlist.notification.disabled" = "Podemos avisarte cuando sea tu turno, pero las notificaciones están actualmente desactivadas para DuckDuckGo."; + +/* Privacy disclaimer for the Waitlist feature */ +"waitlist.privacy-disclaimer" = "No tendrás que compartir ninguna información personal para unirte a la lista de espera. Asegurarás tu lugar en la fila con una marca de tiempo que existe únicamente en tu dispositivo para que podamos notificarte cuando sea tu turno."; + +/* Title for the queue screen */ +"waitlist.queue-screen.on-the-list" = "¡Estás en la lista!"; + +/* Title for the settings subtitle */ +"waitlist.settings.download-available" = "Descarga disponible"; + +/* Title for the share sheet entry */ +"waitlist.share-sheet.title" = "¡Has recibido una invitación!"; + /* Confirmation message */ "web.url.remove.favorite.done" = "Favorito eliminado"; @@ -1342,3 +1777,51 @@ /* Alert title explaining the message is shown by a website */ "webJSAlert.website-message.format" = "Un mensaje de %@:"; +/* No comment provided by engineer. */ +"Welcome to the Duck Side!" = "Bienvenidas al \"Lado Pato\"!"; + +/* Title for the Windows waitlist notification */ +"windows-waitlist.available.notification.title" = "¡Prueba DuckDuckGo para Windows!"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-1.description" = "Visita esta URL en tu dispositivo Windows para descargarlo:"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-2.description" = "Abre el instalador DuckDuckGo en Descargas, selecciona Instalar e introduce tu código de invitación."; + +/* Subtitle for the Windows Waitlist Invite screen */ +"windows-waitlist.invite-screen.subtitle" = "¿Todo listo para usar DuckDuckGo en Windows?"; + +/* Title for the Windows waitlist button redirecting to Mac waitlist */ +"windows-waitlist.join-waitlist-screen.mac-waitlist" = "¿Buscas la versión para Mac?"; + +/* Title for the Join Windows Waitlist screen */ +"windows-waitlist.join-waitlist-screen.try-duckduckgo-for-windows" = "¡Consigue acceso anticipado a la prueba de DuckDuckGo para Windows!"; + +/* Message for the alert to confirm enabling notifications */ +"windows-waitlist.joined.no-notification.get-notification-confirmation-message" = "Te enviaremos una notificación cuando tu copia de DuckDuckGo para Windows esté lista para descargar. "; + +/* Label text for the Joined Waitlist state with notifications declined */ +"windows-waitlist.joined.notifications-declined" = "Tu invitación para probar DuckDuckGo para Windows llegará aquí. Vuelve a consultar pronto, o podemos enviarte una notificación cuando sea tu turno."; + +/* Label text for the Joined Waitlist state with notifications enabled */ +"windows-waitlist.joined.notifications-enabled" = "Te enviaremos una notificación cuando tu copia de DuckDuckGo para Windows esté lista para descargar."; + +/* Title for the settings subtitle */ +"windows-waitlist.settings.browse-privately" = "Navega de forma privada con nuestra aplicación para Windows"; + +/* Message used when sharing to iMessage. Parameter is an eight digit invite code. */ +"windows-waitlist.share-sheet.invite-code-message" = "¡Has recibido una invitación!\n\n¿Listo para usar DuckDuckGo en Windows?\n\nPaso 1\nVisita esta URL en tu ordenador con Windows para la descarga:\nhttps://duckkgogo.com/windows\n\nPaso 2\nAbre el instalador DuckDuckGo en Descargas, selecciona Instalar e introduce tu código de invitación.\n\nCódigo de invitación: %@"; + +/* Message used when sharing to iMessage */ +"windows-waitlist.share-sheet.message" = "¿Todo listo para empezar a navegar de forma privada en Windows?\n\nVisita esta URL en tu ordenador para la descarga: \nhttps://duckduckgo.com/windows"; + +/* Summary text for the Windows browser waitlist */ +"windows-waitlist.summary" = "DuckDuckGo para Windows tiene todo lo que necesitas para navegar con más privacidad: búsqueda privada, bloqueo de rastreadores, encriptación forzada y bloqueo de ventanas emergentes de cookies, además de más protecciones de primera nivel que están por llegar."; + +/* Title for the Windows Waitlist feature */ +"windows-waitlist.title" = "Aplicación DuckDuckGo para Windows"; + +/* Title for the Windows browser download link page */ +"windows-waitlist.waitlist-download-screen.try-duckduckgo-for-windows" = "¡Consigue DuckDuckGo para Windows!"; + diff --git a/DuckDuckGo/es.lproj/Settings.strings b/DuckDuckGo/es.lproj/Settings.strings index 951257e63a..98762d2786 100644 --- a/DuckDuckGo/es.lproj/Settings.strings +++ b/DuckDuckGo/es.lproj/Settings.strings @@ -37,6 +37,9 @@ /* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ "9Ko-0g-T3h.text" = "Tamaño del texto"; +/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ +"9kt-6R-XiZ.text" = "Título"; + /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Abrir aplicación"; @@ -73,6 +76,12 @@ /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Control Global de Privacidad (GPC)"; +/* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ +"CxT-QK-iVn.title" = "Añadir"; + +/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ +"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; + /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Salir de la aplicación (15 minutos de inactividad)"; @@ -151,6 +160,9 @@ /* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ "HLr-R8-xxF.text" = "Vistas previas con pulsación larga"; +/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ +"hoT-Nu-KXP.text" = "Navega de forma privada con nuestra aplicación para Windows"; + /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Protección de privacidad habilitada para todos los sitios"; @@ -163,6 +175,12 @@ /* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ "m23-t6-9cb.text" = "Compartir opiniones"; +/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ +"mLn-1x-Fl5.title" = "Posición de la barra de direcciones"; + +/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ +"mqV-pf-NZ1.text" = "Posición de la barra de direcciones"; + /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Deja que DuckDuckGo gestione las ventanas emergentes de consentimiento de cookies"; @@ -175,15 +193,27 @@ /* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ "oM7-1o-9oY.text" = "Acerca de DuckDuckGo"; +/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ +"opn-JO-idF.text" = "Arriba"; + /* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ "OxE-MQ-uJk.headerTitle" = "Más sobre DuckDuckGo"; +/* Class = "UILabel"; text = "Sync"; ObjectID = "oXN-ez-gct"; */ +"oXN-ez-gct.text" = "Sincronizar"; + /* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ "P0F-ts-ekd.text" = "Navega de forma privada con nuestra aplicación para Mac "; +/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ +"qah-gb-udB.text" = "Network Protection"; + /* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ "qeN-SV-zy7.text" = "Etiqueta"; +/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ +"RQ8-H1-Ez1.text" = "Aplicación DuckDuckGo para Windows"; + /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Tamaño del texto"; @@ -232,8 +262,8 @@ /* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ "yvj-LL-MiR.text" = "Añadir una aplicación a tu Dock"; -/* Class = "UILabel"; text = "DuckDuckGo Desktop App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "Aplicación de escritorio DuckDuckGo"; +/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ +"Yz9-17-qnn.text" = "Aplicación DuckDuckGo para Mac"; /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Nueva pestaña"; diff --git a/DuckDuckGo/et.lproj/Feedback.strings b/DuckDuckGo/et.lproj/Feedback.strings index a7f376dc2a..55e2028121 100644 --- a/DuckDuckGo/et.lproj/Feedback.strings +++ b/DuckDuckGo/et.lproj/Feedback.strings @@ -28,9 +28,6 @@ /* Class = "UIBarButtonItem"; title = "Back"; ObjectID = "JF8-Ow-zXL"; */ "JF8-Ow-zXL.title" = "Tagasi"; -/* Class = "UIBarButtonItem"; title = "Submit"; ObjectID = "jr6-Al-4fr"; */ -"jr6-Al-4fr.title" = "Esita"; - /* Class = "UIButton"; normalTitle = "Submit"; ObjectID = "lsB-Kq-U8p"; */ "lsB-Kq-U8p.normalTitle" = "Esita"; @@ -58,9 +55,6 @@ /* Class = "UINavigationItem"; title = "Report Broken Site"; ObjectID = "WjT-HM-yim"; */ "WjT-HM-yim.title" = "Teata mittetoimivast saidist"; -/* Class = "UILabel"; text = "Broken Site Reason"; ObjectID = "Wx3-Vb-ItS"; */ -"Wx3-Vb-ItS.text" = "Mittetoimiva saidi põhjus"; - /* Class = "UINavigationItem"; title = "Share Feedback"; ObjectID = "Xb0-Zu-opM"; */ "Xb0-Zu-opM.title" = "Jaga tagasisidet"; diff --git a/DuckDuckGo/et.lproj/Localizable.strings b/DuckDuckGo/et.lproj/Localizable.strings index 0299189580..3df8179e8c 100644 --- a/DuckDuckGo/et.lproj/Localizable.strings +++ b/DuckDuckGo/et.lproj/Localizable.strings @@ -1,6 +1,9 @@ /* No comment provided by engineer. */ "%@" = "%@"; +/* No comment provided by engineer. */ +"%@ [%@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)" = "%1$@ [%2$@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)"; + /* Buton label for Edit action */ "action.generic.edit" = "Redigeeri"; @@ -103,6 +106,12 @@ /* Share action */ "action.title.share" = "Jaga"; +/* Settings label for bottom position for the address bar */ +"address.bar.bottom" = "All"; + +/* Settings label for top position for the address bar */ +"address.bar.top" = "Populaarseimad"; + /* No comment provided by engineer. */ "addWidget.button" = "Lisa vidin"; @@ -142,6 +151,225 @@ /* Shown on authentication screen */ "app.authentication.unlock" = "Vabasta DuckDuckGo."; +/* First part of about page content (note the trailing space) */ +"appTP.about.content1" = "You’ve probably heard about companies like Google and Facebook tracking you behind the scenes on third-party websites. But did you know they also track your personal information through apps on your device?\n\nIn 2022, DuckDuckGo found that "; + +/* Second part of about page content (note the trailing space) */ +"appTP.about.content2" = "over 85% of free iOS apps tested contained hidden trackers from other companies."; + +/* Third part of about page content (note the leading space) */ +"appTP.about.content3" = " Of the 395 apps tested, 60% sent data to Google. This happens even while you’re not using your device.\n\nTrackers in apps may have access to a lot more information than their website tracker cousins, such as your location down to which floor of a building you're on, how often you play games while at work, and when and how long you sleep each day. Even if you haven’t given apps explicit permission to collect data, they can still take it without your knowledge.\n\nTracking networks like Facebook and Google use these little pieces of information to build a digital profile about you. With it, tracking networks can manipulate what you see online and allow advertisers to bid on access to you based on your data.\n\nTrackers in apps is a BIG problem for privacy. But DuckDuckGo has a solution that can help.\n\nWhen enabled in the DuckDuckGo Privacy Browser app, App Tracking Protection blocks many trackers in other apps, not just the trackers we find on websites when you browse. These dual layers of protection reduce what companies know about you overall, so you can use your apps with more peace of mind, knowing you’re more protected."; + +/* Navigation Title for AppTP about page */ +"appTP.about.navTitle" = "About App Trackers"; + +/* Title for AppTP about page */ +"appTP.about.title" = "What Are App Trackers?"; + +/* Title for 'Report an Issue' button in the activity view. */ +"appTP.activityView.reportIssue" = "Report Issue"; + +/* Text label for switch that turns blocking on or off for a tracker */ +"appTP.blockTrackerText" = "Block this Tracker"; + +/* Detail string describing what AppTP is */ +"appTP.cell.detail" = "Blokeeri oma seadmes rakenduste jälgurid"; + +/* String indicating AppTP is disabled when viewed from the settings screen */ +"appTP.cell.disabled" = "Keelatud"; + +/* String indicating AppTP is enabled when viewed from the settings screen */ +"appTP.cell.enabled" = "Lubatud"; + +/* Info string informing the user what App Tracking Protection does. */ +"appTP.empty.disabled.info" = "Luba App Tracking Protection, et saaksime blokeerida tüütud jälgurid teistes rakendustes."; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.heading" = "We’re blocking hidden trackers"; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.info" = "Come back soon to see a list of all the app trackers we’ve blocked."; + +/* First answer for AppTP FAQ page */ +"appTP.faq.answer1" = "App Tracking Protection blocks app trackers from other companies, like when Facebook tries to track you in a banking app. Companies may still track you in apps they own."; + +/* Second answer for AppTP FAQ page */ +"appTP.faq.answer2" = "Yes! App Tracking Protection works across all apps on your device to block the most common hidden trackers we find trying to collect your personal info."; + +/* Third answer for AppTP FAQ page */ +"appTP.faq.answer3" = "We currently only block the most common trackers that we find on iOS. This helps us to comprehensively test App Tracking Protection and lower frequency of app breakage, while blocking up to 70% of all tracking requests."; + +/* Fourth answer for AppTP FAQ page */ +"appTP.faq.answer4" = "You’ll be asked to set up a virtual private network (VPN) connection, but you don't need to install a VPN app for App Tracking Protection to work.\n\nThis permission, which works only on your device, allows App Tracking Protection to monitor network traffic so that it can block known trackers."; + +/* Fifth answer for AppTP FAQ page */ +"appTP.faq.answer5" = "You can use App Tracking Protection at the same time as using an IKEv2 protocol VPN app on an iOS device. You won’t be able to use App Tracking Protection on an iOS device if you’re using a VPN app that uses a different type of protocol, like WireGuard or OpenVPN type VPNs."; + +/* Sixth answer for AppTP FAQ page */ +"appTP.faq.answer6" = "A VPN sends your data from the device to its own server, where it secures and anonymizes your data from prying eyes. However, this allows the VPN company to see your network traffic.\n\nApp Tracking Protection is different. Instead of sending your data to a VPN server, App Tracking Protection works only on your device, sitting between your apps and the servers they talk to.\n\nWhenever App Tracking Protection recognizes a known tracker, it blocks the tracker from sending personal information (such as your IP address, activity, and device details) off your device. All other traffic reaches its destination, so your apps work normally."; + +/* Seventh answer for AppTP FAQ page */ +"appTP.faq.answer7" = "App Tracking Protection works only on your device and doesn’t send your data off your device to DuckDuckGo. We don’t collect or store any data from your apps."; + +/* First question for AppTP FAQ page */ +"appTP.faq.question1" = "How does App Tracking Protection work?"; + +/* Second question for AppTP FAQ page */ +"appTP.faq.question2" = "Does App Tracking Protection block trackers in all apps on my device?"; + +/* Third question for AppTP FAQ page */ +"appTP.faq.question3" = "Does App Tracking Protection block all app trackers?"; + +/* Fourth question for AppTP FAQ page */ +"appTP.faq.question4" = "Why does App Tracking Protection use a VPN connection?"; + +/* Fifth question for AppTP FAQ page */ +"appTP.faq.question5" = "Will App Tracking Protection work if I also use a VPN app?"; + +/* Sixth question for AppTP FAQ page */ +"appTP.faq.question6" = "How is App Tracking Protection different from a VPN?"; + +/* Seventh question for AppTP FAQ page */ +"appTP.faq.question7" = "Is my data private?"; + +/* Title for AppTP FAQ page */ +"appTP.faq.title" = "App Tracking Protection FAQ"; + +/* Do not translate. StringsDict entry -- Count part of string 'App Tracking Protection blocked x tracking attempts today' */ +"appTP.home.blockedCount" = "appTP.home.blockedCount"; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the trailing space) */ +"appTP.home.blockedPrefix" = "App Tracking Protection blokeeris "; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the leading space) */ +"appTP.home.blockedSuffix" = " täna sinu rakendustes."; + +/* Prefix of string 'App Tracking Protection disabled. Tap to re-enable.' (note the trailing space) */ +"appTP.home.disabledPrefix" = "App Tracking Protection disabled. "; + +/* Suffix of string 'App Tracking Protection disabled. Tap to re-enable.' */ +"appTP.home.disabledSuffix" = "Tap to continue blocking tracking attempts across your apps."; + +/* Text indicating the tracking event occured 'just now'. Example: Last attempt 'just now' */ +"appTP.justNow" = "Just nüüd"; + +/* View to manage trackers for AppTP. Allows the user to turn trackers on or off. */ +"appTP.manageTrackers" = "Halda jälgureid"; + +/* Button title for AppTP onboarding */ +"appTP.onboarding.continueButton" = "Continue"; + +/* Button title for AppTP onboarding to enable AppTP */ +"appTP.onboarding.enableeButton" = "Enable App Tracking Protection"; + +/* Button title for AppTP onboarding to learn more about AppTP */ +"appTP.onboarding.learnMoreButton" = "Learn More"; + +/* First part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info1" = "Over 85% of free iOS apps"; + +/* Second part of info on the first AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page1Info2" = " we’ve tested allow other companies to track your personal information, even when you’re sleeping."; + +/* Third part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info3" = "See who we catch trying to track you in your apps and take back control."; + +/* First part of info on the second AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page2Info1" = "App Tracking Protection "; + +/* Second part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info2" = "detects and blocks app trackers from other companies,"; + +/* Third part of info on the second AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page2Info3" = " like when Google attempts to track you in a health app."; + +/* Fourth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info4" = "It’s free,"; + +/* Fifth part of info on the second AppTP onboarding page (note the leading and trailing space) */ +"appTP.onboarding.page2Info5" = " and you can enjoy your apps as you normally would. Working in the background, it helps "; + +/* Sixth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info6" = "protect you night and day."; + +/* First part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info1" = "App Tracking Protection is not a VPN."; + +/* Second part of info on the third AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page3Info2" = " However, your device will recognize it as one. This is because it uses a local VPN connection to work."; + +/* Third part of info on the third AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page3Info3" = "App Tracking Protection is different. "; + +/* Fourth part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info4" = "It never routes app data through an external server."; + +/* Title for first AppTP onboarding page */ +"appTP.onboarding.title1" = "One easy step for better app privacy!"; + +/* Title for second AppTP onboarding page */ +"appTP.onboarding.title2" = "How does it work?"; + +/* Title for third AppTP onboarding page */ +"appTP.onboarding.title3" = "Who sees your data?"; + +/* Breakage report app name label */ +"appTP.report.appLabel" = "Which app is having issues?"; + +/* Breakage report app name placeholder */ +"appTP.report.appPlaceholder" = "App name"; + +/* Breakage report category label */ +"appTP.report.categoryLabel" = "Mis toimub?"; + +/* Breakage report comment label */ +"appTP.report.commentLabel" = "Comments"; + +/* Breakage report comment placeholder */ +"appTP.report.commentPlaceholder" = "Add additional details"; + +/* Breakage report footer explaining what is collected in the breakage report */ +"appTP.report.footer" = "In addition to the details entered into this form, your app issue report will contain:\n• A list of trackers blocked in the last 10 min\n• Whether App Tracking Protection is enabled\n• Aggregate DuckDuckGo app diagnostics"; + +/* Breakage report submit button */ +"appTP.report.submit" = "Esita"; + +/* Breakage report form title */ +"appTP.report.title" = "Report Issue"; + +/* Breakage report succcess message */ +"appTP.report.toast" = "Aitäh! Tagasiside on esitatud."; + +/* Cancel button for 'Report an Issue' alert. */ +"appTP.reportAlert.cancel" = "Mitte praegu"; + +/* Confirm button for 'Report an Issue' alert. */ +"appTP.reportAlert.confirm" = "Teata probleemist"; + +/* Message for 'Report an Issue' alert. */ +"appTP.reportAlert.message" = "Let us know if you disabled App Tracking Protection for this specific tracker because it caused app issues. Your feedback helps us improve!"; + +/* Title for 'Report an Issue' alert. */ +"appTP.reportAlert.title" = "Report Issue?"; + +/* Toast notification diplayed after restoring the blocklist to default settings */ +"appTP.restoreDefaultsToast" = "Default settings restored"; + +/* Button to restore the blocklist to its default state. */ +"appTP.restoreDefualts" = "Taasta vaikesätted"; + +/* Title for the App Tracking Protection feature */ +"appTP.title" = "Rakenduse jälgimise kaitse"; + +/* Text indicating when the tracker was last allowed. Example: Last attempt allowed (timeString) */ +"appTP.trackerAllowedTimestamp" = "Viimane lubatud katse %@"; + +/* Text indicating when the tracker was last blocked. Example: Last attempt blocked (timeString) */ +"appTP.trackerBlockedTimestamp" = "Viimane blokeeritud katse %@"; + +/* Do not translate. StringsDict entry -- Subtitle for tracking attempts in App Tracking Protection Activity View. Example: (count) tracking attempts */ +"appTP.trackingattempts" = "appTP.trackingattempts"; + /* Authentication Alert Sign In Button */ "auth.alert.login.button" = "Sisselogimine"; @@ -301,6 +529,18 @@ /* Toast message when a login item without a title is deleted */ "autofill.logins.list.login-deleted-message-no-title" = "Sisselogimisandmed kustutati"; +/* Title for a button that allows a user to reset their list of never saved sites */ +"autofill.logins.list.never.saved" = "Lähtesta välistatud saidid"; + +/* Cancel button for resetting list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.cancel" = "Tühista"; + +/* Confirm button to reset list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.confirm" = "Lähtesta välistatud saidid"; + +/* Alert title */ +"autofill.logins.list.never.saved.reset.action.title" = "Kui lähtestad välistatud saidid, küsitakse järgmisel mõnele neist saitidest sisselogimisel, kas soovid salvestada oma sisselogimisandmed."; + /* Placeholder for search field on autofill login listing */ "autofill.logins.list.search-placeholder" = "Otsi sisselogimisandmeid"; @@ -335,7 +575,7 @@ "autofill.logins.prompt.password.button.title" = "Parool veebisaidi %@ jaoks"; /* Title for autofill login prompt */ -"autofill.logins.prompt.title" = "Kas kasutada salvestatud sisselogimisandmeid?"; +"autofill.logins.prompt.title" = "Kas kasutada salvestatud parooli?"; /* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ "autofill.logins.search.no-results.subtitle" = "otsinguga '%@'"; @@ -385,27 +625,24 @@ /* Title for the alert dialog telling the user an updated username is no longer a private email address */ "autofill.removed.duck.address.title" = "Privaatse Duck Addressi kasutajanimi eemaldati"; +/* CTA displayed on modal asking if the user never wants to be prompted to save a login for this website agin */ +"autofill.save-login.never-prompt.CTA" = "Ära selle saidi kohta rohkem küsi"; + /* Message displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.message" = "Logisid hoitakse turvaliselt sinu seadmes logide menüüs."; +"autofill.save-login.new-user.message" = "Paroole hoitakse turvaliselt sinu seadmes sisselogimisandmete menüüs."; /* Title displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.title" = "Kas soovid, et DuckDuckGo salvestaks sinu sisselogimisandmed?"; +"autofill.save-login.new-user.title" = "Kas soovid, et DuckDuckGo salvestaks sinu parooli?"; /* Cancel CTA displayed on modal asking for the user to save the login */ "autofill.save-login.not-now.CTA" = "Ära salvesta"; -/* Confirm CTA displayed on modal asking for the user to save the login */ -"autofill.save-login.save.CTA" = "Salvesta sisselogimisandmed"; - /* Title displayed on modal asking for the user to save the login */ "autofill.save-login.title" = "Kas salvestada sisselogimisandmed?"; /* Confirm CTA displayed on modal asking for the user to save the password */ "autofill.save-password.save.CTA" = "Salvesta parool"; -/* Title displayed on modal asking for the user to save the password */ -"autofill.save-password.title" = "Kas salvestada parool?"; - /* Accessibility title for a Show Password button displaying actial password instead of ***** */ "autofill.show-password" = "Kuva parool"; @@ -413,7 +650,7 @@ "autofill.signin.to.manage" = "%@, et hallata selles seadmes oma Duck Addresse."; /* Message displayed on modal asking for the user to update the password */ -"autofill.update-password.message" = "DuckDuckGo värskendab need salvestatud sisselogimisandmed sinu seadmes."; +"autofill.update-password.message" = "DuckDuckGo värskendab selle salvestatud parooli sinu seadmes."; /* Confirm CTA displayed on modal asking for the user to update the password */ "autofill.update-password.save.CTA" = "Värskenda parooli"; @@ -547,9 +784,21 @@ /* Broken Site Category */ "brokensite.category.videos" = "Videot ei esitatud"; +/* Broken Site Category Placeholder */ +"brokensite.categoryPlaceholder" = "Vali loendist oma probleem..."; + +/* Broken Site Category Section Title */ +"brokensite.categoryTitle" = "VALI KATEGOORIA"; + +/* Broken Site Comment Placeholder */ +"brokensite.commentPlaceholder" = "Probleemi täpsem kirjeldamine võib meil aidata seda lahendada"; + /* Broken Site Section Title */ "brokensite.sectionTitle" = "KIRJELDAGE TOIMUNUT"; +/* No comment provided by engineer. */ +"bucket: %@" = "andmekogum: %@"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Viimane kuu"; @@ -781,6 +1030,39 @@ /* Empty search placeholder on bookmarks search */ "empty.search" = "Ühtegi vastet ei leitud"; +/* No comment provided by engineer. */ +"Error" = "Viga"; + +/* Button title to Sign In */ +"error.email-protection-sign-in.action" = "Sisselogimine"; + +/* Alert message */ +"error.email-protection-sign-in.body" = "Vabandust, logi uuesti sisse, et Email Protection selles brauseris uuesti lubada."; + +/* Alert title */ +"error.email-protection-sign-in.title" = "Email Protectioni tõrge"; + +/* Button title to open device settings */ +"error.insufficient-disk-space.action" = "Ava seaded"; + +/* Alert message */ +"error.insufficient-disk-space.body" = "Paistab, et sinu seadme salvestusruum on otsa saanud. Jätkamiseks vabasta veidi ruumi."; + +/* Alert title */ +"error.insufficient-disk-space.title" = "Ei ole piisavalt ruumi"; + +/* Button title that is shutting down the app */ +"error.preemptive-crash.action" = "Sule rakendus"; + +/* Alert message */ +"error.preemptive-crash.body" = "Paistab, et rakendusega on probleem ja see tuleb sulgeda. Jätkamiseks ava see uuesti."; + +/* Alert title */ +"error.preemptive-crash.title" = "Tuvastati rakenduse probleem"; + +/* Generic error message on a dialog for when the cause is not known. */ +"error.unknown.try.again" = "An unknown error has occurred"; + /* No comment provided by engineer. */ "favorite" = "Lemmik"; @@ -1000,6 +1282,18 @@ /* Home tab title */ "homeTab.title" = "Avaleht"; +/* OK title for invite screen alert dismissal button */ +"invite.alert.ok.button" = "OK"; + +/* Continue button on an invite dialog */ +"invite.dialog.continue.button" = "Continue"; + +/* Get Started button on an invite dialog */ +"invite.dialog.get.started.button" = "Get Started"; + +/* Message to show after user enters an unrecognized invite code */ +"invite.dialog.unrecognized.code.message" = "We didn’t recognize this Invite Code."; + /* No comment provided by engineer. */ "keyCommandAddBookmark" = "Lisa järjehoidja"; @@ -1066,15 +1360,15 @@ /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "DuckDuckGo for Mac on just nii kiire, kui vajad, oodatud sirvimisfunktsioonidega ja tulvil meie oma klassi parimaid privaatsuselemente."; -/* Body text for the macOS waitlist notification */ -"mac-waitlist.available.notification.body" = "Ava oma kutse"; - /* Title for the macOS waitlist notification */ "mac-waitlist.available.notification.title" = "DuckDuckGo for Mac on valmis."; /* Title for the copy action */ "mac-waitlist.copy" = "Kopeeri"; +/* Description text above the Share Link button */ +"mac-waitlist.join-waitlist-screen.on-your-computer-go-to" = "Ava oma Windowsi arvutis:"; + /* Description text above the Share Link button */ "mac-waitlist.join-waitlist-screen.on-your-mac-go-to" = "Ava oma Macis:"; @@ -1087,6 +1381,9 @@ /* Disclaimer for the Join Waitlist screen */ "mac-waitlist.join-waitlist-screen.windows" = "Windowsi versioon tuleb varsti."; +/* Title for the macOS waitlist button redirecting to Windows waitlist */ +"mac-waitlist.join-waitlist-screen.windows-waitlist" = "Kas otsid Windowsi versiooni?"; + /* Title for the settings subtitle */ "mac-waitlist.settings.browse-privately" = "Sirvi privaatselt meie Maci rakendusega"; @@ -1097,7 +1394,7 @@ "mac-waitlist.share-sheet.title" = "DuckDuckGo for Mac"; /* Title for the Mac Waitlist feature */ -"mac-waitlist.title" = "DuckDuckGo töölauarakendus"; +"mac-waitlist.title" = "DuckDuckGo rakendus Macile"; /* No comment provided by engineer. */ "menu.button.hint" = "Sirvimismenüü"; @@ -1114,9 +1411,81 @@ /* Edit button */ "navigation.title.edit" = "Redigeeri"; +/* String indicating NetP is connected when viewed from the settings screen */ +"netP.cell.connected" = "Connected"; + +/* String indicating NetP is disconnected when viewed from the settings screen */ +"netP.cell.disconnected" = "Not connected"; + +/* Title for the Network Protection feature */ +"netP.title" = "Network Protection"; + +/* Message for the network protection invite dialog */ +"network.protection.invite.dialog.message" = "Enter your invite code to get started."; + +/* Title for the network protection invite screen */ +"network.protection.invite.dialog.title" = "You're invited to try Network Protection"; + +/* Prompt for the network protection invite code text field */ +"network.protection.invite.field.prompt" = "Invite Code"; + +/* Message explaining that netP is invite only */ +"network.protection.invite.only.message" = "DuckDuckGo Network Protection is currently invite-only."; + +/* Message for the network protection invite success view */ +"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; + +/* Title for the network protection invite success view */ +"network.protection.invite.success.title" = "Success! You’re in."; + +/* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ +"network.protection.status.connected.format" = "Connected - %@"; + +/* The label for the NetP VPN when connecting */ +"network.protection.status.connecting" = "Connecting..."; + +/* The label for the NetP VPN when disconnected */ +"network.protection.status.disconnected" = "Not connected"; + +/* The label for the NetP VPN when disconnecting */ +"network.protection.status.disconnecting" = "Disconnecting..."; + +/* Message label text for the netP status view */ +"network.protection.status.header.message" = "DuckDuckGo's VPN secures all of your device's Internet traffic anytime, anywhere."; + +/* Header title label text for the status view when netP is disconnected */ +"network.protection.status.header.title.off" = "Network Protection is Off"; + +/* Header title label text for the status view when netP is connected */ +"network.protection.status.header.title.on" = "Network Protection is On"; + +/* The status view 'Share Feedback' button which is shown inline on the status view after the \(netPInviteOnlyMessage) text */ +"network.protection.status.menu.share.feedback" = "Share Feedback"; + +/* Connection details label shown in NetworkProtection's status view. */ +"network.protection.status.view.connection.details" = "Connection Details"; + +/* Generic connection failed error message shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.message" = "Please try again later."; + +/* Generic connection failed error title shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; + +/* IP Address label shown in NetworkProtection's status view. */ +"network.protection.status.view.ip.address" = "IP Address"; + +/* Location label shown in NetworkProtection's status view. */ +"network.protection.status.view.location" = "Location"; + +/* Title label text for the status view when netP is disconnected */ +"network.protection.status.view.title" = "Network Protection"; + /* Do not translate - stringsdict entry */ "number.of.tabs" = "number.of.tabs"; +/* No comment provided by engineer. */ +"Ok" = "OK"; + /* Text displayed on notification appearing in the address bar when the browser dismissed the cookie popup automatically rejecting it */ "omnibar.notification.cookies-managed" = "Hallatavad küpsised"; @@ -1156,6 +1525,18 @@ /* ’Link’ is link on a website */ "open.externally.failed" = "Selle lingi avamiseks vajalikku rakendust ei leitud"; +/* No comment provided by engineer. */ +"Please enter which app on your device is broken." = "Kirjuta, milline rakendus on sinu seadmes katki."; + +/* Activate button */ +"pm.activate" = "Reactivate"; + +/* Cancel button */ +"pm.cancel" = "Cancel"; + +/* Deactivate button */ +"pm.deactivate" = "Deactivate"; + /* No comment provided by engineer. */ "preserveLogins.domain.list.footer" = "Veebisaidid kasutavad küpsiseid, et hoida sind sisse logitud. Kui teed saidi tulekindlaks, ei kustutata küpsiseid ja sa jääd sisse logituks isegi pärast tulenupu kasutamist. Me blokeerime endiselt kolmanda poole jälitajad, kelle me tulekindlatelt lehtedelt leiame."; @@ -1225,8 +1606,8 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Lemmikud"; -/* No comment provided by engineer. */ -"settings.about.text" = "DuckDuckGo on veebi usaldusväärsuse uus standard.\n \nDuckDuckGo privaatsusbrauser pakub kõiki privaatsuse põhifunktsioone, mida peate enda kaitsmiseks veebist otsides ja sirvides kasutama, sealhulgas jälgijate blokeerimist, nutikamat krüptimist ja DuckDuckGo privaatset otsingut.\n\nLõppude lõpuks ei tohiks Internet nii jube olla ja Internetis väärilise privaatsuse saavutamine peaks olema sama lihtne kui ruloo alla tõmbamine."; +/* about page */ +"settings.about.text" = "DuckDuckGo on 2008. aastal asutatud sõltumatu internetiprivaatsuse ettevõte igaühele, kellel on kõrini varjatud jälgimisest internetis ja kes soovib lihtsat lahendust selle vältimiseks. Me oleme tõestuseks, et internetis on võimalik saada tõelist privaatsuskaitset ilma kompromisse tegemata.\n\nDuckDuckGo brauseril on kõik funktsioonid, mida sa brauserilt ootad, nagu järjehoidjad, vahekaardid, paroolid ja palju muud, lisaks sellele aga ka üle [tosina võimsa privaatsuskaitse funktsiooni](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/), mida enamik populaarsemaid brausereid vaikimisi ei paku. See ainulaadselt terviklik privaatsuskaitse aitab kaitsta sinu tegevust internetis alates otsingutest kuni sirvimiseni, meilide saatmiseni ja palju muuni.\n\nMeie privaatsuskaitse toimib ilma, et peaksid teadma midagi tehnilistest nüanssidest või tegelema keeruliste seadistustega. Ainus, mis sa pead tegema, on vahetada oma brauser kõigis seadmetes DuckDuckGo vastu – ja vaikeprivaatsus ongi tagatud.\n\nKui aga *soovid* piiluda kapoti alla, leiate lisateavet DuckDuckGo privaatsuskaitse tööpõhimõtete kohta meie [abilehtedelt](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/)."; /* Report a Broken Site screen confirmation button */ "siteFeedback.buttonText" = "Saada aruanne"; @@ -1246,6 +1627,9 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Milline veebisait on katki?"; +/* No comment provided by engineer. */ +"sync.remove-device.message" = "%@ ei pääse enam sinu sünkroonitud andmetele juurde."; + /* Accessibility label on remove button */ "tab.close.home" = "Sulge avalehe vahekaart"; @@ -1318,6 +1702,57 @@ /* Voice-search footer note with on-device privacy warning */ "voiceSearch.footer.note" = "Heli töödeldakse seadmes. Seda ei salvestata ega jagata kellegagi, sealhulgas DuckDuckGoga."; +/* Title for the button to enable push notifications in system settings */ +"waitlist.allow-notifications" = "Luba teavitused"; + +/* Body text for the waitlist notification */ +"waitlist.available.notification.body" = "Ava oma kutse"; + +/* Title for the copy action */ +"waitlist.copy" = "Kopeeri"; + +/* Label text for the invite code */ +"waitlist.invite-code" = "Kutsekood"; + +/* Step title on the invite screen */ +"waitlist.invite-screen.step.title" = "Samm %d"; + +/* Title for the invite code screen */ +"waitlist.invite-screen.youre-invited" = "Oled kutsutud!"; + +/* Title for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.join" = "Liituge privaatse ootenimekirjaga"; + +/* Temporary status text for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.joining" = "Ootenimekirjaga liitumine ..."; + +/* Title for the Share Link button */ +"waitlist.join-waitlist-screen.share-link" = "Jaga linki"; + +/* Notification text for the waitlist */ +"waitlist.joined.no-notification.get-notification" = "TEAVITAGE MIND"; + +/* Title for the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.get-notification-confirmation-title" = "Kas soovid saada teavituse, kui on sinu kord?"; + +/* Cancel button in the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.no-thanks" = "Ei aitäh"; + +/* Text used for the Notifications Disabled state */ +"waitlist.notification.disabled" = "Võime sind teavitada, kui on sinu kord aga DuckDuckGo jaoks on teavitused praegu keelatud."; + +/* Privacy disclaimer for the Waitlist feature */ +"waitlist.privacy-disclaimer" = "Ootenimekirjaga liitumiseks ei pea isikuandmeid jagama. Sinu järjekorrakoht on kinnitatud ainult sinu seadmes oleva ajatempliga ja me teavitame sind, kui on sinu kord."; + +/* Title for the queue screen */ +"waitlist.queue-screen.on-the-list" = "Oled ootenimekirjas!"; + +/* Title for the settings subtitle */ +"waitlist.settings.download-available" = "Saadaval on allalaadimine"; + +/* Title for the share sheet entry */ +"waitlist.share-sheet.title" = "Oled kutsutud!"; + /* Confirmation message */ "web.url.remove.favorite.done" = "Lemmik on eemaldatud"; @@ -1342,3 +1777,51 @@ /* Alert title explaining the message is shown by a website */ "webJSAlert.website-message.format" = "Sõnum saatjalt %@:"; +/* No comment provided by engineer. */ +"Welcome to the Duck Side!" = "Tere tulemast Pardipoolele!"; + +/* Title for the Windows waitlist notification */ +"windows-waitlist.available.notification.title" = "Proovi rakendust DuckDuckGo for Windows!"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-1.description" = "Allalaadimiseks ava oma Windowsi seadmes see URL:"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-2.description" = "Ava allalaadimiste kaustas DuckDuckGo Installer, vali Installi ja sisesta seejärel oma kutsekood."; + +/* Subtitle for the Windows Waitlist Invite screen */ +"windows-waitlist.invite-screen.subtitle" = "Kas oled valmis kasutama DuckDuckGo Windowsi rakendust?"; + +/* Title for the Windows waitlist button redirecting to Mac waitlist */ +"windows-waitlist.join-waitlist-screen.mac-waitlist" = "Kas otsid Maci versiooni?"; + +/* Title for the Join Windows Waitlist screen */ +"windows-waitlist.join-waitlist-screen.try-duckduckgo-for-windows" = "Hangi varane juurdepääs, et proovida DuckDuckGo Windowsi rakendust!"; + +/* Message for the alert to confirm enabling notifications */ +"windows-waitlist.joined.no-notification.get-notification-confirmation-message" = "Saadame sulle teavituse, kui sinu DuckDuckGo for Windows on allalaadimiseks valmis. "; + +/* Label text for the Joined Waitlist state with notifications declined */ +"windows-waitlist.joined.notifications-declined" = "Sinu kutse DuckDuckGo Maci rakenduse proovimiseks saabub siia. Vaata varsti tagasi või telli teavitus, millal on sinu kord."; + +/* Label text for the Joined Waitlist state with notifications enabled */ +"windows-waitlist.joined.notifications-enabled" = "Saadame sulle teavituse, kui sinu DuckDuckGo for Windows on allalaadimiseks valmis."; + +/* Title for the settings subtitle */ +"windows-waitlist.settings.browse-privately" = "Sirvi privaatselt meie Windowsi rakendusega"; + +/* Message used when sharing to iMessage. Parameter is an eight digit invite code. */ +"windows-waitlist.share-sheet.invite-code-message" = "Oled kutsutud!\n\nKas oled valmis kasutama DuckDuckGo Windowsi rakendust?Samm 1\nAva allalaadimiseks oma Windowsi seadmes see URL:\nhttps://duckduckgo.com/windows\n\nSamm 2\nAva allalaadimiste kaustas DuckDuckGo Installer, vali Installi ja sisesta seejärel oma kutsekood.\n\nKutsekood: %@"; + +/* Message used when sharing to iMessage */ +"windows-waitlist.share-sheet.message" = "Kas oled valmis Windowsis privaatseks sirvimiseks?\n\nAllalaadimiseks ava oma Windowsi arvutis see URL:\nhttps://duckduckgo.com/windows"; + +/* Summary text for the Windows browser waitlist */ +"windows-waitlist.summary" = "DuckDuckGo Windowsi rakendus sisaldab kõike vajalikku privaatsemaks sirvimiseks: privaatne otsing, jälgurite blokeerimine, sundkrüptimine ja küpsiste hüpikakende blokeerimine. Lisaks on tulemas veel täiendavaid tippklassi kaitsemeetmeid."; + +/* Title for the Windows Waitlist feature */ +"windows-waitlist.title" = "DuckDuckGo rakendus Windowsile"; + +/* Title for the Windows browser download link page */ +"windows-waitlist.waitlist-download-screen.try-duckduckgo-for-windows" = "Hangi DuckDuckGo for Windows!"; + diff --git a/DuckDuckGo/et.lproj/Settings.strings b/DuckDuckGo/et.lproj/Settings.strings index 308e6cfdf2..5cb1f1e951 100644 --- a/DuckDuckGo/et.lproj/Settings.strings +++ b/DuckDuckGo/et.lproj/Settings.strings @@ -37,6 +37,9 @@ /* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ "9Ko-0g-T3h.text" = "Teksti suurus"; +/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ +"9kt-6R-XiZ.text" = "Pealkiri"; + /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Rakenduse käivitamine"; @@ -73,6 +76,12 @@ /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Üleilmne privaatsuskontroll (Global Privacy Control (GPC))"; +/* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ +"CxT-QK-iVn.title" = "Lisa"; + +/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ +"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; + /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Rakendusest väljumisel, 15-minutilise passiivsuse järel"; @@ -151,6 +160,9 @@ /* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ "HLr-R8-xxF.text" = "Pika vajutusega eelvaated"; +/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ +"hoT-Nu-KXP.text" = "Sirvi privaatselt meie Windowsi rakendusega"; + /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Privaatsuse kaitse on lubatud kõigil saitidel"; @@ -163,6 +175,12 @@ /* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ "m23-t6-9cb.text" = "Jaga tagasisidet"; +/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ +"mLn-1x-Fl5.title" = "Aadressiriba asukoht"; + +/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ +"mqV-pf-NZ1.text" = "Aadressiriba asukoht"; + /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Luba DuckDuckGo'l hallata küpsiste nõusoleku hüpikaknaid"; @@ -175,15 +193,27 @@ /* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ "oM7-1o-9oY.text" = "DuckDuckGo'st"; +/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ +"opn-JO-idF.text" = "Populaarseimad"; + /* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ "OxE-MQ-uJk.headerTitle" = "Veel DuckDuckGo'lt"; +/* Class = "UILabel"; text = "Sync"; ObjectID = "oXN-ez-gct"; */ +"oXN-ez-gct.text" = "Sünkrooni"; + /* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ "P0F-ts-ekd.text" = "Sirvi privaatselt meie Maci rakendusega "; +/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ +"qah-gb-udB.text" = "Network Protection"; + /* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ "qeN-SV-zy7.text" = "Silt"; +/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ +"RQ8-H1-Ez1.text" = "DuckDuckGo Windowsi rakendus"; + /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Teksti suurus"; @@ -232,8 +262,8 @@ /* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ "yvj-LL-MiR.text" = "Lisa rakendus oma dokki"; -/* Class = "UILabel"; text = "DuckDuckGo Desktop App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "DuckDuckGo töölauarakendus"; +/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ +"Yz9-17-qnn.text" = "DuckDuckGo Maci rakendus"; /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Uus vaheleht"; diff --git a/DuckDuckGo/fi.lproj/Feedback.strings b/DuckDuckGo/fi.lproj/Feedback.strings index ae31a9e68b..aef969e8ca 100644 --- a/DuckDuckGo/fi.lproj/Feedback.strings +++ b/DuckDuckGo/fi.lproj/Feedback.strings @@ -28,9 +28,6 @@ /* Class = "UIBarButtonItem"; title = "Back"; ObjectID = "JF8-Ow-zXL"; */ "JF8-Ow-zXL.title" = "Takaisin"; -/* Class = "UIBarButtonItem"; title = "Submit"; ObjectID = "jr6-Al-4fr"; */ -"jr6-Al-4fr.title" = "Lähetä"; - /* Class = "UIButton"; normalTitle = "Submit"; ObjectID = "lsB-Kq-U8p"; */ "lsB-Kq-U8p.normalTitle" = "Lähetä"; @@ -58,9 +55,6 @@ /* Class = "UINavigationItem"; title = "Report Broken Site"; ObjectID = "WjT-HM-yim"; */ "WjT-HM-yim.title" = "Ilmoita viallisesta sivustosta"; -/* Class = "UILabel"; text = "Broken Site Reason"; ObjectID = "Wx3-Vb-ItS"; */ -"Wx3-Vb-ItS.text" = "Rikkinäisen sivuston syy"; - /* Class = "UINavigationItem"; title = "Share Feedback"; ObjectID = "Xb0-Zu-opM"; */ "Xb0-Zu-opM.title" = "Jaa palaute"; diff --git a/DuckDuckGo/fi.lproj/Localizable.strings b/DuckDuckGo/fi.lproj/Localizable.strings index f5e0fab46e..2f5ff76343 100644 --- a/DuckDuckGo/fi.lproj/Localizable.strings +++ b/DuckDuckGo/fi.lproj/Localizable.strings @@ -1,6 +1,9 @@ /* No comment provided by engineer. */ "%@" = "%@"; +/* No comment provided by engineer. */ +"%@ [%@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)" = "%1$@ [%2$@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)"; + /* Buton label for Edit action */ "action.generic.edit" = "Muokkaa"; @@ -103,6 +106,12 @@ /* Share action */ "action.title.share" = "Jaa"; +/* Settings label for bottom position for the address bar */ +"address.bar.bottom" = "Alareuna"; + +/* Settings label for top position for the address bar */ +"address.bar.top" = "Suosituin"; + /* No comment provided by engineer. */ "addWidget.button" = "Lisää widget"; @@ -142,6 +151,225 @@ /* Shown on authentication screen */ "app.authentication.unlock" = "Avaa DuckDuckGo."; +/* First part of about page content (note the trailing space) */ +"appTP.about.content1" = "You’ve probably heard about companies like Google and Facebook tracking you behind the scenes on third-party websites. But did you know they also track your personal information through apps on your device?\n\nIn 2022, DuckDuckGo found that "; + +/* Second part of about page content (note the trailing space) */ +"appTP.about.content2" = "over 85% of free iOS apps tested contained hidden trackers from other companies."; + +/* Third part of about page content (note the leading space) */ +"appTP.about.content3" = " Of the 395 apps tested, 60% sent data to Google. This happens even while you’re not using your device.\n\nTrackers in apps may have access to a lot more information than their website tracker cousins, such as your location down to which floor of a building you're on, how often you play games while at work, and when and how long you sleep each day. Even if you haven’t given apps explicit permission to collect data, they can still take it without your knowledge.\n\nTracking networks like Facebook and Google use these little pieces of information to build a digital profile about you. With it, tracking networks can manipulate what you see online and allow advertisers to bid on access to you based on your data.\n\nTrackers in apps is a BIG problem for privacy. But DuckDuckGo has a solution that can help.\n\nWhen enabled in the DuckDuckGo Privacy Browser app, App Tracking Protection blocks many trackers in other apps, not just the trackers we find on websites when you browse. These dual layers of protection reduce what companies know about you overall, so you can use your apps with more peace of mind, knowing you’re more protected."; + +/* Navigation Title for AppTP about page */ +"appTP.about.navTitle" = "About App Trackers"; + +/* Title for AppTP about page */ +"appTP.about.title" = "What Are App Trackers?"; + +/* Title for 'Report an Issue' button in the activity view. */ +"appTP.activityView.reportIssue" = "Report Issue"; + +/* Text label for switch that turns blocking on or off for a tracker */ +"appTP.blockTrackerText" = "Block this Tracker"; + +/* Detail string describing what AppTP is */ +"appTP.cell.detail" = "Estä sovellusseuranta laitteessa"; + +/* String indicating AppTP is disabled when viewed from the settings screen */ +"appTP.cell.disabled" = "Pois käytöstä"; + +/* String indicating AppTP is enabled when viewed from the settings screen */ +"appTP.cell.enabled" = "Käytössä"; + +/* Info string informing the user what App Tracking Protection does. */ +"appTP.empty.disabled.info" = "Ota App Tracking Protection käyttöön, jotta voimme estää muiden sovellusten ärsyttävät seurantaohjelmat."; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.heading" = "We’re blocking hidden trackers"; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.info" = "Come back soon to see a list of all the app trackers we’ve blocked."; + +/* First answer for AppTP FAQ page */ +"appTP.faq.answer1" = "App Tracking Protection blocks app trackers from other companies, like when Facebook tries to track you in a banking app. Companies may still track you in apps they own."; + +/* Second answer for AppTP FAQ page */ +"appTP.faq.answer2" = "Yes! App Tracking Protection works across all apps on your device to block the most common hidden trackers we find trying to collect your personal info."; + +/* Third answer for AppTP FAQ page */ +"appTP.faq.answer3" = "We currently only block the most common trackers that we find on iOS. This helps us to comprehensively test App Tracking Protection and lower frequency of app breakage, while blocking up to 70% of all tracking requests."; + +/* Fourth answer for AppTP FAQ page */ +"appTP.faq.answer4" = "You’ll be asked to set up a virtual private network (VPN) connection, but you don't need to install a VPN app for App Tracking Protection to work.\n\nThis permission, which works only on your device, allows App Tracking Protection to monitor network traffic so that it can block known trackers."; + +/* Fifth answer for AppTP FAQ page */ +"appTP.faq.answer5" = "You can use App Tracking Protection at the same time as using an IKEv2 protocol VPN app on an iOS device. You won’t be able to use App Tracking Protection on an iOS device if you’re using a VPN app that uses a different type of protocol, like WireGuard or OpenVPN type VPNs."; + +/* Sixth answer for AppTP FAQ page */ +"appTP.faq.answer6" = "A VPN sends your data from the device to its own server, where it secures and anonymizes your data from prying eyes. However, this allows the VPN company to see your network traffic.\n\nApp Tracking Protection is different. Instead of sending your data to a VPN server, App Tracking Protection works only on your device, sitting between your apps and the servers they talk to.\n\nWhenever App Tracking Protection recognizes a known tracker, it blocks the tracker from sending personal information (such as your IP address, activity, and device details) off your device. All other traffic reaches its destination, so your apps work normally."; + +/* Seventh answer for AppTP FAQ page */ +"appTP.faq.answer7" = "App Tracking Protection works only on your device and doesn’t send your data off your device to DuckDuckGo. We don’t collect or store any data from your apps."; + +/* First question for AppTP FAQ page */ +"appTP.faq.question1" = "How does App Tracking Protection work?"; + +/* Second question for AppTP FAQ page */ +"appTP.faq.question2" = "Does App Tracking Protection block trackers in all apps on my device?"; + +/* Third question for AppTP FAQ page */ +"appTP.faq.question3" = "Does App Tracking Protection block all app trackers?"; + +/* Fourth question for AppTP FAQ page */ +"appTP.faq.question4" = "Why does App Tracking Protection use a VPN connection?"; + +/* Fifth question for AppTP FAQ page */ +"appTP.faq.question5" = "Will App Tracking Protection work if I also use a VPN app?"; + +/* Sixth question for AppTP FAQ page */ +"appTP.faq.question6" = "How is App Tracking Protection different from a VPN?"; + +/* Seventh question for AppTP FAQ page */ +"appTP.faq.question7" = "Is my data private?"; + +/* Title for AppTP FAQ page */ +"appTP.faq.title" = "App Tracking Protection FAQ"; + +/* Do not translate. StringsDict entry -- Count part of string 'App Tracking Protection blocked x tracking attempts today' */ +"appTP.home.blockedCount" = "appTP.home.blockedCount"; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the trailing space) */ +"appTP.home.blockedPrefix" = "App Tracking Protection estetty "; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the leading space) */ +"appTP.home.blockedSuffix" = " sovelluksissasi tänään."; + +/* Prefix of string 'App Tracking Protection disabled. Tap to re-enable.' (note the trailing space) */ +"appTP.home.disabledPrefix" = "App Tracking Protection disabled. "; + +/* Suffix of string 'App Tracking Protection disabled. Tap to re-enable.' */ +"appTP.home.disabledSuffix" = "Tap to continue blocking tracking attempts across your apps."; + +/* Text indicating the tracking event occured 'just now'. Example: Last attempt 'just now' */ +"appTP.justNow" = "Juuri nyt"; + +/* View to manage trackers for AppTP. Allows the user to turn trackers on or off. */ +"appTP.manageTrackers" = "Hallitse seurantaohjelmia"; + +/* Button title for AppTP onboarding */ +"appTP.onboarding.continueButton" = "Continue"; + +/* Button title for AppTP onboarding to enable AppTP */ +"appTP.onboarding.enableeButton" = "Enable App Tracking Protection"; + +/* Button title for AppTP onboarding to learn more about AppTP */ +"appTP.onboarding.learnMoreButton" = "Learn More"; + +/* First part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info1" = "Over 85% of free iOS apps"; + +/* Second part of info on the first AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page1Info2" = " we’ve tested allow other companies to track your personal information, even when you’re sleeping."; + +/* Third part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info3" = "See who we catch trying to track you in your apps and take back control."; + +/* First part of info on the second AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page2Info1" = "App Tracking Protection "; + +/* Second part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info2" = "detects and blocks app trackers from other companies,"; + +/* Third part of info on the second AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page2Info3" = " like when Google attempts to track you in a health app."; + +/* Fourth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info4" = "It’s free,"; + +/* Fifth part of info on the second AppTP onboarding page (note the leading and trailing space) */ +"appTP.onboarding.page2Info5" = " and you can enjoy your apps as you normally would. Working in the background, it helps "; + +/* Sixth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info6" = "protect you night and day."; + +/* First part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info1" = "App Tracking Protection is not a VPN."; + +/* Second part of info on the third AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page3Info2" = " However, your device will recognize it as one. This is because it uses a local VPN connection to work."; + +/* Third part of info on the third AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page3Info3" = "App Tracking Protection is different. "; + +/* Fourth part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info4" = "It never routes app data through an external server."; + +/* Title for first AppTP onboarding page */ +"appTP.onboarding.title1" = "One easy step for better app privacy!"; + +/* Title for second AppTP onboarding page */ +"appTP.onboarding.title2" = "How does it work?"; + +/* Title for third AppTP onboarding page */ +"appTP.onboarding.title3" = "Who sees your data?"; + +/* Breakage report app name label */ +"appTP.report.appLabel" = "Which app is having issues?"; + +/* Breakage report app name placeholder */ +"appTP.report.appPlaceholder" = "App name"; + +/* Breakage report category label */ +"appTP.report.categoryLabel" = "Mitä tapahtuu?"; + +/* Breakage report comment label */ +"appTP.report.commentLabel" = "Comments"; + +/* Breakage report comment placeholder */ +"appTP.report.commentPlaceholder" = "Add additional details"; + +/* Breakage report footer explaining what is collected in the breakage report */ +"appTP.report.footer" = "In addition to the details entered into this form, your app issue report will contain:\n• A list of trackers blocked in the last 10 min\n• Whether App Tracking Protection is enabled\n• Aggregate DuckDuckGo app diagnostics"; + +/* Breakage report submit button */ +"appTP.report.submit" = "Lähetä"; + +/* Breakage report form title */ +"appTP.report.title" = "Report Issue"; + +/* Breakage report succcess message */ +"appTP.report.toast" = "Kiitos! Palaute lähetetty."; + +/* Cancel button for 'Report an Issue' alert. */ +"appTP.reportAlert.cancel" = "Ei nyt"; + +/* Confirm button for 'Report an Issue' alert. */ +"appTP.reportAlert.confirm" = "Ilmoita ongelmasta"; + +/* Message for 'Report an Issue' alert. */ +"appTP.reportAlert.message" = "Let us know if you disabled App Tracking Protection for this specific tracker because it caused app issues. Your feedback helps us improve!"; + +/* Title for 'Report an Issue' alert. */ +"appTP.reportAlert.title" = "Report Issue?"; + +/* Toast notification diplayed after restoring the blocklist to default settings */ +"appTP.restoreDefaultsToast" = "Default settings restored"; + +/* Button to restore the blocklist to its default state. */ +"appTP.restoreDefualts" = "Palauta oletusasetukset"; + +/* Title for the App Tracking Protection feature */ +"appTP.title" = "Sovellusten seurantasuojaus"; + +/* Text indicating when the tracker was last allowed. Example: Last attempt allowed (timeString) */ +"appTP.trackerAllowedTimestamp" = "Viimeisin sallittu yritys %@"; + +/* Text indicating when the tracker was last blocked. Example: Last attempt blocked (timeString) */ +"appTP.trackerBlockedTimestamp" = "Viimeisin estetty yritys %@"; + +/* Do not translate. StringsDict entry -- Subtitle for tracking attempts in App Tracking Protection Activity View. Example: (count) tracking attempts */ +"appTP.trackingattempts" = "appTP.trackingattempts"; + /* Authentication Alert Sign In Button */ "auth.alert.login.button" = "Kirjaudu sisään"; @@ -301,6 +529,18 @@ /* Toast message when a login item without a title is deleted */ "autofill.logins.list.login-deleted-message-no-title" = "Kirjautumistieto poistettu"; +/* Title for a button that allows a user to reset their list of never saved sites */ +"autofill.logins.list.never.saved" = "Nollaa poissuljetut sivustot"; + +/* Cancel button for resetting list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.cancel" = "Peruuta"; + +/* Confirm button to reset list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.confirm" = "Nollaa poissuljetut sivustot"; + +/* Alert title */ +"autofill.logins.list.never.saved.reset.action.title" = "Jos nollaat poissuljetut sivustot, sinua pyydetään tallentamaan kirjautumistietosi, kun seuraavan kerran kirjaudut sisään jollekin näistä sivustoista."; + /* Placeholder for search field on autofill login listing */ "autofill.logins.list.search-placeholder" = "Hae kirjautumistietoja"; @@ -335,7 +575,7 @@ "autofill.logins.prompt.password.button.title" = "Sivuston %@ salasana"; /* Title for autofill login prompt */ -"autofill.logins.prompt.title" = "Käytä tallennettuja kirjautumistietoja?"; +"autofill.logins.prompt.title" = "Käytetäänkö tallennettua salasanaa?"; /* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ "autofill.logins.search.no-results.subtitle" = "kohteelle '%@'"; @@ -385,27 +625,24 @@ /* Title for the alert dialog telling the user an updated username is no longer a private email address */ "autofill.removed.duck.address.title" = "Yksityinen Duck Address -käyttäjätunnus on poistettu"; +/* CTA displayed on modal asking if the user never wants to be prompted to save a login for this website agin */ +"autofill.save-login.never-prompt.CTA" = "Älä kysy enää tällä sivustolla"; + /* Message displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.message" = "Kirjautumistiedot tallennetaan turvallisesti laitteellesi kirjautumistiedot-valikkoon."; +"autofill.save-login.new-user.message" = "Salasanat tallennetaan turvallisesti laitteellesi kirjautumistiedot-valikkoon."; /* Title displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.title" = "Haluatko, että DuckDuckGo tallentaa kirjautumistietosi?"; +"autofill.save-login.new-user.title" = "Haluatko, että DuckDuckGo tallentaa salasanasi?"; /* Cancel CTA displayed on modal asking for the user to save the login */ "autofill.save-login.not-now.CTA" = "Älä tallenna"; -/* Confirm CTA displayed on modal asking for the user to save the login */ -"autofill.save-login.save.CTA" = "Tallenna kirjautumistiedot"; - /* Title displayed on modal asking for the user to save the login */ "autofill.save-login.title" = "Tallennetaanko kirjautumistiedot?"; /* Confirm CTA displayed on modal asking for the user to save the password */ "autofill.save-password.save.CTA" = "Tallenna salasana"; -/* Title displayed on modal asking for the user to save the password */ -"autofill.save-password.title" = "Tallennetaanko salasana?"; - /* Accessibility title for a Show Password button displaying actial password instead of ***** */ "autofill.show-password" = "Näytä salasana"; @@ -413,7 +650,7 @@ "autofill.signin.to.manage" = "%@ hallitaksesi Duck Address -osoitteita tällä laitteella."; /* Message displayed on modal asking for the user to update the password */ -"autofill.update-password.message" = "DuckDuckGo päivittää tämän laitteellesi tallennetun kirjautumistiedon."; +"autofill.update-password.message" = "DuckDuckGo päivittää tämän tallennetun salasanan laitteellesi."; /* Confirm CTA displayed on modal asking for the user to update the password */ "autofill.update-password.save.CTA" = "Päivitä salasana"; @@ -547,9 +784,21 @@ /* Broken Site Category */ "brokensite.category.videos" = "Videota ei toistettu"; +/* Broken Site Category Placeholder */ +"brokensite.categoryPlaceholder" = "Valitse ongelma luettelosta..."; + +/* Broken Site Category Section Title */ +"brokensite.categoryTitle" = "VALITSE KATEGORIA"; + +/* Broken Site Comment Placeholder */ +"brokensite.commentPlaceholder" = "Lisätiedot saattavat auttaa meitä ratkaisemaan tämän ongelman"; + /* Broken Site Section Title */ "brokensite.sectionTitle" = "KUVAILE MITÄ TAPAHTUI"; +/* No comment provided by engineer. */ +"bucket: %@" = "bucket: %@"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Viime kuukausi"; @@ -781,6 +1030,39 @@ /* Empty search placeholder on bookmarks search */ "empty.search" = "Ei hakutuloksia"; +/* No comment provided by engineer. */ +"Error" = "Virhe"; + +/* Button title to Sign In */ +"error.email-protection-sign-in.action" = "Kirjaudu sisään"; + +/* Alert message */ +"error.email-protection-sign-in.body" = "Kirjaudu sisään uudelleen, jotta Email Protection -ominaisuudet otetaan käyttöön uudelleen tässä selaimessa."; + +/* Alert title */ +"error.email-protection-sign-in.title" = "Email Protection -virhe"; + +/* Button title to open device settings */ +"error.insufficient-disk-space.action" = "Avaa asetukset"; + +/* Alert message */ +"error.insufficient-disk-space.body" = "Näyttää siltä, että laitteesi tallennustila on loppunut. Vapauta tilaa jatkaaksesi."; + +/* Alert title */ +"error.insufficient-disk-space.title" = "Ei riittävästi tallennustilaa"; + +/* Button title that is shutting down the app */ +"error.preemptive-crash.action" = "Sulje sovellus"; + +/* Alert message */ +"error.preemptive-crash.body" = "Näyttää siltä, että sovelluksessa on ongelma, ja se on suljettava. Avaa uudelleen jatkaaksesi."; + +/* Alert title */ +"error.preemptive-crash.title" = "Sovellusongelma havaittu"; + +/* Generic error message on a dialog for when the cause is not known. */ +"error.unknown.try.again" = "An unknown error has occurred"; + /* No comment provided by engineer. */ "favorite" = "Suosikki"; @@ -1000,6 +1282,18 @@ /* Home tab title */ "homeTab.title" = "Koti"; +/* OK title for invite screen alert dismissal button */ +"invite.alert.ok.button" = "OK"; + +/* Continue button on an invite dialog */ +"invite.dialog.continue.button" = "Continue"; + +/* Get Started button on an invite dialog */ +"invite.dialog.get.started.button" = "Get Started"; + +/* Message to show after user enters an unrecognized invite code */ +"invite.dialog.unrecognized.code.message" = "We didn’t recognize this Invite Code."; + /* No comment provided by engineer. */ "keyCommandAddBookmark" = "Lisää kirjanmerkki"; @@ -1066,15 +1360,15 @@ /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "Macin DuckDuckGo tarjoaa kaipaamasi nopeuden, odottamasi selaintoiminnot ja luokkansa parhaat tietosuojaominaisuudet."; -/* Body text for the macOS waitlist notification */ -"mac-waitlist.available.notification.body" = "Avaa kutsusi"; - /* Title for the macOS waitlist notification */ "mac-waitlist.available.notification.title" = "Macin DuckDuckGo on valmis!"; /* Title for the copy action */ "mac-waitlist.copy" = "Kopioi"; +/* Description text above the Share Link button */ +"mac-waitlist.join-waitlist-screen.on-your-computer-go-to" = "Siirry Windows-tietokoneella osoitteeseen:"; + /* Description text above the Share Link button */ "mac-waitlist.join-waitlist-screen.on-your-mac-go-to" = "Siirry Mac-tietokoneellasi osoitteeseen:"; @@ -1087,6 +1381,9 @@ /* Disclaimer for the Join Waitlist screen */ "mac-waitlist.join-waitlist-screen.windows" = "Windows-versio on tulossa pian!"; +/* Title for the macOS waitlist button redirecting to Windows waitlist */ +"mac-waitlist.join-waitlist-screen.windows-waitlist" = "Etsitkö Windows-versiota?"; + /* Title for the settings subtitle */ "mac-waitlist.settings.browse-privately" = "Selaa yksityisesti Mac-sovelluksellamme"; @@ -1097,7 +1394,7 @@ "mac-waitlist.share-sheet.title" = "DuckDuckGo Mac"; /* Title for the Mac Waitlist feature */ -"mac-waitlist.title" = "DuckDuckGo-työpöytäsovellus"; +"mac-waitlist.title" = "DuckDuckGo-sovellus Mac-laitteelle"; /* No comment provided by engineer. */ "menu.button.hint" = "Selausvalikko"; @@ -1114,9 +1411,81 @@ /* Edit button */ "navigation.title.edit" = "Muokkaa"; +/* String indicating NetP is connected when viewed from the settings screen */ +"netP.cell.connected" = "Connected"; + +/* String indicating NetP is disconnected when viewed from the settings screen */ +"netP.cell.disconnected" = "Not connected"; + +/* Title for the Network Protection feature */ +"netP.title" = "Network Protection"; + +/* Message for the network protection invite dialog */ +"network.protection.invite.dialog.message" = "Enter your invite code to get started."; + +/* Title for the network protection invite screen */ +"network.protection.invite.dialog.title" = "You're invited to try Network Protection"; + +/* Prompt for the network protection invite code text field */ +"network.protection.invite.field.prompt" = "Invite Code"; + +/* Message explaining that netP is invite only */ +"network.protection.invite.only.message" = "DuckDuckGo Network Protection is currently invite-only."; + +/* Message for the network protection invite success view */ +"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; + +/* Title for the network protection invite success view */ +"network.protection.invite.success.title" = "Success! You’re in."; + +/* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ +"network.protection.status.connected.format" = "Connected - %@"; + +/* The label for the NetP VPN when connecting */ +"network.protection.status.connecting" = "Connecting..."; + +/* The label for the NetP VPN when disconnected */ +"network.protection.status.disconnected" = "Not connected"; + +/* The label for the NetP VPN when disconnecting */ +"network.protection.status.disconnecting" = "Disconnecting..."; + +/* Message label text for the netP status view */ +"network.protection.status.header.message" = "DuckDuckGo's VPN secures all of your device's Internet traffic anytime, anywhere."; + +/* Header title label text for the status view when netP is disconnected */ +"network.protection.status.header.title.off" = "Network Protection is Off"; + +/* Header title label text for the status view when netP is connected */ +"network.protection.status.header.title.on" = "Network Protection is On"; + +/* The status view 'Share Feedback' button which is shown inline on the status view after the \(netPInviteOnlyMessage) text */ +"network.protection.status.menu.share.feedback" = "Share Feedback"; + +/* Connection details label shown in NetworkProtection's status view. */ +"network.protection.status.view.connection.details" = "Connection Details"; + +/* Generic connection failed error message shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.message" = "Please try again later."; + +/* Generic connection failed error title shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; + +/* IP Address label shown in NetworkProtection's status view. */ +"network.protection.status.view.ip.address" = "IP Address"; + +/* Location label shown in NetworkProtection's status view. */ +"network.protection.status.view.location" = "Location"; + +/* Title label text for the status view when netP is disconnected */ +"network.protection.status.view.title" = "Network Protection"; + /* Do not translate - stringsdict entry */ "number.of.tabs" = "number.of.tabs"; +/* No comment provided by engineer. */ +"Ok" = "OK"; + /* Text displayed on notification appearing in the address bar when the browser dismissed the cookie popup automatically rejecting it */ "omnibar.notification.cookies-managed" = "Hallitut evästeet"; @@ -1156,6 +1525,18 @@ /* ’Link’ is link on a website */ "open.externally.failed" = "Linkin avaamiseen tarvittavaa sovellusta ei löydy"; +/* No comment provided by engineer. */ +"Please enter which app on your device is broken." = "Ilmoita, mikä laitteesi sovellus on rikki."; + +/* Activate button */ +"pm.activate" = "Reactivate"; + +/* Cancel button */ +"pm.cancel" = "Cancel"; + +/* Deactivate button */ +"pm.deactivate" = "Deactivate"; + /* No comment provided by engineer. */ "preserveLogins.domain.list.footer" = "Verkkosivustot käyttävät evästeitä, joilla pysyt kirjautuneena sisään. Kun teet sivustosta palonkestävän, evästeitä ei poisteta ja pysyt kirjautuneena sisään, vaikka käytät Liekki-painiketta. Estämme edelleen kolmannen osapuolen seurannan palonkestävillä sivustoilla."; @@ -1225,8 +1606,8 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Suosikit"; -/* No comment provided by engineer. */ -"settings.about.text" = "DuckDuckGo asettaa uudet standardit luottamukselle verkossa.\n\nDuckDuckGo-tietosuojaselain tarjoaa kaikki keskeiset tietosuojaominaisuudet nettihakujesi ja -selailusi turvaksi, kuten seurantapalvelinten eston, älykkäämmän salauksen ja yksityisen DuckDuckGo-haun.\n\nInternetin ei pitäisi tuntua karmivalta paikalta ja nettiyksityisyyden pitäisi olla yhtä helppoa kuin verhojen vetäminen ikkunan eteen."; +/* about page */ +"settings.about.text" = "DuckDuckGo on vuonna 2008 perustettu riippumaton internetissä tietosuojaa tarjoava yritys niille, jotka ovat kyllästyneet siihen, että heitä seurataan netissä, ja haluavat yksinkertaisen ratkaisun. Olemme todiste siitä, että voit saada todellista tietosuojaa verkossa ilman kompromisseja.\n\nDuckDuckGo-selaimessa on kaikki parhaiden selainten ominaisuudet, kuten kirjanmerkit, välilehdet, salasanat ja paljon muuta, sekä yli [kymmenkunta tehokasta tietosuojausta](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/) ei tarjolla oletuksena suosituimmissa selaimissa. Tämä ainutlaatuisen kattava tietosuoja auttaa turvaamaan haut, selaamisen, sähköpostin lähettämiseen ja muun toimintasi verkossa.\n\nTietosuojamme toimivat, vaikka et tietäisi mitään teknisistä yksityiskohdista tai osaisi käsitellä monimutkaisia asetuksia. Sinun tarvitsee vain vaihtaa selaimesi DuckDuckGohon kaikilla laitteillasi, ja saat tietosuojan oletuksena.\n\nJos kuitenkin haluat kurkistaa konepellin alle, lisätietoja DuckDuckGon tietosuojan toiminnasta on [ohjesivuillamme](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/)."; /* Report a Broken Site screen confirmation button */ "siteFeedback.buttonText" = "Lähetä raportti"; @@ -1246,6 +1627,9 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Mikä verkkosivusto on viallinen?"; +/* No comment provided by engineer. */ +"sync.remove-device.message" = "\"%@\" ei enää pääse käsiksi synkronoituihin tietoihisi."; + /* Accessibility label on remove button */ "tab.close.home" = "Sulje koti-välilehti"; @@ -1318,6 +1702,57 @@ /* Voice-search footer note with on-device privacy warning */ "voiceSearch.footer.note" = "Ääni käsitellään laitteella. Sitä ei tallenneta eikä jaeta kenellekään, edes DuckDuckGolle."; +/* Title for the button to enable push notifications in system settings */ +"waitlist.allow-notifications" = "Salli ilmoitukset"; + +/* Body text for the waitlist notification */ +"waitlist.available.notification.body" = "Avaa kutsusi"; + +/* Title for the copy action */ +"waitlist.copy" = "Kopioi"; + +/* Label text for the invite code */ +"waitlist.invite-code" = "Kutsukoodi"; + +/* Step title on the invite screen */ +"waitlist.invite-screen.step.title" = "Vaihe %d"; + +/* Title for the invite code screen */ +"waitlist.invite-screen.youre-invited" = "Sinut on kutsuttu!"; + +/* Title for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.join" = "Liity yksityiselle odotuslistalle"; + +/* Temporary status text for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.joining" = "Liitytään odotuslistalle..."; + +/* Title for the Share Link button */ +"waitlist.join-waitlist-screen.share-link" = "Jaa linkki"; + +/* Notification text for the waitlist */ +"waitlist.joined.no-notification.get-notification" = "HALUAN ILMOITUKSEN"; + +/* Title for the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.get-notification-confirmation-title" = "Haluatko saada ilmoituksen, kun on sinun vuorosi?"; + +/* Cancel button in the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.no-thanks" = "Ei kiitos"; + +/* Text used for the Notifications Disabled state */ +"waitlist.notification.disabled" = "Voimme ilmoittaa sinulle, kun on sinun vuorosi, mutta ilmoitukset ovat tällä hetkellä poissa käytöstä DuckDuckGossa."; + +/* Privacy disclaimer for the Waitlist feature */ +"waitlist.privacy-disclaimer" = "Sinun ei tarvitse jakaa henkilötietoja liittyäksesi jonotuslistalle. Varmistat paikkasi jonossa aikaleimalla, joka on olemassa vain laitteessasi. Näin voimme ilmoittaa sinulle, kun on sinun vuorosi."; + +/* Title for the queue screen */ +"waitlist.queue-screen.on-the-list" = "Olet odotuslistalla!"; + +/* Title for the settings subtitle */ +"waitlist.settings.download-available" = "Lataus saatavilla"; + +/* Title for the share sheet entry */ +"waitlist.share-sheet.title" = "Sinut on kutsuttu!"; + /* Confirmation message */ "web.url.remove.favorite.done" = "Suosikki poistettu"; @@ -1342,3 +1777,51 @@ /* Alert title explaining the message is shown by a website */ "webJSAlert.website-message.format" = "Viesti käyttäjältä %@:"; +/* No comment provided by engineer. */ +"Welcome to the Duck Side!" = "Tervetuloa Duckin puolelle!"; + +/* Title for the Windows waitlist notification */ +"windows-waitlist.available.notification.title" = "Hanki DuckDuckGo Windows-laitteeseesi!"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-1.description" = "Lataa tämä URL-osoite Windows-laitteessasi:"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-2.description" = "Avaa DuckDuckGo-asennusohjelma Lataukset-kohdasta, valitse Asenna ja kirjoita sitten kutsukoodisi."; + +/* Subtitle for the Windows Waitlist Invite screen */ +"windows-waitlist.invite-screen.subtitle" = "Oletko valmis käyttämään DuckDuckGota Windows-laitteessa?"; + +/* Title for the Windows waitlist button redirecting to Mac waitlist */ +"windows-waitlist.join-waitlist-screen.mac-waitlist" = "Etsitkö Mac-versiota?"; + +/* Title for the Join Windows Waitlist screen */ +"windows-waitlist.join-waitlist-screen.try-duckduckgo-for-windows" = "Kokeile Windows-laitteen DuckDuckGo-sovellusta!"; + +/* Message for the alert to confirm enabling notifications */ +"windows-waitlist.joined.no-notification.get-notification-confirmation-message" = "Lähetämme sinulle ilmoituksen, kun DuckDuckGo-kopiosi on ladattavissa Windows-laitteeseen. "; + +/* Label text for the Joined Waitlist state with notifications declined */ +"windows-waitlist.joined.notifications-declined" = "Kutsusi kokeilla DuckDuckGon Windows-sovellusta saapuu tänne. Tarkista tilanne pian uudelleen. Voimme myös lähettää sinulle ilmoituksen, kun on sinun vuorosi."; + +/* Label text for the Joined Waitlist state with notifications enabled */ +"windows-waitlist.joined.notifications-enabled" = "Lähetämme sinulle ilmoituksen, kun DuckDuckGo-kopiosi on ladattavissa Windows-laitteeseen."; + +/* Title for the settings subtitle */ +"windows-waitlist.settings.browse-privately" = "Selaa yksityisesti Windows-sovelluksellamme"; + +/* Message used when sharing to iMessage. Parameter is an eight digit invite code. */ +"windows-waitlist.share-sheet.invite-code-message" = "Olet saanut kutsun!\n\nOletko valmis käyttämään DuckDuckGo'ta Windows-laitteessa?Vaihe 1\nLataa tämä URL-osoite Windows-laitteessasi:\nhttps://duckduckgo.com/windows\n\nVaihe 2\nAvaa DuckDuckGo-asennusohjelma Lataukset-kohdasta, valitse Asenna ja kirjoita sitten kutsukoodisi.\n\nKutsukoodi: %@"; + +/* Message used when sharing to iMessage */ +"windows-waitlist.share-sheet.message" = "Oletko valmis aloittamaan yksityisen selaamisen Windows-laitteessa?\n\nLataa seuraavasta osoitteesta:\nhttps://duckduckgo.com/windows"; + +/* Summary text for the Windows browser waitlist */ +"windows-waitlist.summary" = "DuckDuckGon Windows-sovellus tarjoaa kaiken, mitä tarvitset selaamiseen yksityisemmin. Siinä on yksityinen haku, jäljittäjien esto, pakotettu salaus ja evästeiden ponnahdusikkunoiden esto, ja tulossa on vielä lisää luokkansa parhaita suojauksia."; + +/* Title for the Windows Waitlist feature */ +"windows-waitlist.title" = "DuckDuckGo-sovellus Windowsille"; + +/* Title for the Windows browser download link page */ +"windows-waitlist.waitlist-download-screen.try-duckduckgo-for-windows" = "Hanki DuckDuckGo Windowsille!"; + diff --git a/DuckDuckGo/fi.lproj/Settings.strings b/DuckDuckGo/fi.lproj/Settings.strings index 9960995278..11b81ffefe 100644 --- a/DuckDuckGo/fi.lproj/Settings.strings +++ b/DuckDuckGo/fi.lproj/Settings.strings @@ -37,6 +37,9 @@ /* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ "9Ko-0g-T3h.text" = "Tekstin koko"; +/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ +"9kt-6R-XiZ.text" = "Otsikko"; + /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Sovelluksen käynnistys"; @@ -73,6 +76,12 @@ /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Global Privacy Control (GPC)"; +/* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ +"CxT-QK-iVn.title" = "Lisää"; + +/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ +"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; + /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Sovelluksesta poistuttaessa, 15 minuutin toimettomuuden jälkeen"; @@ -151,6 +160,9 @@ /* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ "HLr-R8-xxF.text" = "Esikatselu pitkällä painalluksella"; +/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ +"hoT-Nu-KXP.text" = "Selaa yksityisesti Windows-sovelluksellamme"; + /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Tietosuoja on käytössä kaikilla sivustoilla"; @@ -163,6 +175,12 @@ /* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ "m23-t6-9cb.text" = "Jaa palaute"; +/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ +"mLn-1x-Fl5.title" = "Osoitepalkin sijainti"; + +/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ +"mqV-pf-NZ1.text" = "Osoitepalkin sijainti"; + /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Anna DuckDuckGon hallinnoida evästeiden hallinnan ponnahdusikkunoita"; @@ -175,15 +193,27 @@ /* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ "oM7-1o-9oY.text" = "Tietoa DuckDuckGo:sta"; +/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ +"opn-JO-idF.text" = "Suosituin"; + /* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ "OxE-MQ-uJk.headerTitle" = "Lisää DuckDuckGolta"; +/* Class = "UILabel"; text = "Sync"; ObjectID = "oXN-ez-gct"; */ +"oXN-ez-gct.text" = "Synkronoi"; + /* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ "P0F-ts-ekd.text" = "Selaa yksityisesti Mac-sovelluksellamme "; +/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ +"qah-gb-udB.text" = "Network Protection"; + /* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ "qeN-SV-zy7.text" = "Label"; +/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ +"RQ8-H1-Ez1.text" = "DuckDuckGo-sovellus Windowsille"; + /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Tekstin koko"; @@ -232,8 +262,8 @@ /* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ "yvj-LL-MiR.text" = "Lisää sovellus telakkaasi"; -/* Class = "UILabel"; text = "DuckDuckGo Desktop App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "DuckDuckGo-työpöytäsovellus"; +/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ +"Yz9-17-qnn.text" = "DuckDuckGo-sovellus Macille"; /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Uusi välilehti"; diff --git a/DuckDuckGo/fr.lproj/Feedback.strings b/DuckDuckGo/fr.lproj/Feedback.strings index 1cf29f1065..e829b3329b 100644 --- a/DuckDuckGo/fr.lproj/Feedback.strings +++ b/DuckDuckGo/fr.lproj/Feedback.strings @@ -28,9 +28,6 @@ /* Class = "UIBarButtonItem"; title = "Back"; ObjectID = "JF8-Ow-zXL"; */ "JF8-Ow-zXL.title" = "Retour"; -/* Class = "UIBarButtonItem"; title = "Submit"; ObjectID = "jr6-Al-4fr"; */ -"jr6-Al-4fr.title" = "Envoyer"; - /* Class = "UIButton"; normalTitle = "Submit"; ObjectID = "lsB-Kq-U8p"; */ "lsB-Kq-U8p.normalTitle" = "Envoyer"; @@ -58,9 +55,6 @@ /* Class = "UINavigationItem"; title = "Report Broken Site"; ObjectID = "WjT-HM-yim"; */ "WjT-HM-yim.title" = "Signaler un problème de site"; -/* Class = "UILabel"; text = "Broken Site Reason"; ObjectID = "Wx3-Vb-ItS"; */ -"Wx3-Vb-ItS.text" = "Raison du mauvais fonctionnement du site"; - /* Class = "UINavigationItem"; title = "Share Feedback"; ObjectID = "Xb0-Zu-opM"; */ "Xb0-Zu-opM.title" = "Partagez vos commentaires"; diff --git a/DuckDuckGo/fr.lproj/Localizable.strings b/DuckDuckGo/fr.lproj/Localizable.strings index 3eb6c00b8b..e29c841bb2 100644 --- a/DuckDuckGo/fr.lproj/Localizable.strings +++ b/DuckDuckGo/fr.lproj/Localizable.strings @@ -1,6 +1,9 @@ /* No comment provided by engineer. */ "%@" = "%@"; +/* No comment provided by engineer. */ +"%@ [%@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)" = "%1$@ [%2$@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)"; + /* Buton label for Edit action */ "action.generic.edit" = "Modifier"; @@ -103,6 +106,12 @@ /* Share action */ "action.title.share" = "Partager"; +/* Settings label for bottom position for the address bar */ +"address.bar.bottom" = "En bas"; + +/* Settings label for top position for the address bar */ +"address.bar.top" = "Haut de page"; + /* No comment provided by engineer. */ "addWidget.button" = "Ajouter un widget"; @@ -142,6 +151,225 @@ /* Shown on authentication screen */ "app.authentication.unlock" = "Déverrouiller DuckDuckGo."; +/* First part of about page content (note the trailing space) */ +"appTP.about.content1" = "You’ve probably heard about companies like Google and Facebook tracking you behind the scenes on third-party websites. But did you know they also track your personal information through apps on your device?\n\nIn 2022, DuckDuckGo found that "; + +/* Second part of about page content (note the trailing space) */ +"appTP.about.content2" = "over 85% of free iOS apps tested contained hidden trackers from other companies."; + +/* Third part of about page content (note the leading space) */ +"appTP.about.content3" = " Of the 395 apps tested, 60% sent data to Google. This happens even while you’re not using your device.\n\nTrackers in apps may have access to a lot more information than their website tracker cousins, such as your location down to which floor of a building you're on, how often you play games while at work, and when and how long you sleep each day. Even if you haven’t given apps explicit permission to collect data, they can still take it without your knowledge.\n\nTracking networks like Facebook and Google use these little pieces of information to build a digital profile about you. With it, tracking networks can manipulate what you see online and allow advertisers to bid on access to you based on your data.\n\nTrackers in apps is a BIG problem for privacy. But DuckDuckGo has a solution that can help.\n\nWhen enabled in the DuckDuckGo Privacy Browser app, App Tracking Protection blocks many trackers in other apps, not just the trackers we find on websites when you browse. These dual layers of protection reduce what companies know about you overall, so you can use your apps with more peace of mind, knowing you’re more protected."; + +/* Navigation Title for AppTP about page */ +"appTP.about.navTitle" = "About App Trackers"; + +/* Title for AppTP about page */ +"appTP.about.title" = "What Are App Trackers?"; + +/* Title for 'Report an Issue' button in the activity view. */ +"appTP.activityView.reportIssue" = "Report Issue"; + +/* Text label for switch that turns blocking on or off for a tracker */ +"appTP.blockTrackerText" = "Block this Tracker"; + +/* Detail string describing what AppTP is */ +"appTP.cell.detail" = "Bloquer les traqueurs d'applications sur votre appareil"; + +/* String indicating AppTP is disabled when viewed from the settings screen */ +"appTP.cell.disabled" = "Désactivé"; + +/* String indicating AppTP is enabled when viewed from the settings screen */ +"appTP.cell.enabled" = "Activé"; + +/* Info string informing the user what App Tracking Protection does. */ +"appTP.empty.disabled.info" = "Activez App Tracking Protection pour que nous puissions bloquer les traqueurs indésirables dans d'autres applications."; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.heading" = "We’re blocking hidden trackers"; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.info" = "Come back soon to see a list of all the app trackers we’ve blocked."; + +/* First answer for AppTP FAQ page */ +"appTP.faq.answer1" = "App Tracking Protection blocks app trackers from other companies, like when Facebook tries to track you in a banking app. Companies may still track you in apps they own."; + +/* Second answer for AppTP FAQ page */ +"appTP.faq.answer2" = "Yes! App Tracking Protection works across all apps on your device to block the most common hidden trackers we find trying to collect your personal info."; + +/* Third answer for AppTP FAQ page */ +"appTP.faq.answer3" = "We currently only block the most common trackers that we find on iOS. This helps us to comprehensively test App Tracking Protection and lower frequency of app breakage, while blocking up to 70% of all tracking requests."; + +/* Fourth answer for AppTP FAQ page */ +"appTP.faq.answer4" = "You’ll be asked to set up a virtual private network (VPN) connection, but you don't need to install a VPN app for App Tracking Protection to work.\n\nThis permission, which works only on your device, allows App Tracking Protection to monitor network traffic so that it can block known trackers."; + +/* Fifth answer for AppTP FAQ page */ +"appTP.faq.answer5" = "You can use App Tracking Protection at the same time as using an IKEv2 protocol VPN app on an iOS device. You won’t be able to use App Tracking Protection on an iOS device if you’re using a VPN app that uses a different type of protocol, like WireGuard or OpenVPN type VPNs."; + +/* Sixth answer for AppTP FAQ page */ +"appTP.faq.answer6" = "A VPN sends your data from the device to its own server, where it secures and anonymizes your data from prying eyes. However, this allows the VPN company to see your network traffic.\n\nApp Tracking Protection is different. Instead of sending your data to a VPN server, App Tracking Protection works only on your device, sitting between your apps and the servers they talk to.\n\nWhenever App Tracking Protection recognizes a known tracker, it blocks the tracker from sending personal information (such as your IP address, activity, and device details) off your device. All other traffic reaches its destination, so your apps work normally."; + +/* Seventh answer for AppTP FAQ page */ +"appTP.faq.answer7" = "App Tracking Protection works only on your device and doesn’t send your data off your device to DuckDuckGo. We don’t collect or store any data from your apps."; + +/* First question for AppTP FAQ page */ +"appTP.faq.question1" = "How does App Tracking Protection work?"; + +/* Second question for AppTP FAQ page */ +"appTP.faq.question2" = "Does App Tracking Protection block trackers in all apps on my device?"; + +/* Third question for AppTP FAQ page */ +"appTP.faq.question3" = "Does App Tracking Protection block all app trackers?"; + +/* Fourth question for AppTP FAQ page */ +"appTP.faq.question4" = "Why does App Tracking Protection use a VPN connection?"; + +/* Fifth question for AppTP FAQ page */ +"appTP.faq.question5" = "Will App Tracking Protection work if I also use a VPN app?"; + +/* Sixth question for AppTP FAQ page */ +"appTP.faq.question6" = "How is App Tracking Protection different from a VPN?"; + +/* Seventh question for AppTP FAQ page */ +"appTP.faq.question7" = "Is my data private?"; + +/* Title for AppTP FAQ page */ +"appTP.faq.title" = "App Tracking Protection FAQ"; + +/* Do not translate. StringsDict entry -- Count part of string 'App Tracking Protection blocked x tracking attempts today' */ +"appTP.home.blockedCount" = "appTP.home.blockedCount"; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the trailing space) */ +"appTP.home.blockedPrefix" = "App Tracking Protection a bloqué "; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the leading space) */ +"appTP.home.blockedSuffix" = " dans vos applications aujourd'hui."; + +/* Prefix of string 'App Tracking Protection disabled. Tap to re-enable.' (note the trailing space) */ +"appTP.home.disabledPrefix" = "App Tracking Protection disabled. "; + +/* Suffix of string 'App Tracking Protection disabled. Tap to re-enable.' */ +"appTP.home.disabledSuffix" = "Tap to continue blocking tracking attempts across your apps."; + +/* Text indicating the tracking event occured 'just now'. Example: Last attempt 'just now' */ +"appTP.justNow" = "À l'instant"; + +/* View to manage trackers for AppTP. Allows the user to turn trackers on or off. */ +"appTP.manageTrackers" = "Gérer les traqueurs"; + +/* Button title for AppTP onboarding */ +"appTP.onboarding.continueButton" = "Continue"; + +/* Button title for AppTP onboarding to enable AppTP */ +"appTP.onboarding.enableeButton" = "Enable App Tracking Protection"; + +/* Button title for AppTP onboarding to learn more about AppTP */ +"appTP.onboarding.learnMoreButton" = "Learn More"; + +/* First part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info1" = "Over 85% of free iOS apps"; + +/* Second part of info on the first AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page1Info2" = " we’ve tested allow other companies to track your personal information, even when you’re sleeping."; + +/* Third part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info3" = "See who we catch trying to track you in your apps and take back control."; + +/* First part of info on the second AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page2Info1" = "App Tracking Protection "; + +/* Second part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info2" = "detects and blocks app trackers from other companies,"; + +/* Third part of info on the second AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page2Info3" = " like when Google attempts to track you in a health app."; + +/* Fourth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info4" = "It’s free,"; + +/* Fifth part of info on the second AppTP onboarding page (note the leading and trailing space) */ +"appTP.onboarding.page2Info5" = " and you can enjoy your apps as you normally would. Working in the background, it helps "; + +/* Sixth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info6" = "protect you night and day."; + +/* First part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info1" = "App Tracking Protection is not a VPN."; + +/* Second part of info on the third AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page3Info2" = " However, your device will recognize it as one. This is because it uses a local VPN connection to work."; + +/* Third part of info on the third AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page3Info3" = "App Tracking Protection is different. "; + +/* Fourth part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info4" = "It never routes app data through an external server."; + +/* Title for first AppTP onboarding page */ +"appTP.onboarding.title1" = "One easy step for better app privacy!"; + +/* Title for second AppTP onboarding page */ +"appTP.onboarding.title2" = "How does it work?"; + +/* Title for third AppTP onboarding page */ +"appTP.onboarding.title3" = "Who sees your data?"; + +/* Breakage report app name label */ +"appTP.report.appLabel" = "Which app is having issues?"; + +/* Breakage report app name placeholder */ +"appTP.report.appPlaceholder" = "App name"; + +/* Breakage report category label */ +"appTP.report.categoryLabel" = "Que se passe-t-il ?"; + +/* Breakage report comment label */ +"appTP.report.commentLabel" = "Comments"; + +/* Breakage report comment placeholder */ +"appTP.report.commentPlaceholder" = "Add additional details"; + +/* Breakage report footer explaining what is collected in the breakage report */ +"appTP.report.footer" = "In addition to the details entered into this form, your app issue report will contain:\n• A list of trackers blocked in the last 10 min\n• Whether App Tracking Protection is enabled\n• Aggregate DuckDuckGo app diagnostics"; + +/* Breakage report submit button */ +"appTP.report.submit" = "Envoyer"; + +/* Breakage report form title */ +"appTP.report.title" = "Report Issue"; + +/* Breakage report succcess message */ +"appTP.report.toast" = "Merci ! Commentaire envoyé."; + +/* Cancel button for 'Report an Issue' alert. */ +"appTP.reportAlert.cancel" = "Pas maintenant"; + +/* Confirm button for 'Report an Issue' alert. */ +"appTP.reportAlert.confirm" = "Signaler le problème"; + +/* Message for 'Report an Issue' alert. */ +"appTP.reportAlert.message" = "Let us know if you disabled App Tracking Protection for this specific tracker because it caused app issues. Your feedback helps us improve!"; + +/* Title for 'Report an Issue' alert. */ +"appTP.reportAlert.title" = "Report Issue?"; + +/* Toast notification diplayed after restoring the blocklist to default settings */ +"appTP.restoreDefaultsToast" = "Default settings restored"; + +/* Button to restore the blocklist to its default state. */ +"appTP.restoreDefualts" = "Rétablir les valeurs par défaut"; + +/* Title for the App Tracking Protection feature */ +"appTP.title" = "Protection contre le suivi des applications"; + +/* Text indicating when the tracker was last allowed. Example: Last attempt allowed (timeString) */ +"appTP.trackerAllowedTimestamp" = "Dernière tentative d'autorisation %@"; + +/* Text indicating when the tracker was last blocked. Example: Last attempt blocked (timeString) */ +"appTP.trackerBlockedTimestamp" = "Dernière tentative de blocage %@"; + +/* Do not translate. StringsDict entry -- Subtitle for tracking attempts in App Tracking Protection Activity View. Example: (count) tracking attempts */ +"appTP.trackingattempts" = "appTP.trackingattempts"; + /* Authentication Alert Sign In Button */ "auth.alert.login.button" = "Se connecter"; @@ -301,6 +529,18 @@ /* Toast message when a login item without a title is deleted */ "autofill.logins.list.login-deleted-message-no-title" = "Identifiant supprimé"; +/* Title for a button that allows a user to reset their list of never saved sites */ +"autofill.logins.list.never.saved" = "Réinitialiser les sites exclus"; + +/* Cancel button for resetting list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.cancel" = "Annuler"; + +/* Confirm button to reset list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.confirm" = "Réinitialiser les sites exclus"; + +/* Alert title */ +"autofill.logins.list.never.saved.reset.action.title" = "Si vous réinitialisez les sites exclus, on vous invitera à enregistrer votre identifiant la prochaine fois que vous vous connecterez à l'un de ces sites."; + /* Placeholder for search field on autofill login listing */ "autofill.logins.list.search-placeholder" = "Recherche d'identifiants"; @@ -335,7 +575,7 @@ "autofill.logins.prompt.password.button.title" = "Mot de passe pour %@"; /* Title for autofill login prompt */ -"autofill.logins.prompt.title" = "Utiliser un identifiant enregistré ?"; +"autofill.logins.prompt.title" = "Utiliser un mot de passe enregistré ?"; /* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ "autofill.logins.search.no-results.subtitle" = "pour '%@'"; @@ -385,27 +625,24 @@ /* Title for the alert dialog telling the user an updated username is no longer a private email address */ "autofill.removed.duck.address.title" = "Le nom d'utilisateur de la Duck Address privée a été supprimé"; +/* CTA displayed on modal asking if the user never wants to be prompted to save a login for this website agin */ +"autofill.save-login.never-prompt.CTA" = "Ne jamais demander pour ce site"; + /* Message displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.message" = "Les identifiants de connexion sont stockés en toute sécurité sur votre appareil dans le menu Identifiants."; +"autofill.save-login.new-user.message" = "Les mots de passe sont stockés en toute sécurité sur votre appareil dans le menu Identifiants."; /* Title displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.title" = "Voulez-vous que DuckDuckGo enregistre votre identifiant ?"; +"autofill.save-login.new-user.title" = "Voulez-vous que DuckDuckGo enregistre votre mot de passe ?"; /* Cancel CTA displayed on modal asking for the user to save the login */ "autofill.save-login.not-now.CTA" = "Ne pas enregistrer"; -/* Confirm CTA displayed on modal asking for the user to save the login */ -"autofill.save-login.save.CTA" = "Enregistrer l'identifiant"; - /* Title displayed on modal asking for the user to save the login */ "autofill.save-login.title" = "Enregistrer l'identifiant ?"; /* Confirm CTA displayed on modal asking for the user to save the password */ "autofill.save-password.save.CTA" = "Enregistrer le mot de passe"; -/* Title displayed on modal asking for the user to save the password */ -"autofill.save-password.title" = "Enregistrer le mot de passe ?"; - /* Accessibility title for a Show Password button displaying actial password instead of ***** */ "autofill.show-password" = "Afficher le mot de passe"; @@ -413,7 +650,7 @@ "autofill.signin.to.manage" = "%@ pour gérer vos Duck Addresses sur cet appareil."; /* Message displayed on modal asking for the user to update the password */ -"autofill.update-password.message" = "DuckDuckGo mettra à jour cet identifiant enregistré sur votre appareil."; +"autofill.update-password.message" = "DuckDuckGo mettra à jour ce mot de passe enregistré sur votre appareil."; /* Confirm CTA displayed on modal asking for the user to update the password */ "autofill.update-password.save.CTA" = "Modifier le mot de passe"; @@ -547,9 +784,21 @@ /* Broken Site Category */ "brokensite.category.videos" = "La vidéo n'a pas été lue"; +/* Broken Site Category Placeholder */ +"brokensite.categoryPlaceholder" = "Sélectionnez votre problème dans la liste..."; + +/* Broken Site Category Section Title */ +"brokensite.categoryTitle" = "SÉLECTIONNER UNE CATÉGORIE"; + +/* Broken Site Comment Placeholder */ +"brokensite.commentPlaceholder" = "Le fait de partager plus d'informations peut nous aider à résoudre ce problème"; + /* Broken Site Section Title */ "brokensite.sectionTitle" = "DÉCRIVEZ CE QU'IL S'EST PASSÉ"; +/* No comment provided by engineer. */ +"bucket: %@" = "case : %@"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Le mois dernier"; @@ -781,6 +1030,39 @@ /* Empty search placeholder on bookmarks search */ "empty.search" = "Aucune correspondance trouvée"; +/* No comment provided by engineer. */ +"Error" = "Erreur"; + +/* Button title to Sign In */ +"error.email-protection-sign-in.action" = "Se connecter"; + +/* Alert message */ +"error.email-protection-sign-in.body" = "Désolé, veuillez vous connecter à nouveau pour réactiver les fonctionnalités d'Email Protection sur ce navigateur."; + +/* Alert title */ +"error.email-protection-sign-in.title" = "Erreur Email Protection"; + +/* Button title to open device settings */ +"error.insufficient-disk-space.action" = "Ouvrez les Paramètres"; + +/* Alert message */ +"error.insufficient-disk-space.body" = "Apparemment, votre appareil n'a plus d'espace de stockage. Veuillez libérer de l'espace pour pouvoir continuer."; + +/* Alert title */ +"error.insufficient-disk-space.title" = "Pas assez d'espace de stockage"; + +/* Button title that is shutting down the app */ +"error.preemptive-crash.action" = "Fermer l'application"; + +/* Alert message */ +"error.preemptive-crash.body" = "Apparemment, un problème lié à l'application fait qu'elle doit fermer. Veuillez la rouvrir pour pouvoir continuer."; + +/* Alert title */ +"error.preemptive-crash.title" = "Problème d'application détecté"; + +/* Generic error message on a dialog for when the cause is not known. */ +"error.unknown.try.again" = "An unknown error has occurred"; + /* No comment provided by engineer. */ "favorite" = "Favori"; @@ -1000,6 +1282,18 @@ /* Home tab title */ "homeTab.title" = "Accueil"; +/* OK title for invite screen alert dismissal button */ +"invite.alert.ok.button" = "OK"; + +/* Continue button on an invite dialog */ +"invite.dialog.continue.button" = "Continue"; + +/* Get Started button on an invite dialog */ +"invite.dialog.get.started.button" = "Get Started"; + +/* Message to show after user enters an unrecognized invite code */ +"invite.dialog.unrecognized.code.message" = "We didn’t recognize this Invite Code."; + /* No comment provided by engineer. */ "keyCommandAddBookmark" = "Ajouter un signet"; @@ -1066,15 +1360,15 @@ /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "DuckDuckGo pour Mac répond à vos besoins de rapidité et de fonctionnalités de navigation, et dispose des meilleurs outils de leur catégorie pour protéger votre vie privée."; -/* Body text for the macOS waitlist notification */ -"mac-waitlist.available.notification.body" = "Ouvrez votre invitation"; - /* Title for the macOS waitlist notification */ "mac-waitlist.available.notification.title" = "DuckDuckGo pour Mac est prêt !"; /* Title for the copy action */ "mac-waitlist.copy" = "Copier"; +/* Description text above the Share Link button */ +"mac-waitlist.join-waitlist-screen.on-your-computer-go-to" = "Sur votre ordinateur Windows, accédez à :"; + /* Description text above the Share Link button */ "mac-waitlist.join-waitlist-screen.on-your-mac-go-to" = "Sur votre Mac, accédez à :"; @@ -1087,6 +1381,9 @@ /* Disclaimer for the Join Waitlist screen */ "mac-waitlist.join-waitlist-screen.windows" = "Des places vont bientôt se libérer !"; +/* Title for the macOS waitlist button redirecting to Windows waitlist */ +"mac-waitlist.join-waitlist-screen.windows-waitlist" = "Vous cherchez la version Windows ?"; + /* Title for the settings subtitle */ "mac-waitlist.settings.browse-privately" = "Naviguez incognito avec notre application pour Mac"; @@ -1097,7 +1394,7 @@ "mac-waitlist.share-sheet.title" = "DuckDuckGo pour Mac"; /* Title for the Mac Waitlist feature */ -"mac-waitlist.title" = "Application de bureau DuckDuckGo"; +"mac-waitlist.title" = "Application DuckDuckGo pour Mac"; /* No comment provided by engineer. */ "menu.button.hint" = "Menu de navigation"; @@ -1114,9 +1411,81 @@ /* Edit button */ "navigation.title.edit" = "Modifier"; +/* String indicating NetP is connected when viewed from the settings screen */ +"netP.cell.connected" = "Connected"; + +/* String indicating NetP is disconnected when viewed from the settings screen */ +"netP.cell.disconnected" = "Not connected"; + +/* Title for the Network Protection feature */ +"netP.title" = "Network Protection"; + +/* Message for the network protection invite dialog */ +"network.protection.invite.dialog.message" = "Enter your invite code to get started."; + +/* Title for the network protection invite screen */ +"network.protection.invite.dialog.title" = "You're invited to try Network Protection"; + +/* Prompt for the network protection invite code text field */ +"network.protection.invite.field.prompt" = "Invite Code"; + +/* Message explaining that netP is invite only */ +"network.protection.invite.only.message" = "DuckDuckGo Network Protection is currently invite-only."; + +/* Message for the network protection invite success view */ +"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; + +/* Title for the network protection invite success view */ +"network.protection.invite.success.title" = "Success! You’re in."; + +/* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ +"network.protection.status.connected.format" = "Connected - %@"; + +/* The label for the NetP VPN when connecting */ +"network.protection.status.connecting" = "Connecting..."; + +/* The label for the NetP VPN when disconnected */ +"network.protection.status.disconnected" = "Not connected"; + +/* The label for the NetP VPN when disconnecting */ +"network.protection.status.disconnecting" = "Disconnecting..."; + +/* Message label text for the netP status view */ +"network.protection.status.header.message" = "DuckDuckGo's VPN secures all of your device's Internet traffic anytime, anywhere."; + +/* Header title label text for the status view when netP is disconnected */ +"network.protection.status.header.title.off" = "Network Protection is Off"; + +/* Header title label text for the status view when netP is connected */ +"network.protection.status.header.title.on" = "Network Protection is On"; + +/* The status view 'Share Feedback' button which is shown inline on the status view after the \(netPInviteOnlyMessage) text */ +"network.protection.status.menu.share.feedback" = "Share Feedback"; + +/* Connection details label shown in NetworkProtection's status view. */ +"network.protection.status.view.connection.details" = "Connection Details"; + +/* Generic connection failed error message shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.message" = "Please try again later."; + +/* Generic connection failed error title shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; + +/* IP Address label shown in NetworkProtection's status view. */ +"network.protection.status.view.ip.address" = "IP Address"; + +/* Location label shown in NetworkProtection's status view. */ +"network.protection.status.view.location" = "Location"; + +/* Title label text for the status view when netP is disconnected */ +"network.protection.status.view.title" = "Network Protection"; + /* Do not translate - stringsdict entry */ "number.of.tabs" = "number.of.tabs"; +/* No comment provided by engineer. */ +"Ok" = "OK"; + /* Text displayed on notification appearing in the address bar when the browser dismissed the cookie popup automatically rejecting it */ "omnibar.notification.cookies-managed" = "Cookies gérés"; @@ -1156,6 +1525,18 @@ /* ’Link’ is link on a website */ "open.externally.failed" = "L'application nécessaire pour ouvrir ce lien est introuvable"; +/* No comment provided by engineer. */ +"Please enter which app on your device is broken." = "Veuillez indiquer quelle application est défectueuse sur votre appareil."; + +/* Activate button */ +"pm.activate" = "Reactivate"; + +/* Cancel button */ +"pm.cancel" = "Cancel"; + +/* Deactivate button */ +"pm.deactivate" = "Deactivate"; + /* No comment provided by engineer. */ "preserveLogins.domain.list.footer" = "Les sites Web utilisent des cookies pour maintenir votre connexion. Quand vous placez un site en mode coupe-feu, les cookies sont conservés et vous restez connecté(e), même après avoir utilisé le bouton en forme de flamme. Ceci dit, les traqueurs tiers sont bloqués sur les sites coupe-feu."; @@ -1225,8 +1606,8 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Favoris"; -/* No comment provided by engineer. */ -"settings.about.text" = "Chez DuckDuckGo, nous mettons en place un nouveau concept de confiance en ligne.\n\nLe navigateur confidentiel DuckDuckGo fournit tous les éléments de confidentialité essentiels dont vous avez besoin pour vous protéger lorsque vous faites des recherches et naviguez sur le Web, notamment le blocage des traqueurs, un cryptage plus intelligent et la recherche privée DuckDuckGo.\n\nAprès tout, la sphère Internet gagnerait à s’affranchir d’un éventail de pratiques douteuses. Avec nous, protégez votre vie privée en ligne aussi aisément que vous fermez les rideaux."; +/* about page */ +"settings.about.text" = "DuckDuckGo est une société indépendante de confidentialité sur internet fondée en 2008, destinée à tous ceux qui en ont assez d'être suivis en ligne et qui veulent une solution simple. Nous sommes la preuve que vous pouvez bénéficier d'une véritable protection de la confidentialité en ligne, sans avoir à faire de compromis.\n\nLe navigateur DuckDuckGo est doté des fonctionnalités que vous attendez d'un navigateur de référence, comme les signets, les onglets et les mots de passe, entre autres, auxquels s'ajoutent plus d'[une douzaine de protections puissantes de la confidentialité](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/) que la plupart des navigateurs populaires ne proposent pas par défaut. Cet ensemble unique et complet de protections de la confidentialité permet de préserver vos activités en ligne, de la recherche à la navigation, en passant par l'envoi d'e-mails, etc.\n\nInutile de s'y connaître en détails techniques ou de gérer des paramètres complexes pour faire fonctionner nos protections de la confidentialité. Il vous suffit d'opter pour le navigateur DuckDuckGo sur tous vos appareils afin de pouvoir bénéficier de la confidentialité par défaut.\n\nMais si vous voulez vous faire une idée, vous trouverez plus d'informations sur le fonctionnement des protections de la confidentialité DuckDuckGo sur nos [pages d'aide](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/)."; /* Report a Broken Site screen confirmation button */ "siteFeedback.buttonText" = "Envoyer le rapport"; @@ -1246,6 +1627,9 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Quel site Web pose problème ?"; +/* No comment provided by engineer. */ +"sync.remove-device.message" = "« %@ » ne pourra plus accéder à vos données synchronisées."; + /* Accessibility label on remove button */ "tab.close.home" = "Fermer l'onglet d'accueil"; @@ -1318,6 +1702,57 @@ /* Voice-search footer note with on-device privacy warning */ "voiceSearch.footer.note" = "L'audio est traité directement sur l'appareil. Il n'est ni stocké ni partagé avec qui que ce soit, y compris DuckDuckGo."; +/* Title for the button to enable push notifications in system settings */ +"waitlist.allow-notifications" = "Autoriser les notifications"; + +/* Body text for the waitlist notification */ +"waitlist.available.notification.body" = "Ouvrez votre invitation"; + +/* Title for the copy action */ +"waitlist.copy" = "Copier"; + +/* Label text for the invite code */ +"waitlist.invite-code" = "Code d'invitation"; + +/* Step title on the invite screen */ +"waitlist.invite-screen.step.title" = "Étape %d"; + +/* Title for the invite code screen */ +"waitlist.invite-screen.youre-invited" = "Vous avez reçu une invitation !"; + +/* Title for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.join" = "Rejoindre la liste d'attente privée"; + +/* Temporary status text for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.joining" = "Inscription à la liste d'attente..."; + +/* Title for the Share Link button */ +"waitlist.join-waitlist-screen.share-link" = "Partager le lien"; + +/* Notification text for the waitlist */ +"waitlist.joined.no-notification.get-notification" = "ME PRÉVENIR"; + +/* Title for the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.get-notification-confirmation-title" = "Recevoir une notification lorsque ce sera votre tour ?"; + +/* Cancel button in the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.no-thanks" = "Non merci"; + +/* Text used for the Notifications Disabled state */ +"waitlist.notification.disabled" = "Nous pouvons vous avertir lorsque ce sera votre tour, mais les notifications sont actuellement désactivées pour DuckDuckGo."; + +/* Privacy disclaimer for the Waitlist feature */ +"waitlist.privacy-disclaimer" = "Vous n'aurez pas à fournir d'informations personnelles pour vous inscrire sur la liste d'attente. Votre place sera assurée par un horodatage associé uniquement à votre appareil, qui nous permettra de vous informer quand viendra votre tour."; + +/* Title for the queue screen */ +"waitlist.queue-screen.on-the-list" = "Vous êtes sur liste d'attente !"; + +/* Title for the settings subtitle */ +"waitlist.settings.download-available" = "Téléchargement disponible"; + +/* Title for the share sheet entry */ +"waitlist.share-sheet.title" = "Vous avez reçu une invitation !"; + /* Confirmation message */ "web.url.remove.favorite.done" = "Favori supprimé"; @@ -1342,3 +1777,51 @@ /* Alert title explaining the message is shown by a website */ "webJSAlert.website-message.format" = "Message de %@ :"; +/* No comment provided by engineer. */ +"Welcome to the Duck Side!" = "Bienvenue du côté Duck !"; + +/* Title for the Windows waitlist notification */ +"windows-waitlist.available.notification.title" = "Essayez DuckDuckGo pour Windows !"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-1.description" = "Accédez à cette URL sur votre appareil Windows pour télécharger l'application :"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-2.description" = "Ouvrez le programme d'installation DuckDuckGo dans Téléchargements, sélectionnez Installer, puis saisissez votre code d'invitation."; + +/* Subtitle for the Windows Waitlist Invite screen */ +"windows-waitlist.invite-screen.subtitle" = "Prêt(e) à utiliser DuckDuckGo sous Windows ?"; + +/* Title for the Windows waitlist button redirecting to Mac waitlist */ +"windows-waitlist.join-waitlist-screen.mac-waitlist" = "Vous cherchez la version Mac ?"; + +/* Title for the Join Windows Waitlist screen */ +"windows-waitlist.join-waitlist-screen.try-duckduckgo-for-windows" = "Profitez d'un accès anticipé afin d'essayer DuckDuckGo pour Windows !"; + +/* Message for the alert to confirm enabling notifications */ +"windows-waitlist.joined.no-notification.get-notification-confirmation-message" = "Nous vous avertirons lorsque vous pourrez télécharger DuckDuckGo pour Windows. "; + +/* Label text for the Joined Waitlist state with notifications declined */ +"windows-waitlist.joined.notifications-declined" = "Votre invitation à essayer DuckDuckGo pour Windows arrivera ici. Vous pouvez revenir dans quelque temps ou attendre de recevoir une notification de notre part."; + +/* Label text for the Joined Waitlist state with notifications enabled */ +"windows-waitlist.joined.notifications-enabled" = "Nous vous avertirons lorsque vous pourrez télécharger DuckDuckGo pour Windows."; + +/* Title for the settings subtitle */ +"windows-waitlist.settings.browse-privately" = "Naviguez incognito avec notre application pour Windows"; + +/* Message used when sharing to iMessage. Parameter is an eight digit invite code. */ +"windows-waitlist.share-sheet.invite-code-message" = "Vous avez reçu une invitation !\n\nPrêt(e) à utiliser DuckDuckGo sous Windows ?\n\nÉtape 1\nAccédez à cette URL sur votre appareil Windows pour télécharger l'application :\nhttps://duckduckgo.com/windows\n\nÉtape 2\nOuvrez le programme d'installation DuckDuckGo dans Téléchargements, sélectionnez Installer, puis saisissez votre code d'invitation.\n\nCode d'invitation : %@"; + +/* Message used when sharing to iMessage */ +"windows-waitlist.share-sheet.message" = "Prêt(e) à naviguer incognito sur Windows ?\n\nAccédez à cette URL sur votre ordinateur pour télécharger l'application :\nhttps://duckduckgo.com/windows"; + +/* Summary text for the Windows browser waitlist */ +"windows-waitlist.summary" = "DuckDuckGo pour Windows a tout ce dont vous avez besoin pour naviguer en toute confidentialité : recherche privée, blocage des traqueurs, cryptage forcé et blocage des fenêtres contextuelles (cookies), sans oublier d'autres protections de choix à venir."; + +/* Title for the Windows Waitlist feature */ +"windows-waitlist.title" = "Application DuckDuckGo pour Windows"; + +/* Title for the Windows browser download link page */ +"windows-waitlist.waitlist-download-screen.try-duckduckgo-for-windows" = "Procurez-vous DuckDuckGo pour Windows !"; + diff --git a/DuckDuckGo/fr.lproj/Settings.strings b/DuckDuckGo/fr.lproj/Settings.strings index 6a69d04247..b4e289ee89 100644 --- a/DuckDuckGo/fr.lproj/Settings.strings +++ b/DuckDuckGo/fr.lproj/Settings.strings @@ -37,6 +37,9 @@ /* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ "9Ko-0g-T3h.text" = "Taille du texte"; +/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ +"9kt-6R-XiZ.text" = "Titre"; + /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Au lancement de l'application"; @@ -73,6 +76,12 @@ /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Global Privacy Control (GPC)"; +/* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ +"CxT-QK-iVn.title" = "Ajouter"; + +/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ +"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; + /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Quitter l'application, inactivité pendant 15 minutes"; @@ -151,6 +160,9 @@ /* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ "HLr-R8-xxF.text" = "Aperçus obtenus en appuyant longuement"; +/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ +"hoT-Nu-KXP.text" = "Naviguez incognito avec notre application pour Windows"; + /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "La protection de la confidentialité est activée pour tous les sites"; @@ -163,6 +175,12 @@ /* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ "m23-t6-9cb.text" = "Partagez vos commentaires"; +/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ +"mLn-1x-Fl5.title" = "Position de la barre d'adresse"; + +/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ +"mqV-pf-NZ1.text" = "Position de la barre d'adresse"; + /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Autoriser DuckDuckGo à gérer les fenêtres contextuelles de consentement aux cookies"; @@ -175,15 +193,27 @@ /* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ "oM7-1o-9oY.text" = "À propos de DuckDuckGo"; +/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ +"opn-JO-idF.text" = "Haut de page"; + /* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ "OxE-MQ-uJk.headerTitle" = "Plus de la part de DuckDuckGo"; +/* Class = "UILabel"; text = "Sync"; ObjectID = "oXN-ez-gct"; */ +"oXN-ez-gct.text" = "Synchronisation"; + /* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ "P0F-ts-ekd.text" = "Naviguez incognito avec notre application pour Mac "; +/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ +"qah-gb-udB.text" = "Network Protection"; + /* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ "qeN-SV-zy7.text" = "Label"; +/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ +"RQ8-H1-Ez1.text" = "Application DuckDuckGo pour Windows"; + /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Taille du texte"; @@ -232,8 +262,8 @@ /* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ "yvj-LL-MiR.text" = "Ajouter l'application à votre Dock"; -/* Class = "UILabel"; text = "DuckDuckGo Desktop App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "Application de bureau DuckDuckGo"; +/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ +"Yz9-17-qnn.text" = "Application DuckDuckGo pour Mac"; /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Nouvel onglet"; diff --git a/DuckDuckGo/hr.lproj/Feedback.strings b/DuckDuckGo/hr.lproj/Feedback.strings index 5cfc55ead8..db97dd3363 100644 --- a/DuckDuckGo/hr.lproj/Feedback.strings +++ b/DuckDuckGo/hr.lproj/Feedback.strings @@ -28,9 +28,6 @@ /* Class = "UIBarButtonItem"; title = "Back"; ObjectID = "JF8-Ow-zXL"; */ "JF8-Ow-zXL.title" = "Natrag"; -/* Class = "UIBarButtonItem"; title = "Submit"; ObjectID = "jr6-Al-4fr"; */ -"jr6-Al-4fr.title" = "Pošalji"; - /* Class = "UIButton"; normalTitle = "Submit"; ObjectID = "lsB-Kq-U8p"; */ "lsB-Kq-U8p.normalTitle" = "Pošalji"; @@ -58,9 +55,6 @@ /* Class = "UINavigationItem"; title = "Report Broken Site"; ObjectID = "WjT-HM-yim"; */ "WjT-HM-yim.title" = "Prijavi neispravno web-mjesto"; -/* Class = "UILabel"; text = "Broken Site Reason"; ObjectID = "Wx3-Vb-ItS"; */ -"Wx3-Vb-ItS.text" = "Razlog za neispravno web-mjesto"; - /* Class = "UINavigationItem"; title = "Share Feedback"; ObjectID = "Xb0-Zu-opM"; */ "Xb0-Zu-opM.title" = "Podijeli povratne informacije"; diff --git a/DuckDuckGo/hr.lproj/Localizable.strings b/DuckDuckGo/hr.lproj/Localizable.strings index 65fb6c8599..47220b3f4c 100644 --- a/DuckDuckGo/hr.lproj/Localizable.strings +++ b/DuckDuckGo/hr.lproj/Localizable.strings @@ -1,6 +1,9 @@ /* No comment provided by engineer. */ "%@" = "%@"; +/* No comment provided by engineer. */ +"%@ [%@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)" = "%1$@ [%2$@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)"; + /* Buton label for Edit action */ "action.generic.edit" = "Uredi"; @@ -103,6 +106,12 @@ /* Share action */ "action.title.share" = "Podijeli"; +/* Settings label for bottom position for the address bar */ +"address.bar.bottom" = "Dno"; + +/* Settings label for top position for the address bar */ +"address.bar.top" = "Vrh"; + /* No comment provided by engineer. */ "addWidget.button" = "Dodaj widget"; @@ -142,6 +151,225 @@ /* Shown on authentication screen */ "app.authentication.unlock" = "Otključaj DuckDuckGo."; +/* First part of about page content (note the trailing space) */ +"appTP.about.content1" = "You’ve probably heard about companies like Google and Facebook tracking you behind the scenes on third-party websites. But did you know they also track your personal information through apps on your device?\n\nIn 2022, DuckDuckGo found that "; + +/* Second part of about page content (note the trailing space) */ +"appTP.about.content2" = "over 85% of free iOS apps tested contained hidden trackers from other companies."; + +/* Third part of about page content (note the leading space) */ +"appTP.about.content3" = " Of the 395 apps tested, 60% sent data to Google. This happens even while you’re not using your device.\n\nTrackers in apps may have access to a lot more information than their website tracker cousins, such as your location down to which floor of a building you're on, how often you play games while at work, and when and how long you sleep each day. Even if you haven’t given apps explicit permission to collect data, they can still take it without your knowledge.\n\nTracking networks like Facebook and Google use these little pieces of information to build a digital profile about you. With it, tracking networks can manipulate what you see online and allow advertisers to bid on access to you based on your data.\n\nTrackers in apps is a BIG problem for privacy. But DuckDuckGo has a solution that can help.\n\nWhen enabled in the DuckDuckGo Privacy Browser app, App Tracking Protection blocks many trackers in other apps, not just the trackers we find on websites when you browse. These dual layers of protection reduce what companies know about you overall, so you can use your apps with more peace of mind, knowing you’re more protected."; + +/* Navigation Title for AppTP about page */ +"appTP.about.navTitle" = "About App Trackers"; + +/* Title for AppTP about page */ +"appTP.about.title" = "What Are App Trackers?"; + +/* Title for 'Report an Issue' button in the activity view. */ +"appTP.activityView.reportIssue" = "Report Issue"; + +/* Text label for switch that turns blocking on or off for a tracker */ +"appTP.blockTrackerText" = "Block this Tracker"; + +/* Detail string describing what AppTP is */ +"appTP.cell.detail" = "Blokiraj aplikacija za praćenje na tvom uređaju"; + +/* String indicating AppTP is disabled when viewed from the settings screen */ +"appTP.cell.disabled" = "Onemogućeno"; + +/* String indicating AppTP is enabled when viewed from the settings screen */ +"appTP.cell.enabled" = "Omogućeno"; + +/* Info string informing the user what App Tracking Protection does. */ +"appTP.empty.disabled.info" = "Omogući App Tracking Protection kako bismo mogli blokirati dosadne \"tragače\" u drugim aplikacijama."; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.heading" = "We’re blocking hidden trackers"; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.info" = "Come back soon to see a list of all the app trackers we’ve blocked."; + +/* First answer for AppTP FAQ page */ +"appTP.faq.answer1" = "App Tracking Protection blocks app trackers from other companies, like when Facebook tries to track you in a banking app. Companies may still track you in apps they own."; + +/* Second answer for AppTP FAQ page */ +"appTP.faq.answer2" = "Yes! App Tracking Protection works across all apps on your device to block the most common hidden trackers we find trying to collect your personal info."; + +/* Third answer for AppTP FAQ page */ +"appTP.faq.answer3" = "We currently only block the most common trackers that we find on iOS. This helps us to comprehensively test App Tracking Protection and lower frequency of app breakage, while blocking up to 70% of all tracking requests."; + +/* Fourth answer for AppTP FAQ page */ +"appTP.faq.answer4" = "You’ll be asked to set up a virtual private network (VPN) connection, but you don't need to install a VPN app for App Tracking Protection to work.\n\nThis permission, which works only on your device, allows App Tracking Protection to monitor network traffic so that it can block known trackers."; + +/* Fifth answer for AppTP FAQ page */ +"appTP.faq.answer5" = "You can use App Tracking Protection at the same time as using an IKEv2 protocol VPN app on an iOS device. You won’t be able to use App Tracking Protection on an iOS device if you’re using a VPN app that uses a different type of protocol, like WireGuard or OpenVPN type VPNs."; + +/* Sixth answer for AppTP FAQ page */ +"appTP.faq.answer6" = "A VPN sends your data from the device to its own server, where it secures and anonymizes your data from prying eyes. However, this allows the VPN company to see your network traffic.\n\nApp Tracking Protection is different. Instead of sending your data to a VPN server, App Tracking Protection works only on your device, sitting between your apps and the servers they talk to.\n\nWhenever App Tracking Protection recognizes a known tracker, it blocks the tracker from sending personal information (such as your IP address, activity, and device details) off your device. All other traffic reaches its destination, so your apps work normally."; + +/* Seventh answer for AppTP FAQ page */ +"appTP.faq.answer7" = "App Tracking Protection works only on your device and doesn’t send your data off your device to DuckDuckGo. We don’t collect or store any data from your apps."; + +/* First question for AppTP FAQ page */ +"appTP.faq.question1" = "How does App Tracking Protection work?"; + +/* Second question for AppTP FAQ page */ +"appTP.faq.question2" = "Does App Tracking Protection block trackers in all apps on my device?"; + +/* Third question for AppTP FAQ page */ +"appTP.faq.question3" = "Does App Tracking Protection block all app trackers?"; + +/* Fourth question for AppTP FAQ page */ +"appTP.faq.question4" = "Why does App Tracking Protection use a VPN connection?"; + +/* Fifth question for AppTP FAQ page */ +"appTP.faq.question5" = "Will App Tracking Protection work if I also use a VPN app?"; + +/* Sixth question for AppTP FAQ page */ +"appTP.faq.question6" = "How is App Tracking Protection different from a VPN?"; + +/* Seventh question for AppTP FAQ page */ +"appTP.faq.question7" = "Is my data private?"; + +/* Title for AppTP FAQ page */ +"appTP.faq.title" = "App Tracking Protection FAQ"; + +/* Do not translate. StringsDict entry -- Count part of string 'App Tracking Protection blocked x tracking attempts today' */ +"appTP.home.blockedCount" = "appTP.home.blockedCount"; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the trailing space) */ +"appTP.home.blockedPrefix" = "App Tracking Protection danas je blokirao "; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the leading space) */ +"appTP.home.blockedSuffix" = " u tvojim aplikacijama."; + +/* Prefix of string 'App Tracking Protection disabled. Tap to re-enable.' (note the trailing space) */ +"appTP.home.disabledPrefix" = "App Tracking Protection disabled. "; + +/* Suffix of string 'App Tracking Protection disabled. Tap to re-enable.' */ +"appTP.home.disabledSuffix" = "Tap to continue blocking tracking attempts across your apps."; + +/* Text indicating the tracking event occured 'just now'. Example: Last attempt 'just now' */ +"appTP.justNow" = "Upravo sada"; + +/* View to manage trackers for AppTP. Allows the user to turn trackers on or off. */ +"appTP.manageTrackers" = "Upravljanje alatima za praćenje"; + +/* Button title for AppTP onboarding */ +"appTP.onboarding.continueButton" = "Continue"; + +/* Button title for AppTP onboarding to enable AppTP */ +"appTP.onboarding.enableeButton" = "Enable App Tracking Protection"; + +/* Button title for AppTP onboarding to learn more about AppTP */ +"appTP.onboarding.learnMoreButton" = "Learn More"; + +/* First part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info1" = "Over 85% of free iOS apps"; + +/* Second part of info on the first AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page1Info2" = " we’ve tested allow other companies to track your personal information, even when you’re sleeping."; + +/* Third part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info3" = "See who we catch trying to track you in your apps and take back control."; + +/* First part of info on the second AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page2Info1" = "App Tracking Protection "; + +/* Second part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info2" = "detects and blocks app trackers from other companies,"; + +/* Third part of info on the second AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page2Info3" = " like when Google attempts to track you in a health app."; + +/* Fourth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info4" = "It’s free,"; + +/* Fifth part of info on the second AppTP onboarding page (note the leading and trailing space) */ +"appTP.onboarding.page2Info5" = " and you can enjoy your apps as you normally would. Working in the background, it helps "; + +/* Sixth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info6" = "protect you night and day."; + +/* First part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info1" = "App Tracking Protection is not a VPN."; + +/* Second part of info on the third AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page3Info2" = " However, your device will recognize it as one. This is because it uses a local VPN connection to work."; + +/* Third part of info on the third AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page3Info3" = "App Tracking Protection is different. "; + +/* Fourth part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info4" = "It never routes app data through an external server."; + +/* Title for first AppTP onboarding page */ +"appTP.onboarding.title1" = "One easy step for better app privacy!"; + +/* Title for second AppTP onboarding page */ +"appTP.onboarding.title2" = "How does it work?"; + +/* Title for third AppTP onboarding page */ +"appTP.onboarding.title3" = "Who sees your data?"; + +/* Breakage report app name label */ +"appTP.report.appLabel" = "Which app is having issues?"; + +/* Breakage report app name placeholder */ +"appTP.report.appPlaceholder" = "App name"; + +/* Breakage report category label */ +"appTP.report.categoryLabel" = "Što se događa?"; + +/* Breakage report comment label */ +"appTP.report.commentLabel" = "Comments"; + +/* Breakage report comment placeholder */ +"appTP.report.commentPlaceholder" = "Add additional details"; + +/* Breakage report footer explaining what is collected in the breakage report */ +"appTP.report.footer" = "In addition to the details entered into this form, your app issue report will contain:\n• A list of trackers blocked in the last 10 min\n• Whether App Tracking Protection is enabled\n• Aggregate DuckDuckGo app diagnostics"; + +/* Breakage report submit button */ +"appTP.report.submit" = "Pošalji"; + +/* Breakage report form title */ +"appTP.report.title" = "Report Issue"; + +/* Breakage report succcess message */ +"appTP.report.toast" = "Hvala! Povratne su informacije su poslane."; + +/* Cancel button for 'Report an Issue' alert. */ +"appTP.reportAlert.cancel" = "Ne sada"; + +/* Confirm button for 'Report an Issue' alert. */ +"appTP.reportAlert.confirm" = "Prijavi problem"; + +/* Message for 'Report an Issue' alert. */ +"appTP.reportAlert.message" = "Let us know if you disabled App Tracking Protection for this specific tracker because it caused app issues. Your feedback helps us improve!"; + +/* Title for 'Report an Issue' alert. */ +"appTP.reportAlert.title" = "Report Issue?"; + +/* Toast notification diplayed after restoring the blocklist to default settings */ +"appTP.restoreDefaultsToast" = "Default settings restored"; + +/* Button to restore the blocklist to its default state. */ +"appTP.restoreDefualts" = "Vrati zadano"; + +/* Title for the App Tracking Protection feature */ +"appTP.title" = "Zaštita od praćenja aplikacija"; + +/* Text indicating when the tracker was last allowed. Example: Last attempt allowed (timeString) */ +"appTP.trackerAllowedTimestamp" = "Zadnji pokušaj je dopušten %@"; + +/* Text indicating when the tracker was last blocked. Example: Last attempt blocked (timeString) */ +"appTP.trackerBlockedTimestamp" = "Zadnji pokušaj je blokiran %@"; + +/* Do not translate. StringsDict entry -- Subtitle for tracking attempts in App Tracking Protection Activity View. Example: (count) tracking attempts */ +"appTP.trackingattempts" = "appTP.trackingattempts"; + /* Authentication Alert Sign In Button */ "auth.alert.login.button" = "Prijava"; @@ -301,6 +529,18 @@ /* Toast message when a login item without a title is deleted */ "autofill.logins.list.login-deleted-message-no-title" = "Prijava je izbrisana"; +/* Title for a button that allows a user to reset their list of never saved sites */ +"autofill.logins.list.never.saved" = "Vrati izvorne isključene web lokacije"; + +/* Cancel button for resetting list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.cancel" = "Otkaži"; + +/* Confirm button to reset list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.confirm" = "Resetiraj isključene web lokacije"; + +/* Alert title */ +"autofill.logins.list.never.saved.reset.action.title" = "Ako resetiraš isključene web lokacije, od tebe će se zatražiti da spremiš svoju prijavu sljedeći put kad se prijaviš na bilo koju od ovih lokacija."; + /* Placeholder for search field on autofill login listing */ "autofill.logins.list.search-placeholder" = "Pretraživanje prijava"; @@ -335,7 +575,7 @@ "autofill.logins.prompt.password.button.title" = "Lozinka za %@"; /* Title for autofill login prompt */ -"autofill.logins.prompt.title" = "Koristiti spremljene podatke za prijavu?"; +"autofill.logins.prompt.title" = "Koristiti spremljenu lozinku?"; /* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ "autofill.logins.search.no-results.subtitle" = "za '%@'"; @@ -385,27 +625,24 @@ /* Title for the alert dialog telling the user an updated username is no longer a private email address */ "autofill.removed.duck.address.title" = "Korisničko ime privatne Duck Address adrese uklonjeno je"; +/* CTA displayed on modal asking if the user never wants to be prompted to save a login for this website agin */ +"autofill.save-login.never-prompt.CTA" = "Nikada ne traži za ovu web lokaciju"; + /* Message displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.message" = "Prijave su sigurno pohranjene na tvom uređaju u izborniku Prijave."; +"autofill.save-login.new-user.message" = "Lozinke su sigurno pohranjene na tvom uređaju u izborniku Prijava."; /* Title displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.title" = "Želiš li da DuckDuckGo spremi tvoju prijavu?"; +"autofill.save-login.new-user.title" = "Želiš li da DuckDuckGo spremi tvoju lozinku?"; /* Cancel CTA displayed on modal asking for the user to save the login */ "autofill.save-login.not-now.CTA" = "Nemoj spremiti"; -/* Confirm CTA displayed on modal asking for the user to save the login */ -"autofill.save-login.save.CTA" = "Spremi prijavu"; - /* Title displayed on modal asking for the user to save the login */ "autofill.save-login.title" = "Želiš li spremiti prijavu?"; /* Confirm CTA displayed on modal asking for the user to save the password */ "autofill.save-password.save.CTA" = "Spremi lozinku"; -/* Title displayed on modal asking for the user to save the password */ -"autofill.save-password.title" = "Želiš li spremiti lozinku?"; - /* Accessibility title for a Show Password button displaying actial password instead of ***** */ "autofill.show-password" = "Pokaži lozinku"; @@ -413,7 +650,7 @@ "autofill.signin.to.manage" = "%@ za upravljanje tvojim Duck Address adresama na ovom uređaju."; /* Message displayed on modal asking for the user to update the password */ -"autofill.update-password.message" = "DuckDuckGo će ažurirati ovu spremljenu prijavu na tvom uređaju."; +"autofill.update-password.message" = "DuckDuckGo će ažurirati ovu pohranjenu lozinku na tvom uređaju."; /* Confirm CTA displayed on modal asking for the user to update the password */ "autofill.update-password.save.CTA" = "Ažuriraj lozinku"; @@ -442,6 +679,15 @@ /* Placeholder in the add bookmark form */ "bookmark.address.placeholder" = "www.example.com"; +/* Delete bookmark alert message */ +"bookmark.delete.alert.message" = "Ovo će izbrisati tvoju oznaku za \"%@\""; + +/* Delete bookmark alert title */ +"bookmark.delete.alert.title" = "Delete?"; + +/* The message shown after a bookmark has been deleted */ +"bookmark.deleted.toast" = "Knjižna oznaka je izbrisana"; + /* Delete bookmark folder alert delete button */ "bookmark.deleteFolderAlert.deleteButton" = "Izbriši"; @@ -538,9 +784,21 @@ /* Broken Site Category */ "brokensite.category.videos" = "Videozapis se nije reproducirao"; +/* Broken Site Category Placeholder */ +"brokensite.categoryPlaceholder" = "Odaberi svoj problem s popisa..."; + +/* Broken Site Category Section Title */ +"brokensite.categoryTitle" = "ODABERI KATEGORIJU"; + +/* Broken Site Comment Placeholder */ +"brokensite.commentPlaceholder" = "Dijeljenje više pojedinosti može nam pomoći u rješavanju ovog problema"; + /* Broken Site Section Title */ "brokensite.sectionTitle" = "OPIŠITE ŠTO SE DOGODILO"; +/* No comment provided by engineer. */ +"bucket: %@" = "bucket: %@"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Prošli mjesec"; @@ -772,6 +1030,39 @@ /* Empty search placeholder on bookmarks search */ "empty.search" = "Nema pronađenih rezultata"; +/* No comment provided by engineer. */ +"Error" = "Pogreška"; + +/* Button title to Sign In */ +"error.email-protection-sign-in.action" = "Prijava"; + +/* Alert message */ +"error.email-protection-sign-in.body" = "Žao nam je, prijavi se ponovno kako bi se ponovno omogućile značajke zaštite e-pošte (Email Protection) u ovom pregledniku."; + +/* Alert title */ +"error.email-protection-sign-in.title" = "Email Protection pogreška"; + +/* Button title to open device settings */ +"error.insufficient-disk-space.action" = "Otvori Postavke"; + +/* Alert message */ +"error.insufficient-disk-space.body" = "Čini se da je na tvom uređaju ponestalo prostora za pohranu. Oslobodi prostor za nastavak."; + +/* Alert title */ +"error.insufficient-disk-space.title" = "Nema dovoljno prostora za pohranu"; + +/* Button title that is shutting down the app */ +"error.preemptive-crash.action" = "Zatvori aplikaciju"; + +/* Alert message */ +"error.preemptive-crash.body" = "Čini se da postoji problem s aplikacijom i ona se mora zatvoriti. Ponovno je otvori za nastavak rada."; + +/* Alert title */ +"error.preemptive-crash.title" = "Otkriven je problem s aplikacijom"; + +/* Generic error message on a dialog for when the cause is not known. */ +"error.unknown.try.again" = "An unknown error has occurred"; + /* No comment provided by engineer. */ "favorite" = "Omiljeno"; @@ -991,6 +1282,18 @@ /* Home tab title */ "homeTab.title" = "Početni zaslon"; +/* OK title for invite screen alert dismissal button */ +"invite.alert.ok.button" = "OK"; + +/* Continue button on an invite dialog */ +"invite.dialog.continue.button" = "Continue"; + +/* Get Started button on an invite dialog */ +"invite.dialog.get.started.button" = "Get Started"; + +/* Message to show after user enters an unrecognized invite code */ +"invite.dialog.unrecognized.code.message" = "We didn’t recognize this Invite Code."; + /* No comment provided by engineer. */ "keyCommandAddBookmark" = "Dodaj knjižnu oznaku"; @@ -1057,15 +1360,15 @@ /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "Aplikacija DuckDuckGo za Mac pruža ti potrebnu brzinu i značajke pretraživanja kakve očekuješ, a isporučuje se s našim najboljim alatima za privatnost u svojoj klasi - privacy essentials."; -/* Body text for the macOS waitlist notification */ -"mac-waitlist.available.notification.body" = "Otvori svoju pozivnicu"; - /* Title for the macOS waitlist notification */ "mac-waitlist.available.notification.title" = "DuckDuckGo za Mac je spreman!"; /* Title for the copy action */ "mac-waitlist.copy" = "Kopiraj"; +/* Description text above the Share Link button */ +"mac-waitlist.join-waitlist-screen.on-your-computer-go-to" = "Na računalu sa sustavom Windows idi na:"; + /* Description text above the Share Link button */ "mac-waitlist.join-waitlist-screen.on-your-mac-go-to" = "Na Macu idi na:"; @@ -1078,6 +1381,9 @@ /* Disclaimer for the Join Waitlist screen */ "mac-waitlist.join-waitlist-screen.windows" = "Uskoro i za Windows!"; +/* Title for the macOS waitlist button redirecting to Windows waitlist */ +"mac-waitlist.join-waitlist-screen.windows-waitlist" = "Tražiš verziju za Windows?"; + /* Title for the settings subtitle */ "mac-waitlist.settings.browse-privately" = "Pretražujte privatno s našom aplikacijom za Mac"; @@ -1088,7 +1394,7 @@ "mac-waitlist.share-sheet.title" = "DuckDuckGo za Mac"; /* Title for the Mac Waitlist feature */ -"mac-waitlist.title" = "DuckDuckGo aplikacija za osobno računalo"; +"mac-waitlist.title" = "DuckDuckGo aplikacija za Mac"; /* No comment provided by engineer. */ "menu.button.hint" = "Izbornik pregledavanja"; @@ -1105,9 +1411,81 @@ /* Edit button */ "navigation.title.edit" = "Uredi"; +/* String indicating NetP is connected when viewed from the settings screen */ +"netP.cell.connected" = "Connected"; + +/* String indicating NetP is disconnected when viewed from the settings screen */ +"netP.cell.disconnected" = "Not connected"; + +/* Title for the Network Protection feature */ +"netP.title" = "Network Protection"; + +/* Message for the network protection invite dialog */ +"network.protection.invite.dialog.message" = "Enter your invite code to get started."; + +/* Title for the network protection invite screen */ +"network.protection.invite.dialog.title" = "You're invited to try Network Protection"; + +/* Prompt for the network protection invite code text field */ +"network.protection.invite.field.prompt" = "Invite Code"; + +/* Message explaining that netP is invite only */ +"network.protection.invite.only.message" = "DuckDuckGo Network Protection is currently invite-only."; + +/* Message for the network protection invite success view */ +"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; + +/* Title for the network protection invite success view */ +"network.protection.invite.success.title" = "Success! You’re in."; + +/* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ +"network.protection.status.connected.format" = "Connected - %@"; + +/* The label for the NetP VPN when connecting */ +"network.protection.status.connecting" = "Connecting..."; + +/* The label for the NetP VPN when disconnected */ +"network.protection.status.disconnected" = "Not connected"; + +/* The label for the NetP VPN when disconnecting */ +"network.protection.status.disconnecting" = "Disconnecting..."; + +/* Message label text for the netP status view */ +"network.protection.status.header.message" = "DuckDuckGo's VPN secures all of your device's Internet traffic anytime, anywhere."; + +/* Header title label text for the status view when netP is disconnected */ +"network.protection.status.header.title.off" = "Network Protection is Off"; + +/* Header title label text for the status view when netP is connected */ +"network.protection.status.header.title.on" = "Network Protection is On"; + +/* The status view 'Share Feedback' button which is shown inline on the status view after the \(netPInviteOnlyMessage) text */ +"network.protection.status.menu.share.feedback" = "Share Feedback"; + +/* Connection details label shown in NetworkProtection's status view. */ +"network.protection.status.view.connection.details" = "Connection Details"; + +/* Generic connection failed error message shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.message" = "Please try again later."; + +/* Generic connection failed error title shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; + +/* IP Address label shown in NetworkProtection's status view. */ +"network.protection.status.view.ip.address" = "IP Address"; + +/* Location label shown in NetworkProtection's status view. */ +"network.protection.status.view.location" = "Location"; + +/* Title label text for the status view when netP is disconnected */ +"network.protection.status.view.title" = "Network Protection"; + /* Do not translate - stringsdict entry */ "number.of.tabs" = "number.of.tabs"; +/* No comment provided by engineer. */ +"Ok" = "U redu"; + /* Text displayed on notification appearing in the address bar when the browser dismissed the cookie popup automatically rejecting it */ "omnibar.notification.cookies-managed" = "Upravlja se kolačićima"; @@ -1147,6 +1525,18 @@ /* ’Link’ is link on a website */ "open.externally.failed" = "Nije moguće pronaći aplikaciju koja je potrebna za otvaranje te poveznice"; +/* No comment provided by engineer. */ +"Please enter which app on your device is broken." = "Navedi koja je aplikacija na tvom uređaju neispravna."; + +/* Activate button */ +"pm.activate" = "Reactivate"; + +/* Cancel button */ +"pm.cancel" = "Cancel"; + +/* Deactivate button */ +"pm.deactivate" = "Deactivate"; + /* No comment provided by engineer. */ "preserveLogins.domain.list.footer" = "Web-mjesta oslanjaju se na kolačiće kako bi te zadržala prijavljenim/om. Kada je web-mjesto označeno kao zaštićeno, kolačići neće biti izbrisani i ostat ćeš prijavljen/a čak i nakon upotrebe gumba Fire. I dalje blokiramo alate za praćenje trećih strana koje pronađemo na mrežnim mjestima označenim kao Fireproof."; @@ -1216,8 +1606,8 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Omiljeno"; -/* No comment provided by engineer. */ -"settings.about.text" = "U DuckDuckGou postavljamo novi standard povjerenja na internetu.\n\nDuckDuckGo anonimni je preglednik koji pruža sve što ti je potrebno da se zaštitiš dok pretražuješ i pregledavaš internet, uključujući blokiranje alata za praćenje, pametnije šifriranje i privatno pretraživanje uz pomoć DuckDuckGoa.\n\nUostalom, internet ne bi trebao biti neugodno mjesto, a zaštita privatnosti na internetu koju zaslužuješ trebala bi biti jednostavna poput spuštanja roleta."; +/* about page */ +"settings.about.text" = "DuckDuckGo neovisna je tvrtka za privatnost na internetu osnovana 2008. godine. Namijenjena je svakome tko je umoran od toga da ga prate na internetu i želi jednostavno rješenje za to. Mi smo dokaz da je prava zaštita privatnosti na internetu moguća bez kompromisa.\n\nPreglednik DuckDuckGo dolazi sa značajkama koje očekujete od preglednika, kao što su oznake, kartice, lozinke i još mnogo toga, plus više od [desetak moćnih zaštita privatnosti](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/) nije ponuđeno u većini popularnih preglednika prema zadanim postavkama. Ovaj jedinstveno sveobuhvatan skup zaštita privatnosti pomaže u zaštiti tvojih mrežnih aktivnosti, od pretraživanja do pregledavanja, slanja e-pošte i još mnogo toga.\n\nNaša zaštita privatnosti funkcionira bez potrebe da znamo bilo što o tehničkim detaljima ili da se bavimo kompliciranim postavkama. Sve što trebaš učiniti jest prebaciti svoj preglednik na DuckDuckGo na svim svojim uređajima i prema zadanim postavkama dobivaš našu zaštitu privatnosti.\n\nAli ako *želite* zaviriti \"ispod haube\", više o tome kako DuckDuckGo zaštita privatnosti funkcionira možeš pronaći na našim [stranicama pomoći](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/)."; /* Report a Broken Site screen confirmation button */ "siteFeedback.buttonText" = "Pošalji izvješće"; @@ -1237,6 +1627,9 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Koje je web-mjesto neispravno?"; +/* No comment provided by engineer. */ +"sync.remove-device.message" = "\"%@\" više neće moći pristupiti tvojim sinkroniziranim podacima."; + /* Accessibility label on remove button */ "tab.close.home" = "Zatvori početnu karticu"; @@ -1309,6 +1702,57 @@ /* Voice-search footer note with on-device privacy warning */ "voiceSearch.footer.note" = "Zvuk se obrađuje na uređaju. Ne pohranjuje se i ne dijeli ni s kim, uključujući DuckDuckGo."; +/* Title for the button to enable push notifications in system settings */ +"waitlist.allow-notifications" = "Dopusti obavijesti"; + +/* Body text for the waitlist notification */ +"waitlist.available.notification.body" = "Otvori svoju pozivnicu"; + +/* Title for the copy action */ +"waitlist.copy" = "Kopiraj"; + +/* Label text for the invite code */ +"waitlist.invite-code" = "Pozivni kôd"; + +/* Step title on the invite screen */ +"waitlist.invite-screen.step.title" = "Korak %d"; + +/* Title for the invite code screen */ +"waitlist.invite-screen.youre-invited" = "Pozvani ste!"; + +/* Title for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.join" = "Pridružite se privatnoj listi čekanja"; + +/* Temporary status text for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.joining" = "Pridruživanje listi čekanja..."; + +/* Title for the Share Link button */ +"waitlist.join-waitlist-screen.share-link" = "Podijeli poveznicu"; + +/* Notification text for the waitlist */ +"waitlist.joined.no-notification.get-notification" = "OBAVIJESTI ME"; + +/* Title for the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.get-notification-confirmation-title" = "Primi obavijest kada dođeš na red?"; + +/* Cancel button in the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.no-thanks" = "Ne, hvala"; + +/* Text used for the Notifications Disabled state */ +"waitlist.notification.disabled" = "Možemo vas obavijestiti kada dođete na red, ali obavijesti su trenutačno onemogućene za DuckDuckGo."; + +/* Privacy disclaimer for the Waitlist feature */ +"waitlist.privacy-disclaimer" = "Nećete morati podijeliti nikakve osobne podatke za pridruživanje listi čekanja. Svoje ćete mjesto osigurati u skladu s vremenskom oznakom koja postoji samo na vašem uređaju kako bismo vas mogli obavijestiti kada na vas dođe red."; + +/* Title for the queue screen */ +"waitlist.queue-screen.on-the-list" = "Na popisu ste!"; + +/* Title for the settings subtitle */ +"waitlist.settings.download-available" = "Preuzimanje je dostupno"; + +/* Title for the share sheet entry */ +"waitlist.share-sheet.title" = "Pozvani ste!"; + /* Confirmation message */ "web.url.remove.favorite.done" = "Omiljena je stavka uklonjena"; @@ -1333,3 +1777,51 @@ /* Alert title explaining the message is shown by a website */ "webJSAlert.website-message.format" = "Poruka od %@:"; +/* No comment provided by engineer. */ +"Welcome to the Duck Side!" = "Dobrodošli na našu stranu!"; + +/* Title for the Windows waitlist notification */ +"windows-waitlist.available.notification.title" = "Nabavi DuckDuckGo za Windows!"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-1.description" = "Posjeti ovaj URL na svom Windows uređaju za preuzimanje:"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-2.description" = "Otvori instalacijsku datoteku za DuckDuckGo pod Preuzimanja, odaberi Instaliraj, zatim unesi svoju pozivnu šifru."; + +/* Subtitle for the Windows Waitlist Invite screen */ +"windows-waitlist.invite-screen.subtitle" = "Jesi li spreman koristiti DuckDuckGo u sustavu Windows?"; + +/* Title for the Windows waitlist button redirecting to Mac waitlist */ +"windows-waitlist.join-waitlist-screen.mac-waitlist" = "Tražiš verziju za Mac?"; + +/* Title for the Join Windows Waitlist screen */ +"windows-waitlist.join-waitlist-screen.try-duckduckgo-for-windows" = "Možeš dobiti rani pristup da isprobaš DuckDuckGo za Windows!"; + +/* Message for the alert to confirm enabling notifications */ +"windows-waitlist.joined.no-notification.get-notification-confirmation-message" = "Poslat ćemo ti obavijest kad tvoj primjerak programa DuckDuckGo za Windows bude spreman za preuzimanje. "; + +/* Label text for the Joined Waitlist state with notifications declined */ +"windows-waitlist.joined.notifications-declined" = "Tvoja pozivnica da isprobaš DuckDuckGo za Windows stići će ovdje. Provjerite ponovno uskoro, ili vam možemo poslati obavijest kada dođete na red."; + +/* Label text for the Joined Waitlist state with notifications enabled */ +"windows-waitlist.joined.notifications-enabled" = "Poslat ćemo ti obavijest kad tvoj primjerak programa DuckDuckGo za Windows bude spreman za preuzimanje."; + +/* Title for the settings subtitle */ +"windows-waitlist.settings.browse-privately" = "Privatno pregledavanje s našom aplikacijom za Windows"; + +/* Message used when sharing to iMessage. Parameter is an eight digit invite code. */ +"windows-waitlist.share-sheet.invite-code-message" = "Imaš poziv!\n\nJesi li spreman koristiti DuckDuckGo u sustavu Windows?1. korak\n Posjeti ovaj URL na svom Windows uređaju za preuzimanje: \n https://duckduckgo.com/windows\n\n2. korak\nOtvori instalacijsku datoteku za DuckDuckGo pod Preuzimanja, odaberi Instaliraj, zatim unesi svoju pozivnu šifru.\n\nPozivna šifra: %@"; + +/* Message used when sharing to iMessage */ +"windows-waitlist.share-sheet.message" = "Jesi li spreman za privatno pregledavanje u sustavu Windows?\n\nPosjeti ovaj URL na računalu za preuzimanje:\nhttps://duckduckgo.com/windows"; + +/* Summary text for the Windows browser waitlist */ +"windows-waitlist.summary" = "DuckDuckGo za Windows ima sve što ti je potrebno za pregledavanje s više privatnosti - privatno pretraživanje, blokiranje praćenja, prisilno šifriranje i blokiranje skočnih prozora kolačića, a u skoroj budućnosti dodat ćemo još vrhunskih značajki zaštite."; + +/* Title for the Windows Waitlist feature */ +"windows-waitlist.title" = "Aplikacija DuckDuckGo za Windows"; + +/* Title for the Windows browser download link page */ +"windows-waitlist.waitlist-download-screen.try-duckduckgo-for-windows" = "Nabavi DuckDuckGo za Windows!"; + diff --git a/DuckDuckGo/hr.lproj/Settings.strings b/DuckDuckGo/hr.lproj/Settings.strings index fc22982024..522994e162 100644 --- a/DuckDuckGo/hr.lproj/Settings.strings +++ b/DuckDuckGo/hr.lproj/Settings.strings @@ -37,6 +37,9 @@ /* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ "9Ko-0g-T3h.text" = "Veličina teksta"; +/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ +"9kt-6R-XiZ.text" = "Naslov"; + /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Pokretanje aplikacije"; @@ -73,6 +76,12 @@ /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Globalna kontrola privatnosti (GPC)"; +/* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ +"CxT-QK-iVn.title" = "Dodaj"; + +/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ +"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; + /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Izlaz iz aplikacije, neaktivna 15 minuta"; @@ -151,6 +160,9 @@ /* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ "HLr-R8-xxF.text" = "Pretpregledi na dugi pritisak"; +/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ +"hoT-Nu-KXP.text" = "Privatno pregledavanje s našom aplikacijom za Windows"; + /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Zaštita privatnosti omogućena za sva web-mjesta"; @@ -163,6 +175,12 @@ /* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ "m23-t6-9cb.text" = "Podijeli povratne informacije"; +/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ +"mLn-1x-Fl5.title" = "Položaj adresne trake"; + +/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ +"mqV-pf-NZ1.text" = "Položaj adresne trake"; + /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Neka DuckDuckGo upravlja skočnim prozorima za pristanak na kolačiće"; @@ -175,15 +193,27 @@ /* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ "oM7-1o-9oY.text" = "O DuckDuckGou"; +/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ +"opn-JO-idF.text" = "Vrh"; + /* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ "OxE-MQ-uJk.headerTitle" = "Više od DuckDuckGoa"; +/* Class = "UILabel"; text = "Sync"; ObjectID = "oXN-ez-gct"; */ +"oXN-ez-gct.text" = "Sinkronizacija"; + /* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ "P0F-ts-ekd.text" = "Pretražujte privatno s našom aplikacijom za Mac "; +/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ +"qah-gb-udB.text" = "Network Protection"; + /* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ "qeN-SV-zy7.text" = "Label"; +/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ +"RQ8-H1-Ez1.text" = "Aplikacija DuckDuckGo za Windows"; + /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Veličina teksta"; @@ -232,8 +262,8 @@ /* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ "yvj-LL-MiR.text" = "Dodajte aplikaciju na Dock"; -/* Class = "UILabel"; text = "DuckDuckGo Desktop App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "DuckDuckGo aplikacija za osobno računalo"; +/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ +"Yz9-17-qnn.text" = "Aplikacija DuckDuckGo za Mac"; /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Nova kartica"; diff --git a/DuckDuckGo/hu.lproj/Feedback.strings b/DuckDuckGo/hu.lproj/Feedback.strings index 8007479902..ad536e2dd3 100644 --- a/DuckDuckGo/hu.lproj/Feedback.strings +++ b/DuckDuckGo/hu.lproj/Feedback.strings @@ -28,9 +28,6 @@ /* Class = "UIBarButtonItem"; title = "Back"; ObjectID = "JF8-Ow-zXL"; */ "JF8-Ow-zXL.title" = "Vissza"; -/* Class = "UIBarButtonItem"; title = "Submit"; ObjectID = "jr6-Al-4fr"; */ -"jr6-Al-4fr.title" = "Elküldés"; - /* Class = "UIButton"; normalTitle = "Submit"; ObjectID = "lsB-Kq-U8p"; */ "lsB-Kq-U8p.normalTitle" = "Elküldés"; @@ -58,9 +55,6 @@ /* Class = "UINavigationItem"; title = "Report Broken Site"; ObjectID = "WjT-HM-yim"; */ "WjT-HM-yim.title" = "Hibás weboldal jelentése"; -/* Class = "UILabel"; text = "Broken Site Reason"; ObjectID = "Wx3-Vb-ItS"; */ -"Wx3-Vb-ItS.text" = "Webhelyhiba oka"; - /* Class = "UINavigationItem"; title = "Share Feedback"; ObjectID = "Xb0-Zu-opM"; */ "Xb0-Zu-opM.title" = "Visszajelzés megosztása"; diff --git a/DuckDuckGo/hu.lproj/Localizable.strings b/DuckDuckGo/hu.lproj/Localizable.strings index 900f197b78..340223040d 100644 --- a/DuckDuckGo/hu.lproj/Localizable.strings +++ b/DuckDuckGo/hu.lproj/Localizable.strings @@ -1,6 +1,9 @@ /* No comment provided by engineer. */ "%@" = "%@"; +/* No comment provided by engineer. */ +"%@ [%@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)" = "%1$@ [%2$@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)"; + /* Buton label for Edit action */ "action.generic.edit" = "Szerkesztés"; @@ -103,6 +106,12 @@ /* Share action */ "action.title.share" = "Megosztás"; +/* Settings label for bottom position for the address bar */ +"address.bar.bottom" = "Alul"; + +/* Settings label for top position for the address bar */ +"address.bar.top" = "Fel"; + /* No comment provided by engineer. */ "addWidget.button" = "Minialkalmazás hozzáadása"; @@ -142,6 +151,225 @@ /* Shown on authentication screen */ "app.authentication.unlock" = "Oldd fel a DuckDuckGo-t."; +/* First part of about page content (note the trailing space) */ +"appTP.about.content1" = "You’ve probably heard about companies like Google and Facebook tracking you behind the scenes on third-party websites. But did you know they also track your personal information through apps on your device?\n\nIn 2022, DuckDuckGo found that "; + +/* Second part of about page content (note the trailing space) */ +"appTP.about.content2" = "over 85% of free iOS apps tested contained hidden trackers from other companies."; + +/* Third part of about page content (note the leading space) */ +"appTP.about.content3" = " Of the 395 apps tested, 60% sent data to Google. This happens even while you’re not using your device.\n\nTrackers in apps may have access to a lot more information than their website tracker cousins, such as your location down to which floor of a building you're on, how often you play games while at work, and when and how long you sleep each day. Even if you haven’t given apps explicit permission to collect data, they can still take it without your knowledge.\n\nTracking networks like Facebook and Google use these little pieces of information to build a digital profile about you. With it, tracking networks can manipulate what you see online and allow advertisers to bid on access to you based on your data.\n\nTrackers in apps is a BIG problem for privacy. But DuckDuckGo has a solution that can help.\n\nWhen enabled in the DuckDuckGo Privacy Browser app, App Tracking Protection blocks many trackers in other apps, not just the trackers we find on websites when you browse. These dual layers of protection reduce what companies know about you overall, so you can use your apps with more peace of mind, knowing you’re more protected."; + +/* Navigation Title for AppTP about page */ +"appTP.about.navTitle" = "About App Trackers"; + +/* Title for AppTP about page */ +"appTP.about.title" = "What Are App Trackers?"; + +/* Title for 'Report an Issue' button in the activity view. */ +"appTP.activityView.reportIssue" = "Report Issue"; + +/* Text label for switch that turns blocking on or off for a tracker */ +"appTP.blockTrackerText" = "Block this Tracker"; + +/* Detail string describing what AppTP is */ +"appTP.cell.detail" = "Alkalmazáskövetők letiltása az eszközödön"; + +/* String indicating AppTP is disabled when viewed from the settings screen */ +"appTP.cell.disabled" = "Letiltva"; + +/* String indicating AppTP is enabled when viewed from the settings screen */ +"appTP.cell.enabled" = "Engedélyezve"; + +/* Info string informing the user what App Tracking Protection does. */ +"appTP.empty.disabled.info" = "Engedélyezd az App Tracking Protectiont, hogy blokkolhassuk a bosszantó nyomkövetőket más alkalmazásokban."; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.heading" = "We’re blocking hidden trackers"; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.info" = "Come back soon to see a list of all the app trackers we’ve blocked."; + +/* First answer for AppTP FAQ page */ +"appTP.faq.answer1" = "App Tracking Protection blocks app trackers from other companies, like when Facebook tries to track you in a banking app. Companies may still track you in apps they own."; + +/* Second answer for AppTP FAQ page */ +"appTP.faq.answer2" = "Yes! App Tracking Protection works across all apps on your device to block the most common hidden trackers we find trying to collect your personal info."; + +/* Third answer for AppTP FAQ page */ +"appTP.faq.answer3" = "We currently only block the most common trackers that we find on iOS. This helps us to comprehensively test App Tracking Protection and lower frequency of app breakage, while blocking up to 70% of all tracking requests."; + +/* Fourth answer for AppTP FAQ page */ +"appTP.faq.answer4" = "You’ll be asked to set up a virtual private network (VPN) connection, but you don't need to install a VPN app for App Tracking Protection to work.\n\nThis permission, which works only on your device, allows App Tracking Protection to monitor network traffic so that it can block known trackers."; + +/* Fifth answer for AppTP FAQ page */ +"appTP.faq.answer5" = "You can use App Tracking Protection at the same time as using an IKEv2 protocol VPN app on an iOS device. You won’t be able to use App Tracking Protection on an iOS device if you’re using a VPN app that uses a different type of protocol, like WireGuard or OpenVPN type VPNs."; + +/* Sixth answer for AppTP FAQ page */ +"appTP.faq.answer6" = "A VPN sends your data from the device to its own server, where it secures and anonymizes your data from prying eyes. However, this allows the VPN company to see your network traffic.\n\nApp Tracking Protection is different. Instead of sending your data to a VPN server, App Tracking Protection works only on your device, sitting between your apps and the servers they talk to.\n\nWhenever App Tracking Protection recognizes a known tracker, it blocks the tracker from sending personal information (such as your IP address, activity, and device details) off your device. All other traffic reaches its destination, so your apps work normally."; + +/* Seventh answer for AppTP FAQ page */ +"appTP.faq.answer7" = "App Tracking Protection works only on your device and doesn’t send your data off your device to DuckDuckGo. We don’t collect or store any data from your apps."; + +/* First question for AppTP FAQ page */ +"appTP.faq.question1" = "How does App Tracking Protection work?"; + +/* Second question for AppTP FAQ page */ +"appTP.faq.question2" = "Does App Tracking Protection block trackers in all apps on my device?"; + +/* Third question for AppTP FAQ page */ +"appTP.faq.question3" = "Does App Tracking Protection block all app trackers?"; + +/* Fourth question for AppTP FAQ page */ +"appTP.faq.question4" = "Why does App Tracking Protection use a VPN connection?"; + +/* Fifth question for AppTP FAQ page */ +"appTP.faq.question5" = "Will App Tracking Protection work if I also use a VPN app?"; + +/* Sixth question for AppTP FAQ page */ +"appTP.faq.question6" = "How is App Tracking Protection different from a VPN?"; + +/* Seventh question for AppTP FAQ page */ +"appTP.faq.question7" = "Is my data private?"; + +/* Title for AppTP FAQ page */ +"appTP.faq.title" = "App Tracking Protection FAQ"; + +/* Do not translate. StringsDict entry -- Count part of string 'App Tracking Protection blocked x tracking attempts today' */ +"appTP.home.blockedCount" = "appTP.home.blockedCount"; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the trailing space) */ +"appTP.home.blockedPrefix" = "Az App Tracking Protection blokkolt "; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the leading space) */ +"appTP.home.blockedSuffix" = " ma az alkalmazásokban."; + +/* Prefix of string 'App Tracking Protection disabled. Tap to re-enable.' (note the trailing space) */ +"appTP.home.disabledPrefix" = "App Tracking Protection disabled. "; + +/* Suffix of string 'App Tracking Protection disabled. Tap to re-enable.' */ +"appTP.home.disabledSuffix" = "Tap to continue blocking tracking attempts across your apps."; + +/* Text indicating the tracking event occured 'just now'. Example: Last attempt 'just now' */ +"appTP.justNow" = "Épp most"; + +/* View to manage trackers for AppTP. Allows the user to turn trackers on or off. */ +"appTP.manageTrackers" = "Nyomkövetők kezelése"; + +/* Button title for AppTP onboarding */ +"appTP.onboarding.continueButton" = "Continue"; + +/* Button title for AppTP onboarding to enable AppTP */ +"appTP.onboarding.enableeButton" = "Enable App Tracking Protection"; + +/* Button title for AppTP onboarding to learn more about AppTP */ +"appTP.onboarding.learnMoreButton" = "Learn More"; + +/* First part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info1" = "Over 85% of free iOS apps"; + +/* Second part of info on the first AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page1Info2" = " we’ve tested allow other companies to track your personal information, even when you’re sleeping."; + +/* Third part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info3" = "See who we catch trying to track you in your apps and take back control."; + +/* First part of info on the second AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page2Info1" = "App Tracking Protection "; + +/* Second part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info2" = "detects and blocks app trackers from other companies,"; + +/* Third part of info on the second AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page2Info3" = " like when Google attempts to track you in a health app."; + +/* Fourth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info4" = "It’s free,"; + +/* Fifth part of info on the second AppTP onboarding page (note the leading and trailing space) */ +"appTP.onboarding.page2Info5" = " and you can enjoy your apps as you normally would. Working in the background, it helps "; + +/* Sixth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info6" = "protect you night and day."; + +/* First part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info1" = "App Tracking Protection is not a VPN."; + +/* Second part of info on the third AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page3Info2" = " However, your device will recognize it as one. This is because it uses a local VPN connection to work."; + +/* Third part of info on the third AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page3Info3" = "App Tracking Protection is different. "; + +/* Fourth part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info4" = "It never routes app data through an external server."; + +/* Title for first AppTP onboarding page */ +"appTP.onboarding.title1" = "One easy step for better app privacy!"; + +/* Title for second AppTP onboarding page */ +"appTP.onboarding.title2" = "How does it work?"; + +/* Title for third AppTP onboarding page */ +"appTP.onboarding.title3" = "Who sees your data?"; + +/* Breakage report app name label */ +"appTP.report.appLabel" = "Which app is having issues?"; + +/* Breakage report app name placeholder */ +"appTP.report.appPlaceholder" = "App name"; + +/* Breakage report category label */ +"appTP.report.categoryLabel" = "Mi történik?"; + +/* Breakage report comment label */ +"appTP.report.commentLabel" = "Comments"; + +/* Breakage report comment placeholder */ +"appTP.report.commentPlaceholder" = "Add additional details"; + +/* Breakage report footer explaining what is collected in the breakage report */ +"appTP.report.footer" = "In addition to the details entered into this form, your app issue report will contain:\n• A list of trackers blocked in the last 10 min\n• Whether App Tracking Protection is enabled\n• Aggregate DuckDuckGo app diagnostics"; + +/* Breakage report submit button */ +"appTP.report.submit" = "Elküldés"; + +/* Breakage report form title */ +"appTP.report.title" = "Report Issue"; + +/* Breakage report succcess message */ +"appTP.report.toast" = "Köszönjük! Visszajelzés elküldve."; + +/* Cancel button for 'Report an Issue' alert. */ +"appTP.reportAlert.cancel" = "Most nem"; + +/* Confirm button for 'Report an Issue' alert. */ +"appTP.reportAlert.confirm" = "Probléma jelentése"; + +/* Message for 'Report an Issue' alert. */ +"appTP.reportAlert.message" = "Let us know if you disabled App Tracking Protection for this specific tracker because it caused app issues. Your feedback helps us improve!"; + +/* Title for 'Report an Issue' alert. */ +"appTP.reportAlert.title" = "Report Issue?"; + +/* Toast notification diplayed after restoring the blocklist to default settings */ +"appTP.restoreDefaultsToast" = "Default settings restored"; + +/* Button to restore the blocklist to its default state. */ +"appTP.restoreDefualts" = "Alapértelmezések visszaállítása"; + +/* Title for the App Tracking Protection feature */ +"appTP.title" = "Alkalmazáskövetés elleni védelem"; + +/* Text indicating when the tracker was last allowed. Example: Last attempt allowed (timeString) */ +"appTP.trackerAllowedTimestamp" = "Utolsó engedélyezett próbálkozás időpontja: %@"; + +/* Text indicating when the tracker was last blocked. Example: Last attempt blocked (timeString) */ +"appTP.trackerBlockedTimestamp" = "Utolsó blokkolt próbálkozás időpontja: %@"; + +/* Do not translate. StringsDict entry -- Subtitle for tracking attempts in App Tracking Protection Activity View. Example: (count) tracking attempts */ +"appTP.trackingattempts" = "appTP.trackingattempts"; + /* Authentication Alert Sign In Button */ "auth.alert.login.button" = "Bejelentkezés"; @@ -301,6 +529,18 @@ /* Toast message when a login item without a title is deleted */ "autofill.logins.list.login-deleted-message-no-title" = "Bejelentkezés törölve"; +/* Title for a button that allows a user to reset their list of never saved sites */ +"autofill.logins.list.never.saved" = "Kizárt webhelyek visszaállítása"; + +/* Cancel button for resetting list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.cancel" = "Mégsem"; + +/* Confirm button to reset list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.confirm" = "Kizárt webhelyek visszaállítása"; + +/* Alert title */ +"autofill.logins.list.never.saved.reset.action.title" = "A kizárt webhelyek visszaállítása esetén a rendszer a bejelentkezési adataid mentését kéri, amikor legközelebb bejelentkezel ezen webhelyek bármelyikére."; + /* Placeholder for search field on autofill login listing */ "autofill.logins.list.search-placeholder" = "Bejelentkezési adatok keresése"; @@ -335,7 +575,7 @@ "autofill.logins.prompt.password.button.title" = "%@ jelszava"; /* Title for autofill login prompt */ -"autofill.logins.prompt.title" = "Mentett bejelentkezés használata?"; +"autofill.logins.prompt.title" = "Mentett jelszót használsz?"; /* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ "autofill.logins.search.no-results.subtitle" = "erre: „%@“"; @@ -385,27 +625,24 @@ /* Title for the alert dialog telling the user an updated username is no longer a private email address */ "autofill.removed.duck.address.title" = "Privát Duck-cím felhasználóneve eltávolítva"; +/* CTA displayed on modal asking if the user never wants to be prompted to save a login for this website agin */ +"autofill.save-login.never-prompt.CTA" = "Soha ne kérdezzen rá ennél a webhelynél"; + /* Message displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.message" = "A rendszer biztonságosan tárolja az eszköz Bejelentkezés menüjében elérhető bejelentkezési adatokat."; +"autofill.save-login.new-user.message" = "A rendszer biztonságosan tárolja az eszköz Bejelentkezés menüjében elérhető jelszavakat."; /* Title displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.title" = "A DuckDuckGo mentse a bejelentkezést?"; +"autofill.save-login.new-user.title" = "A DuckDuckGo mentse a jelszót?"; /* Cancel CTA displayed on modal asking for the user to save the login */ "autofill.save-login.not-now.CTA" = "Mentés mellőzése"; -/* Confirm CTA displayed on modal asking for the user to save the login */ -"autofill.save-login.save.CTA" = "Bejelentkezés mentése"; - /* Title displayed on modal asking for the user to save the login */ "autofill.save-login.title" = "Mented a bejelentkezést?"; /* Confirm CTA displayed on modal asking for the user to save the password */ "autofill.save-password.save.CTA" = "Jelszó mentése"; -/* Title displayed on modal asking for the user to save the password */ -"autofill.save-password.title" = "Mented a jelszót?"; - /* Accessibility title for a Show Password button displaying actial password instead of ***** */ "autofill.show-password" = "Jelszó megjelenítése"; @@ -413,7 +650,7 @@ "autofill.signin.to.manage" = "%@ a Duck-címek kezeléséhez ezen az eszközön."; /* Message displayed on modal asking for the user to update the password */ -"autofill.update-password.message" = "A DuckDuckGo frissíti az eszközödön ezt a tárolt bejelentkezést."; +"autofill.update-password.message" = "A DuckDuckGo frissíti az eszközödön ezt a tárolt jelszót."; /* Confirm CTA displayed on modal asking for the user to update the password */ "autofill.update-password.save.CTA" = "Jelszó frissítése"; @@ -442,6 +679,15 @@ /* Placeholder in the add bookmark form */ "bookmark.address.placeholder" = "www.example.com"; +/* Delete bookmark alert message */ +"bookmark.delete.alert.message" = "Ez törli a(z) „%@” könyvjelződet"; + +/* Delete bookmark alert title */ +"bookmark.delete.alert.title" = "Delete?"; + +/* The message shown after a bookmark has been deleted */ +"bookmark.deleted.toast" = "Könyvjelző törölve"; + /* Delete bookmark folder alert delete button */ "bookmark.deleteFolderAlert.deleteButton" = "Törlés"; @@ -538,9 +784,21 @@ /* Broken Site Category */ "brokensite.category.videos" = "A videót nem játszotta le"; +/* Broken Site Category Placeholder */ +"brokensite.categoryPlaceholder" = "Válaszd ki a problémát a listából…"; + +/* Broken Site Category Section Title */ +"brokensite.categoryTitle" = "VÁLASSZ KATEGÓRIÁT"; + +/* Broken Site Comment Placeholder */ +"brokensite.commentPlaceholder" = "A további részletek segítségünkre lehetnek a probléma megoldásában"; + /* Broken Site Section Title */ "brokensite.sectionTitle" = "ESEMÉNY LEÍRÁSA"; +/* No comment provided by engineer. */ +"bucket: %@" = "gyűjtő: %@"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Elmúlt hónapban"; @@ -772,6 +1030,39 @@ /* Empty search placeholder on bookmarks search */ "empty.search" = "Nincs találat"; +/* No comment provided by engineer. */ +"Error" = "Hiba"; + +/* Button title to Sign In */ +"error.email-protection-sign-in.action" = "Bejelentkezés"; + +/* Alert message */ +"error.email-protection-sign-in.body" = "Sajnáljuk, jelentkezz be újra az e-mail-védelemnek az ebben a böngészőben való újbóli bekapcsolásához."; + +/* Alert title */ +"error.email-protection-sign-in.title" = "E-mail-védelmi hiba"; + +/* Button title to open device settings */ +"error.insufficient-disk-space.action" = "Beállítások megnyitása"; + +/* Alert message */ +"error.insufficient-disk-space.body" = "Úgy tűnik, az eszközön elfogyott a tárhely. A folytatáshoz szabadíts fel helyet."; + +/* Alert title */ +"error.insufficient-disk-space.title" = "Nincs elég tárhely"; + +/* Button title that is shutting down the app */ +"error.preemptive-crash.action" = "Alkalmazás bezárása"; + +/* Alert message */ +"error.preemptive-crash.body" = "Úgy tűnik, hogy probléma van az alkalmazással, ezért be kell zárni. A folytatáshoz nyisd meg újra."; + +/* Alert title */ +"error.preemptive-crash.title" = "Alkalmazásprobléma tapasztalható"; + +/* Generic error message on a dialog for when the cause is not known. */ +"error.unknown.try.again" = "An unknown error has occurred"; + /* No comment provided by engineer. */ "favorite" = "Kedvenc"; @@ -991,6 +1282,18 @@ /* Home tab title */ "homeTab.title" = "Kezdőlap"; +/* OK title for invite screen alert dismissal button */ +"invite.alert.ok.button" = "OK"; + +/* Continue button on an invite dialog */ +"invite.dialog.continue.button" = "Continue"; + +/* Get Started button on an invite dialog */ +"invite.dialog.get.started.button" = "Get Started"; + +/* Message to show after user enters an unrecognized invite code */ +"invite.dialog.unrecognized.code.message" = "We didn’t recognize this Invite Code."; + /* No comment provided by engineer. */ "keyCommandAddBookmark" = "Könyvjelző mentése"; @@ -1057,15 +1360,15 @@ /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "A DuckDuckGo Mac verziója gyors, böngészési funkciókban gazdag, és kategóriája legjobb adatvédelmi megoldásait kínálja."; -/* Body text for the macOS waitlist notification */ -"mac-waitlist.available.notification.body" = "Meghívó megnyitása"; - /* Title for the macOS waitlist notification */ "mac-waitlist.available.notification.title" = "Elkészült a DuckDuckGo Maces verziója!"; /* Title for the copy action */ "mac-waitlist.copy" = "Másolás"; +/* Description text above the Share Link button */ +"mac-waitlist.join-waitlist-screen.on-your-computer-go-to" = "Windowsos számítógépen lépj erre az oldalra:"; + /* Description text above the Share Link button */ "mac-waitlist.join-waitlist-screen.on-your-mac-go-to" = "Macen lépj a következőre:"; @@ -1078,6 +1381,9 @@ /* Disclaimer for the Join Waitlist screen */ "mac-waitlist.join-waitlist-screen.windows" = "Hamarosan érkezik a Windows verzió!"; +/* Title for the macOS waitlist button redirecting to Windows waitlist */ +"mac-waitlist.join-waitlist-screen.windows-waitlist" = "A Windowsra készült verziót keresed?"; + /* Title for the settings subtitle */ "mac-waitlist.settings.browse-privately" = "Privát böngészés Maces alkalmazásunkkal"; @@ -1088,7 +1394,7 @@ "mac-waitlist.share-sheet.title" = "DuckDuckGo Mac verzió"; /* Title for the Mac Waitlist feature */ -"mac-waitlist.title" = "DuckDuckGo asztali alkalmazás"; +"mac-waitlist.title" = "DuckDuckGo alkalmazás Machez"; /* No comment provided by engineer. */ "menu.button.hint" = "Tallózás a menüben"; @@ -1105,9 +1411,81 @@ /* Edit button */ "navigation.title.edit" = "Szerkesztés"; +/* String indicating NetP is connected when viewed from the settings screen */ +"netP.cell.connected" = "Connected"; + +/* String indicating NetP is disconnected when viewed from the settings screen */ +"netP.cell.disconnected" = "Not connected"; + +/* Title for the Network Protection feature */ +"netP.title" = "Network Protection"; + +/* Message for the network protection invite dialog */ +"network.protection.invite.dialog.message" = "Enter your invite code to get started."; + +/* Title for the network protection invite screen */ +"network.protection.invite.dialog.title" = "You're invited to try Network Protection"; + +/* Prompt for the network protection invite code text field */ +"network.protection.invite.field.prompt" = "Invite Code"; + +/* Message explaining that netP is invite only */ +"network.protection.invite.only.message" = "DuckDuckGo Network Protection is currently invite-only."; + +/* Message for the network protection invite success view */ +"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; + +/* Title for the network protection invite success view */ +"network.protection.invite.success.title" = "Success! You’re in."; + +/* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ +"network.protection.status.connected.format" = "Connected - %@"; + +/* The label for the NetP VPN when connecting */ +"network.protection.status.connecting" = "Connecting..."; + +/* The label for the NetP VPN when disconnected */ +"network.protection.status.disconnected" = "Not connected"; + +/* The label for the NetP VPN when disconnecting */ +"network.protection.status.disconnecting" = "Disconnecting..."; + +/* Message label text for the netP status view */ +"network.protection.status.header.message" = "DuckDuckGo's VPN secures all of your device's Internet traffic anytime, anywhere."; + +/* Header title label text for the status view when netP is disconnected */ +"network.protection.status.header.title.off" = "Network Protection is Off"; + +/* Header title label text for the status view when netP is connected */ +"network.protection.status.header.title.on" = "Network Protection is On"; + +/* The status view 'Share Feedback' button which is shown inline on the status view after the \(netPInviteOnlyMessage) text */ +"network.protection.status.menu.share.feedback" = "Share Feedback"; + +/* Connection details label shown in NetworkProtection's status view. */ +"network.protection.status.view.connection.details" = "Connection Details"; + +/* Generic connection failed error message shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.message" = "Please try again later."; + +/* Generic connection failed error title shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; + +/* IP Address label shown in NetworkProtection's status view. */ +"network.protection.status.view.ip.address" = "IP Address"; + +/* Location label shown in NetworkProtection's status view. */ +"network.protection.status.view.location" = "Location"; + +/* Title label text for the status view when netP is disconnected */ +"network.protection.status.view.title" = "Network Protection"; + /* Do not translate - stringsdict entry */ "number.of.tabs" = "number.of.tabs"; +/* No comment provided by engineer. */ +"Ok" = "OK"; + /* Text displayed on notification appearing in the address bar when the browser dismissed the cookie popup automatically rejecting it */ "omnibar.notification.cookies-managed" = "Kezelt sütik"; @@ -1147,6 +1525,18 @@ /* ’Link’ is link on a website */ "open.externally.failed" = "A link megnyitásához szükséges alkalmazás nem található."; +/* No comment provided by engineer. */ +"Please enter which app on your device is broken." = "Add meg, hogy melyik alkalmazás működik hibásan az eszközön."; + +/* Activate button */ +"pm.activate" = "Reactivate"; + +/* Cancel button */ +"pm.cancel" = "Cancel"; + +/* Deactivate button */ +"pm.deactivate" = "Deactivate"; + /* No comment provided by engineer. */ "preserveLogins.domain.list.footer" = "A webhelyek sütik segítségével teszik lehetővé, hogy bejelentkezve maradhass. Ha tűzállóvá teszel egy weboldalt, a sütik nem törlődnek, és még a Tűz gomb használata után is bejelentkezve maradsz. A tűzálló webhelyeken a külső felek nyomkövetőit azonban blokkoljuk."; @@ -1216,8 +1606,8 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Kedvencek"; -/* No comment provided by engineer. */ -"settings.about.text" = "A DuckDuckGo célja egy új online bizalmi szint megteremtése.\n\nA DuckDuckGo Adatvédelmi Böngésző minden olyan alapvető adatvédelmi elemet tartalmaz, amelyre szükséged lehet ahhoz, hogy megvédd magad az internetes keresés és böngészés során, beleértve a nyomkövetők letiltását, az intelligensebb titkosítást és a DuckDuckGo privát keresést.\n\nAz internet végső soron nem feltétlenül kell, hogy ellenséges közeg legyen, és hisszük, hogy az általad megérdemelt adatvédelem olyan egyszerű is lehet, mint a redőny lehúzása."; +/* about page */ +"settings.about.text" = "A 2008-ban alapított független internetes adatvédelmi cég, a DuckDuckGo egyszerű megoldást kínál azoknak, akiknek elegük van abból, hogy állandóan nyomon követik őket az interneten. Mi vagyunk annak a bizonyítéka, hogy kompromisszumok nélkül is lehet valódi online adatvédelmet biztosítani.\n\nA DuckDuckGo böngésző rendelkezik a böngészőktől elvárt funkciókkal, mint például könyvjelzők, lapok, jelszavak és egyéb funkciók, valamint több mint [egy tucat olyan hatékony adatvédelmi megoldással](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/), amelyet a legtöbb népszerű böngésző nem kínál alapértelmezés szerint. Ezek az egyedülállóan átfogó adatvédelmi megoldások segítenek megvédeni online tevékenységeid, legyen szó keresésről, böngészésről, e-mailezésről vagy sok minden másról.\n\nAdatvédelmi megoldásaink anélkül működnek, hogy bármit is tudnod kellene a technikai részletekről vagy bonyolult beállításokkal kellene foglalkoznod. Mindössze annyit kell tenned, hogy minden eszközödön a DuckDuckGo böngészőre váltasz, és máris igénybe veheted az alapértelmezés szerinti adatvédelmet.\n\nHa azonban *igazán* szeretnél bepillantani a motorháztető alá is, a [súgóoldalainkon](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/) további információkat találhatsz a DuckDuckGo adatvédelmi megoldásainak működéséről."; /* Report a Broken Site screen confirmation button */ "siteFeedback.buttonText" = "Jelentés beküldése"; @@ -1237,6 +1627,9 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Melyik weboldal nem működik?”"; +/* No comment provided by engineer. */ +"sync.remove-device.message" = "A(z) „%@” többé nem férhet hozzá a szinkronizált adatokhoz."; + /* Accessibility label on remove button */ "tab.close.home" = "Kezdőlap bezárása"; @@ -1309,6 +1702,57 @@ /* Voice-search footer note with on-device privacy warning */ "voiceSearch.footer.note" = "A hang feldolgozása az eszközön történik. Nem tároljuk és nem osztjuk meg senkivel, még a DuckDuckGóval sem."; +/* Title for the button to enable push notifications in system settings */ +"waitlist.allow-notifications" = "Értesítések engedélyezése"; + +/* Body text for the waitlist notification */ +"waitlist.available.notification.body" = "Meghívó megnyitása"; + +/* Title for the copy action */ +"waitlist.copy" = "Másolás"; + +/* Label text for the invite code */ +"waitlist.invite-code" = "Meghívókód"; + +/* Step title on the invite screen */ +"waitlist.invite-screen.step.title" = "%d. lépés"; + +/* Title for the invite code screen */ +"waitlist.invite-screen.youre-invited" = "Meghívót kaptál!"; + +/* Title for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.join" = "Csatlakozz a privát várólistához"; + +/* Temporary status text for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.joining" = "Csatlakozás a várólistához…"; + +/* Title for the Share Link button */ +"waitlist.join-waitlist-screen.share-link" = "Link megosztása"; + +/* Notification text for the waitlist */ +"waitlist.joined.no-notification.get-notification" = "ÉRTESÍTSETEK"; + +/* Title for the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.get-notification-confirmation-title" = "Szeretnél értesítést kapni, ha sorra kerülsz?"; + +/* Cancel button in the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.no-thanks" = "Nem, köszönöm"; + +/* Text used for the Notifications Disabled state */ +"waitlist.notification.disabled" = "Értesíthetünk, ha sorra kerülsz, de a DuckDuckGo értesítései jelenleg le vannak tiltva."; + +/* Privacy disclaimer for the Waitlist feature */ +"waitlist.privacy-disclaimer" = "Nem kell megosztanod a személyes adataid a várólistához való csatlakozáshoz. A sorban elfoglalt helyedet egy olyan időbélyegző igazolja, amely kizárólag az eszközödön található meg, így értesíteni tudunk, amikor sorra kerülsz."; + +/* Title for the queue screen */ +"waitlist.queue-screen.on-the-list" = "Felkerültél a listára!"; + +/* Title for the settings subtitle */ +"waitlist.settings.download-available" = "Letöltés elérhető"; + +/* Title for the share sheet entry */ +"waitlist.share-sheet.title" = "Meghívót kaptál!"; + /* Confirmation message */ "web.url.remove.favorite.done" = "Kedvenc eltávolítva"; @@ -1333,3 +1777,51 @@ /* Alert title explaining the message is shown by a website */ "webJSAlert.website-message.format" = "%@ üzenete:"; +/* No comment provided by engineer. */ +"Welcome to the Duck Side!" = "Isten hozott a Kacsa oldalon!"; + +/* Title for the Windows waitlist notification */ +"windows-waitlist.available.notification.title" = "Próbáld ki a DuckDuckGo Windowsos verzióját!"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-1.description" = "A letöltéshez látogass el erre a címre a windowsos eszközödön:"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-2.description" = "Nyisd meg a DuckDuckGo telepítőjét a Letöltésekben, válaszd a Telepítés lehetőséget, majd írd be a meghívókódot."; + +/* Subtitle for the Windows Waitlist Invite screen */ +"windows-waitlist.invite-screen.subtitle" = "Készen állsz a DuckDuckGo Windowson való használatára?"; + +/* Title for the Windows waitlist button redirecting to Mac waitlist */ +"windows-waitlist.join-waitlist-screen.mac-waitlist" = "A Macre készült verziót keresed?"; + +/* Title for the Join Windows Waitlist screen */ +"windows-waitlist.join-waitlist-screen.try-duckduckgo-for-windows" = "Szerezz korai hozzáférést a windowsos DuckDuckGo alkalmazáshoz."; + +/* Message for the alert to confirm enabling notifications */ +"windows-waitlist.joined.no-notification.get-notification-confirmation-message" = "Értesítést küldünk, ha a DuckDuckGo Windowsos verziója letölthetővé válik a számodra. "; + +/* Label text for the Joined Waitlist state with notifications declined */ +"windows-waitlist.joined.notifications-declined" = "A DuckDuckGo windowsos verziójának kipróbálására szóló meghívód ide fog érkezni. Látogass vissza mielőbb, vagy küldhetünk értesítést, ha sorra kerülsz."; + +/* Label text for the Joined Waitlist state with notifications enabled */ +"windows-waitlist.joined.notifications-enabled" = "Értesítést küldünk, ha a DuckDuckGo windowsos verziója letölthetővé válik a számodra."; + +/* Title for the settings subtitle */ +"windows-waitlist.settings.browse-privately" = "Privát böngészés windowsos alkalmazásunkkal"; + +/* Message used when sharing to iMessage. Parameter is an eight digit invite code. */ +"windows-waitlist.share-sheet.invite-code-message" = "Meghívót kaptál!\n\nKészen állsz a DuckDuckGo Windowson való használatára?1. lépés\nWindowsos eszközödön látogass el erre a címre a letöltéshez:\nhttps://duckduckgo.com/windows\n\n2. lépés\nNyisd meg a DuckDuckGo telepítőjét a Letöltésekben, válaszd a Telepítés lehetőséget, majd írd be a meghívókódot.\n\nMeghívókód: %@"; + +/* Message used when sharing to iMessage */ +"windows-waitlist.share-sheet.message" = "Készen állsz a privát böngészésre a Windowson?\n\nSzámítógépen látogass el erre a címre a letöltéshez:\nhttps://duckduckgo.com/windows"; + +/* Summary text for the Windows browser waitlist */ +"windows-waitlist.summary" = "A windowsos DuckDuckGo tartalmazza mindazt, ami a privát böngészéshez szükséges – támogatja a privát keresést, a nyomkövetők blokkolását, a kényszerített titkosítást és a felugró sütiablakok blokkolását, valamint további, a kategóriájában legjobb védelmet nyújtó megoldásokat kínál."; + +/* Title for the Windows Waitlist feature */ +"windows-waitlist.title" = "DuckDuckGo alkalmazás Windowshoz"; + +/* Title for the Windows browser download link page */ +"windows-waitlist.waitlist-download-screen.try-duckduckgo-for-windows" = "Töltsd le a DuckDuckGo Windows verzióját!"; + diff --git a/DuckDuckGo/hu.lproj/Settings.strings b/DuckDuckGo/hu.lproj/Settings.strings index ecfed36441..e3b5199113 100644 --- a/DuckDuckGo/hu.lproj/Settings.strings +++ b/DuckDuckGo/hu.lproj/Settings.strings @@ -37,6 +37,9 @@ /* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ "9Ko-0g-T3h.text" = "Szövegméret"; +/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ +"9kt-6R-XiZ.text" = "Cím"; + /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Alkalmazás indítása"; @@ -73,6 +76,12 @@ /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Nemzetközi adatvédelmi szabályozás (Global Privacy Control, GPC)"; +/* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ +"CxT-QK-iVn.title" = "Hozzáadás"; + +/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ +"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; + /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Kilépés az alkalmazásból 15 perc inaktivitás után"; @@ -151,6 +160,9 @@ /* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ "HLr-R8-xxF.text" = "Hosszú lenyomásos előnézetek"; +/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ +"hoT-Nu-KXP.text" = "Privát böngészés windowsos alkalmazásunkkal"; + /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Adatvédelem engedélyezve minden weboldalhoz"; @@ -163,6 +175,12 @@ /* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ "m23-t6-9cb.text" = "Visszajelzés megosztása"; +/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ +"mLn-1x-Fl5.title" = "Címsor elhelyezkedése"; + +/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ +"mqV-pf-NZ1.text" = "Címsor elhelyezkedése"; + /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "A DuckDuckGo kezelheti a sütik elfogadására szolgáló felugró ablakokat"; @@ -175,15 +193,27 @@ /* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ "oM7-1o-9oY.text" = "A DuckDuckGóról"; +/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ +"opn-JO-idF.text" = "Fel"; + /* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ "OxE-MQ-uJk.headerTitle" = "Továbbiak a DuckDuckGótól"; +/* Class = "UILabel"; text = "Sync"; ObjectID = "oXN-ez-gct"; */ +"oXN-ez-gct.text" = "Szinkronizálás"; + /* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ "P0F-ts-ekd.text" = "Privát böngészés Maces alkalmazásunkkal "; +/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ +"qah-gb-udB.text" = "Network Protection"; + /* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ "qeN-SV-zy7.text" = "Címke"; +/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ +"RQ8-H1-Ez1.text" = "DuckDuckGo Windows alkalmazás"; + /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Szövegméret"; @@ -232,8 +262,8 @@ /* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ "yvj-LL-MiR.text" = "Alkalmazás hozzáadása a dokkodhoz"; -/* Class = "UILabel"; text = "DuckDuckGo Desktop App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "DuckDuckGo asztali alkalmazás"; +/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ +"Yz9-17-qnn.text" = "DuckDuckGo Mac alkalmazás"; /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Új lap"; diff --git a/DuckDuckGo/it.lproj/Feedback.strings b/DuckDuckGo/it.lproj/Feedback.strings index ea4edf06b4..57876c9c21 100644 --- a/DuckDuckGo/it.lproj/Feedback.strings +++ b/DuckDuckGo/it.lproj/Feedback.strings @@ -28,9 +28,6 @@ /* Class = "UIBarButtonItem"; title = "Back"; ObjectID = "JF8-Ow-zXL"; */ "JF8-Ow-zXL.title" = "Indietro"; -/* Class = "UIBarButtonItem"; title = "Submit"; ObjectID = "jr6-Al-4fr"; */ -"jr6-Al-4fr.title" = "Invia"; - /* Class = "UIButton"; normalTitle = "Submit"; ObjectID = "lsB-Kq-U8p"; */ "lsB-Kq-U8p.normalTitle" = "Invia"; @@ -58,9 +55,6 @@ /* Class = "UINavigationItem"; title = "Report Broken Site"; ObjectID = "WjT-HM-yim"; */ "WjT-HM-yim.title" = "Segnala sito danneggiato"; -/* Class = "UILabel"; text = "Broken Site Reason"; ObjectID = "Wx3-Vb-ItS"; */ -"Wx3-Vb-ItS.text" = "Causa del malfunzionamento del sito"; - /* Class = "UINavigationItem"; title = "Share Feedback"; ObjectID = "Xb0-Zu-opM"; */ "Xb0-Zu-opM.title" = "Condividi feedback"; diff --git a/DuckDuckGo/it.lproj/Localizable.strings b/DuckDuckGo/it.lproj/Localizable.strings index a6dd82d8cc..70109a5069 100644 --- a/DuckDuckGo/it.lproj/Localizable.strings +++ b/DuckDuckGo/it.lproj/Localizable.strings @@ -1,6 +1,9 @@ /* No comment provided by engineer. */ "%@" = "%@"; +/* No comment provided by engineer. */ +"%@ [%@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)" = "%1$@ [%2$@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)"; + /* Buton label for Edit action */ "action.generic.edit" = "Modifica"; @@ -103,6 +106,12 @@ /* Share action */ "action.title.share" = "Condividi"; +/* Settings label for bottom position for the address bar */ +"address.bar.bottom" = "Parte inferiore"; + +/* Settings label for top position for the address bar */ +"address.bar.top" = "Inizio"; + /* No comment provided by engineer. */ "addWidget.button" = "Aggiungi widget"; @@ -142,6 +151,225 @@ /* Shown on authentication screen */ "app.authentication.unlock" = "Sblocca DuckDuckGo."; +/* First part of about page content (note the trailing space) */ +"appTP.about.content1" = "You’ve probably heard about companies like Google and Facebook tracking you behind the scenes on third-party websites. But did you know they also track your personal information through apps on your device?\n\nIn 2022, DuckDuckGo found that "; + +/* Second part of about page content (note the trailing space) */ +"appTP.about.content2" = "over 85% of free iOS apps tested contained hidden trackers from other companies."; + +/* Third part of about page content (note the leading space) */ +"appTP.about.content3" = " Of the 395 apps tested, 60% sent data to Google. This happens even while you’re not using your device.\n\nTrackers in apps may have access to a lot more information than their website tracker cousins, such as your location down to which floor of a building you're on, how often you play games while at work, and when and how long you sleep each day. Even if you haven’t given apps explicit permission to collect data, they can still take it without your knowledge.\n\nTracking networks like Facebook and Google use these little pieces of information to build a digital profile about you. With it, tracking networks can manipulate what you see online and allow advertisers to bid on access to you based on your data.\n\nTrackers in apps is a BIG problem for privacy. But DuckDuckGo has a solution that can help.\n\nWhen enabled in the DuckDuckGo Privacy Browser app, App Tracking Protection blocks many trackers in other apps, not just the trackers we find on websites when you browse. These dual layers of protection reduce what companies know about you overall, so you can use your apps with more peace of mind, knowing you’re more protected."; + +/* Navigation Title for AppTP about page */ +"appTP.about.navTitle" = "About App Trackers"; + +/* Title for AppTP about page */ +"appTP.about.title" = "What Are App Trackers?"; + +/* Title for 'Report an Issue' button in the activity view. */ +"appTP.activityView.reportIssue" = "Report Issue"; + +/* Text label for switch that turns blocking on or off for a tracker */ +"appTP.blockTrackerText" = "Block this Tracker"; + +/* Detail string describing what AppTP is */ +"appTP.cell.detail" = "Blocca i sistemi di tracciamento delle app sul tuo dispositivo"; + +/* String indicating AppTP is disabled when viewed from the settings screen */ +"appTP.cell.disabled" = "Disattivato"; + +/* String indicating AppTP is enabled when viewed from the settings screen */ +"appTP.cell.enabled" = "Attivato"; + +/* Info string informing the user what App Tracking Protection does. */ +"appTP.empty.disabled.info" = "Attiva App Tracking Protection per bloccare fastidiosi sistemi di tracciamento in altre app."; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.heading" = "We’re blocking hidden trackers"; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.info" = "Come back soon to see a list of all the app trackers we’ve blocked."; + +/* First answer for AppTP FAQ page */ +"appTP.faq.answer1" = "App Tracking Protection blocks app trackers from other companies, like when Facebook tries to track you in a banking app. Companies may still track you in apps they own."; + +/* Second answer for AppTP FAQ page */ +"appTP.faq.answer2" = "Yes! App Tracking Protection works across all apps on your device to block the most common hidden trackers we find trying to collect your personal info."; + +/* Third answer for AppTP FAQ page */ +"appTP.faq.answer3" = "We currently only block the most common trackers that we find on iOS. This helps us to comprehensively test App Tracking Protection and lower frequency of app breakage, while blocking up to 70% of all tracking requests."; + +/* Fourth answer for AppTP FAQ page */ +"appTP.faq.answer4" = "You’ll be asked to set up a virtual private network (VPN) connection, but you don't need to install a VPN app for App Tracking Protection to work.\n\nThis permission, which works only on your device, allows App Tracking Protection to monitor network traffic so that it can block known trackers."; + +/* Fifth answer for AppTP FAQ page */ +"appTP.faq.answer5" = "You can use App Tracking Protection at the same time as using an IKEv2 protocol VPN app on an iOS device. You won’t be able to use App Tracking Protection on an iOS device if you’re using a VPN app that uses a different type of protocol, like WireGuard or OpenVPN type VPNs."; + +/* Sixth answer for AppTP FAQ page */ +"appTP.faq.answer6" = "A VPN sends your data from the device to its own server, where it secures and anonymizes your data from prying eyes. However, this allows the VPN company to see your network traffic.\n\nApp Tracking Protection is different. Instead of sending your data to a VPN server, App Tracking Protection works only on your device, sitting between your apps and the servers they talk to.\n\nWhenever App Tracking Protection recognizes a known tracker, it blocks the tracker from sending personal information (such as your IP address, activity, and device details) off your device. All other traffic reaches its destination, so your apps work normally."; + +/* Seventh answer for AppTP FAQ page */ +"appTP.faq.answer7" = "App Tracking Protection works only on your device and doesn’t send your data off your device to DuckDuckGo. We don’t collect or store any data from your apps."; + +/* First question for AppTP FAQ page */ +"appTP.faq.question1" = "How does App Tracking Protection work?"; + +/* Second question for AppTP FAQ page */ +"appTP.faq.question2" = "Does App Tracking Protection block trackers in all apps on my device?"; + +/* Third question for AppTP FAQ page */ +"appTP.faq.question3" = "Does App Tracking Protection block all app trackers?"; + +/* Fourth question for AppTP FAQ page */ +"appTP.faq.question4" = "Why does App Tracking Protection use a VPN connection?"; + +/* Fifth question for AppTP FAQ page */ +"appTP.faq.question5" = "Will App Tracking Protection work if I also use a VPN app?"; + +/* Sixth question for AppTP FAQ page */ +"appTP.faq.question6" = "How is App Tracking Protection different from a VPN?"; + +/* Seventh question for AppTP FAQ page */ +"appTP.faq.question7" = "Is my data private?"; + +/* Title for AppTP FAQ page */ +"appTP.faq.title" = "App Tracking Protection FAQ"; + +/* Do not translate. StringsDict entry -- Count part of string 'App Tracking Protection blocked x tracking attempts today' */ +"appTP.home.blockedCount" = "appTP.home.blockedCount"; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the trailing space) */ +"appTP.home.blockedPrefix" = "Oggi, App Tracking Protection ha bloccato "; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the leading space) */ +"appTP.home.blockedSuffix" = " nelle tue app."; + +/* Prefix of string 'App Tracking Protection disabled. Tap to re-enable.' (note the trailing space) */ +"appTP.home.disabledPrefix" = "App Tracking Protection disabled. "; + +/* Suffix of string 'App Tracking Protection disabled. Tap to re-enable.' */ +"appTP.home.disabledSuffix" = "Tap to continue blocking tracking attempts across your apps."; + +/* Text indicating the tracking event occured 'just now'. Example: Last attempt 'just now' */ +"appTP.justNow" = "Proprio adesso"; + +/* View to manage trackers for AppTP. Allows the user to turn trackers on or off. */ +"appTP.manageTrackers" = "Gestisci sistemi di tracciamento"; + +/* Button title for AppTP onboarding */ +"appTP.onboarding.continueButton" = "Continue"; + +/* Button title for AppTP onboarding to enable AppTP */ +"appTP.onboarding.enableeButton" = "Enable App Tracking Protection"; + +/* Button title for AppTP onboarding to learn more about AppTP */ +"appTP.onboarding.learnMoreButton" = "Learn More"; + +/* First part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info1" = "Over 85% of free iOS apps"; + +/* Second part of info on the first AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page1Info2" = " we’ve tested allow other companies to track your personal information, even when you’re sleeping."; + +/* Third part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info3" = "See who we catch trying to track you in your apps and take back control."; + +/* First part of info on the second AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page2Info1" = "App Tracking Protection "; + +/* Second part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info2" = "detects and blocks app trackers from other companies,"; + +/* Third part of info on the second AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page2Info3" = " like when Google attempts to track you in a health app."; + +/* Fourth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info4" = "It’s free,"; + +/* Fifth part of info on the second AppTP onboarding page (note the leading and trailing space) */ +"appTP.onboarding.page2Info5" = " and you can enjoy your apps as you normally would. Working in the background, it helps "; + +/* Sixth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info6" = "protect you night and day."; + +/* First part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info1" = "App Tracking Protection is not a VPN."; + +/* Second part of info on the third AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page3Info2" = " However, your device will recognize it as one. This is because it uses a local VPN connection to work."; + +/* Third part of info on the third AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page3Info3" = "App Tracking Protection is different. "; + +/* Fourth part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info4" = "It never routes app data through an external server."; + +/* Title for first AppTP onboarding page */ +"appTP.onboarding.title1" = "One easy step for better app privacy!"; + +/* Title for second AppTP onboarding page */ +"appTP.onboarding.title2" = "How does it work?"; + +/* Title for third AppTP onboarding page */ +"appTP.onboarding.title3" = "Who sees your data?"; + +/* Breakage report app name label */ +"appTP.report.appLabel" = "Which app is having issues?"; + +/* Breakage report app name placeholder */ +"appTP.report.appPlaceholder" = "App name"; + +/* Breakage report category label */ +"appTP.report.categoryLabel" = "Cosa sta succede?"; + +/* Breakage report comment label */ +"appTP.report.commentLabel" = "Comments"; + +/* Breakage report comment placeholder */ +"appTP.report.commentPlaceholder" = "Add additional details"; + +/* Breakage report footer explaining what is collected in the breakage report */ +"appTP.report.footer" = "In addition to the details entered into this form, your app issue report will contain:\n• A list of trackers blocked in the last 10 min\n• Whether App Tracking Protection is enabled\n• Aggregate DuckDuckGo app diagnostics"; + +/* Breakage report submit button */ +"appTP.report.submit" = "Invia"; + +/* Breakage report form title */ +"appTP.report.title" = "Report Issue"; + +/* Breakage report succcess message */ +"appTP.report.toast" = "Grazie! Il feedback è stato inviato."; + +/* Cancel button for 'Report an Issue' alert. */ +"appTP.reportAlert.cancel" = "Non adesso"; + +/* Confirm button for 'Report an Issue' alert. */ +"appTP.reportAlert.confirm" = "Segnala problema"; + +/* Message for 'Report an Issue' alert. */ +"appTP.reportAlert.message" = "Let us know if you disabled App Tracking Protection for this specific tracker because it caused app issues. Your feedback helps us improve!"; + +/* Title for 'Report an Issue' alert. */ +"appTP.reportAlert.title" = "Report Issue?"; + +/* Toast notification diplayed after restoring the blocklist to default settings */ +"appTP.restoreDefaultsToast" = "Default settings restored"; + +/* Button to restore the blocklist to its default state. */ +"appTP.restoreDefualts" = "Ripristina i valori predefiniti"; + +/* Title for the App Tracking Protection feature */ +"appTP.title" = "Protezione dal tracciamento delle app"; + +/* Text indicating when the tracker was last allowed. Example: Last attempt allowed (timeString) */ +"appTP.trackerAllowedTimestamp" = "Ultimo tentativo consentito alle %@"; + +/* Text indicating when the tracker was last blocked. Example: Last attempt blocked (timeString) */ +"appTP.trackerBlockedTimestamp" = "Ultimo tentativo bloccato alle %@"; + +/* Do not translate. StringsDict entry -- Subtitle for tracking attempts in App Tracking Protection Activity View. Example: (count) tracking attempts */ +"appTP.trackingattempts" = "appTP.trackingattempts"; + /* Authentication Alert Sign In Button */ "auth.alert.login.button" = "Accedi"; @@ -301,6 +529,18 @@ /* Toast message when a login item without a title is deleted */ "autofill.logins.list.login-deleted-message-no-title" = "Dati di accesso eliminati"; +/* Title for a button that allows a user to reset their list of never saved sites */ +"autofill.logins.list.never.saved" = "Ripristina siti esclusi"; + +/* Cancel button for resetting list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.cancel" = "Annulla"; + +/* Confirm button to reset list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.confirm" = "Ripristina siti esclusi"; + +/* Alert title */ +"autofill.logins.list.never.saved.reset.action.title" = "Se ripristini i siti esclusi, ti verrà richiesto di salvare i tuoi dati di accesso la prossima volta che accederai a uno di questi siti."; + /* Placeholder for search field on autofill login listing */ "autofill.logins.list.search-placeholder" = "Cerca dati di accesso"; @@ -335,7 +575,7 @@ "autofill.logins.prompt.password.button.title" = "Password per %@"; /* Title for autofill login prompt */ -"autofill.logins.prompt.title" = "Usare i dati di accesso salvati?"; +"autofill.logins.prompt.title" = "Utilizzare una password salvata?"; /* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ "autofill.logins.search.no-results.subtitle" = "per \"%@\""; @@ -385,27 +625,24 @@ /* Title for the alert dialog telling the user an updated username is no longer a private email address */ "autofill.removed.duck.address.title" = "Il nome utente Private Duck Address è stato rimosso"; +/* CTA displayed on modal asking if the user never wants to be prompted to save a login for this website agin */ +"autofill.save-login.never-prompt.CTA" = "Non chiedere mai per questo sito"; + /* Message displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.message" = "Gli accessi sono archiviati in modo sicuro sul tuo dispositivo nel menu Accessi."; +"autofill.save-login.new-user.message" = "Le password sono archiviate in modo sicuro sul tuo dispositivo nel menu Accessi."; /* Title displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.title" = "Vuoi che DuckDuckGo salvi i dati di accesso?"; +"autofill.save-login.new-user.title" = "Vuoi che DuckDuckGo salvi la password?"; /* Cancel CTA displayed on modal asking for the user to save the login */ "autofill.save-login.not-now.CTA" = "Non salvare"; -/* Confirm CTA displayed on modal asking for the user to save the login */ -"autofill.save-login.save.CTA" = "Salva dati di accesso"; - /* Title displayed on modal asking for the user to save the login */ "autofill.save-login.title" = "Salvare dati di accesso?"; /* Confirm CTA displayed on modal asking for the user to save the password */ "autofill.save-password.save.CTA" = "Salva password"; -/* Title displayed on modal asking for the user to save the password */ -"autofill.save-password.title" = "Salvare password?"; - /* Accessibility title for a Show Password button displaying actial password instead of ***** */ "autofill.show-password" = "Mostra password"; @@ -413,7 +650,7 @@ "autofill.signin.to.manage" = "%@ per gestire i tuoi Duck Address su questo dispositivo."; /* Message displayed on modal asking for the user to update the password */ -"autofill.update-password.message" = "DuckDuckGo aggiornerà questi dati di accesso memorizzati sul tuo dispositivo."; +"autofill.update-password.message" = "DuckDuckGo aggiornerà questa password memorizzata sul tuo dispositivo."; /* Confirm CTA displayed on modal asking for the user to update the password */ "autofill.update-password.save.CTA" = "Aggiorna password"; @@ -442,6 +679,15 @@ /* Placeholder in the add bookmark form */ "bookmark.address.placeholder" = "www.example.com"; +/* Delete bookmark alert message */ +"bookmark.delete.alert.message" = "L'operazione eliminerà il tuo segnalibro per \"%@\""; + +/* Delete bookmark alert title */ +"bookmark.delete.alert.title" = "Delete?"; + +/* The message shown after a bookmark has been deleted */ +"bookmark.deleted.toast" = "Segnalibro eliminato"; + /* Delete bookmark folder alert delete button */ "bookmark.deleteFolderAlert.deleteButton" = "Cancella"; @@ -538,9 +784,21 @@ /* Broken Site Category */ "brokensite.category.videos" = "Il video non è stato riprodotto"; +/* Broken Site Category Placeholder */ +"brokensite.categoryPlaceholder" = "Seleziona il problema dall'elenco..."; + +/* Broken Site Category Section Title */ +"brokensite.categoryTitle" = "SELEZIONA UNA CATEGORIA"; + +/* Broken Site Comment Placeholder */ +"brokensite.commentPlaceholder" = "Condividere maggiori dettagli può aiutarci a risolvere questo problema"; + /* Broken Site Section Title */ "brokensite.sectionTitle" = "DESCRIVI COSA È SUCCESSO"; +/* No comment provided by engineer. */ +"bucket: %@" = "bucket: %@"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Ultimo mese"; @@ -554,7 +812,7 @@ "date.range.yesterday" = "Ieri"; /* Button title accepting to enable feature to automatically manage cookie popups */ -"dax.cookie-consent.button.accept" = "Gestione dei popup dei cookie"; +"dax.cookie-consent.button.accept" = "Gestisci popup dei cookie"; /* Button title rejecting to enable feature to automatically manage cookie popups */ "dax.cookie-consent.button.reject" = "No, grazie"; @@ -772,6 +1030,39 @@ /* Empty search placeholder on bookmarks search */ "empty.search" = "Nessuna corrispondenza trovata"; +/* No comment provided by engineer. */ +"Error" = "Errore"; + +/* Button title to Sign In */ +"error.email-protection-sign-in.action" = "Accedi"; + +/* Alert message */ +"error.email-protection-sign-in.body" = "Siamo spiacenti, accedi nuovamente per riattivare le funzioni di Email Protection su questo browser."; + +/* Alert title */ +"error.email-protection-sign-in.title" = "Errore di Email Protection"; + +/* Button title to open device settings */ +"error.insufficient-disk-space.action" = "Apri Impostazioni"; + +/* Alert message */ +"error.insufficient-disk-space.body" = "Sembra che il tuo dispositivo abbia esaurito lo spazio di archiviazione. Liberane un po' per continuare."; + +/* Alert title */ +"error.insufficient-disk-space.title" = "Spazio di archiviazione insufficiente"; + +/* Button title that is shutting down the app */ +"error.preemptive-crash.action" = "Chiudi l'app"; + +/* Alert message */ +"error.preemptive-crash.body" = "Sembra che si sia verificato un problema con l'app, pertanto, deve essere chiusa. Riaprila per continuare."; + +/* Alert title */ +"error.preemptive-crash.title" = "Rilevato un problema con l'app"; + +/* Generic error message on a dialog for when the cause is not known. */ +"error.unknown.try.again" = "An unknown error has occurred"; + /* No comment provided by engineer. */ "favorite" = "Preferito"; @@ -991,6 +1282,18 @@ /* Home tab title */ "homeTab.title" = "Schermata iniziale"; +/* OK title for invite screen alert dismissal button */ +"invite.alert.ok.button" = "OK"; + +/* Continue button on an invite dialog */ +"invite.dialog.continue.button" = "Continue"; + +/* Get Started button on an invite dialog */ +"invite.dialog.get.started.button" = "Get Started"; + +/* Message to show after user enters an unrecognized invite code */ +"invite.dialog.unrecognized.code.message" = "We didn’t recognize this Invite Code."; + /* No comment provided by engineer. */ "keyCommandAddBookmark" = "Aggiungi ai segnalibri"; @@ -1057,15 +1360,15 @@ /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "DuckDuckGo per Mac ti offre la velocità di cui hai bisogno, le funzioni di navigazione che desideri e i migliori strumenti per la privacy."; -/* Body text for the macOS waitlist notification */ -"mac-waitlist.available.notification.body" = "Apri il tuo invito"; - /* Title for the macOS waitlist notification */ "mac-waitlist.available.notification.title" = "DuckDuckGo per Mac è adesso disponibile."; /* Title for the copy action */ "mac-waitlist.copy" = "Copia"; +/* Description text above the Share Link button */ +"mac-waitlist.join-waitlist-screen.on-your-computer-go-to" = "Sul tuo computer Windows, apri il browser web e vai su:"; + /* Description text above the Share Link button */ "mac-waitlist.join-waitlist-screen.on-your-mac-go-to" = "Sul tuo Mac, visita:"; @@ -1078,6 +1381,9 @@ /* Disclaimer for the Join Waitlist screen */ "mac-waitlist.join-waitlist-screen.windows" = "La versione per Windows arriverà presto!"; +/* Title for the macOS waitlist button redirecting to Windows waitlist */ +"mac-waitlist.join-waitlist-screen.windows-waitlist" = "Cerchi la versione per Windows?"; + /* Title for the settings subtitle */ "mac-waitlist.settings.browse-privately" = "Naviga in privato con la nostra app per Mac"; @@ -1088,7 +1394,7 @@ "mac-waitlist.share-sheet.title" = "DuckDuckGo per Mac"; /* Title for the Mac Waitlist feature */ -"mac-waitlist.title" = "App desktop DuckDuckGo"; +"mac-waitlist.title" = "App DuckDuckGo per Mac"; /* No comment provided by engineer. */ "menu.button.hint" = "Menu di navigazione"; @@ -1105,9 +1411,81 @@ /* Edit button */ "navigation.title.edit" = "Modifica"; +/* String indicating NetP is connected when viewed from the settings screen */ +"netP.cell.connected" = "Connected"; + +/* String indicating NetP is disconnected when viewed from the settings screen */ +"netP.cell.disconnected" = "Not connected"; + +/* Title for the Network Protection feature */ +"netP.title" = "Network Protection"; + +/* Message for the network protection invite dialog */ +"network.protection.invite.dialog.message" = "Enter your invite code to get started."; + +/* Title for the network protection invite screen */ +"network.protection.invite.dialog.title" = "You're invited to try Network Protection"; + +/* Prompt for the network protection invite code text field */ +"network.protection.invite.field.prompt" = "Invite Code"; + +/* Message explaining that netP is invite only */ +"network.protection.invite.only.message" = "DuckDuckGo Network Protection is currently invite-only."; + +/* Message for the network protection invite success view */ +"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; + +/* Title for the network protection invite success view */ +"network.protection.invite.success.title" = "Success! You’re in."; + +/* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ +"network.protection.status.connected.format" = "Connected - %@"; + +/* The label for the NetP VPN when connecting */ +"network.protection.status.connecting" = "Connecting..."; + +/* The label for the NetP VPN when disconnected */ +"network.protection.status.disconnected" = "Not connected"; + +/* The label for the NetP VPN when disconnecting */ +"network.protection.status.disconnecting" = "Disconnecting..."; + +/* Message label text for the netP status view */ +"network.protection.status.header.message" = "DuckDuckGo's VPN secures all of your device's Internet traffic anytime, anywhere."; + +/* Header title label text for the status view when netP is disconnected */ +"network.protection.status.header.title.off" = "Network Protection is Off"; + +/* Header title label text for the status view when netP is connected */ +"network.protection.status.header.title.on" = "Network Protection is On"; + +/* The status view 'Share Feedback' button which is shown inline on the status view after the \(netPInviteOnlyMessage) text */ +"network.protection.status.menu.share.feedback" = "Share Feedback"; + +/* Connection details label shown in NetworkProtection's status view. */ +"network.protection.status.view.connection.details" = "Connection Details"; + +/* Generic connection failed error message shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.message" = "Please try again later."; + +/* Generic connection failed error title shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; + +/* IP Address label shown in NetworkProtection's status view. */ +"network.protection.status.view.ip.address" = "IP Address"; + +/* Location label shown in NetworkProtection's status view. */ +"network.protection.status.view.location" = "Location"; + +/* Title label text for the status view when netP is disconnected */ +"network.protection.status.view.title" = "Network Protection"; + /* Do not translate - stringsdict entry */ "number.of.tabs" = "number.of.tabs"; +/* No comment provided by engineer. */ +"Ok" = "OK"; + /* Text displayed on notification appearing in the address bar when the browser dismissed the cookie popup automatically rejecting it */ "omnibar.notification.cookies-managed" = "Cookie gestiti"; @@ -1147,6 +1525,18 @@ /* ’Link’ is link on a website */ "open.externally.failed" = "Impossibile trovare l'app necessaria per aprire il link"; +/* No comment provided by engineer. */ +"Please enter which app on your device is broken." = "Indica quale app del tuo dispositivo non funziona."; + +/* Activate button */ +"pm.activate" = "Reactivate"; + +/* Cancel button */ +"pm.cancel" = "Cancel"; + +/* Deactivate button */ +"pm.deactivate" = "Deactivate"; + /* No comment provided by engineer. */ "preserveLogins.domain.list.footer" = "Per mantenere l'accesso degli utenti, i siti web si affidano ai cookie. Quando attivi la Protezione per un sito, i cookie non verranno cancellati e manterrai l'accesso, anche dopo aver usato il pulsante Proteggi (icona del fuoco). Continueremo a bloccare i tracker di terze parti che si trovano su siti web protetti."; @@ -1216,8 +1606,8 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Preferiti"; -/* No comment provided by engineer. */ -"settings.about.text" = "DuckDuckGo è il nuovo standard di sicurezza online.\n\nIl Privacy Browser di DuckDuckGo ti offre tutti i privacy essentials di cui hai bisogno per proteggerti mentre navighi ed effettui ricerche online, inclusi il blocco dei sistemi di tracciamento, una crittografia più sofisticata e la navigazione in incognito DuckDuckGo.\n\nDel resto, Internet non deve essere percepito come un luogo inquietante e avere la meritata privacy online dovrebbe essere semplice come chiudere le persiane."; +/* about page */ +"settings.about.text" = "Fondata nel 2008, DuckDuckGo è un'azienda indipendente che tutela la privacy online per tutti coloro che sono stanchi di essere tracciati online e vogliono una soluzione semplice. Siamo la dimostrazione che si può garantire una vera protezione della privacy online senza compromessi.\n\nIl browser DuckDuckGo è ricco di funzioni essenziali come i segnalibri, le schede, le password e molto altro ancora, [oltre a numerose e potenti protezioni della privacy](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/) che normalmente non vengono offerte dai browser più comuni. Stiamo parlando di uno straordinario set completo per la protezione della privacy, che aiuta a proteggere le attività online, dalla ricerca alla navigazione, alle e-mail e molto altro ancora.\n\nCon le nostre protezioni della privacy non è necessario conoscere particolari tecnici o dover affrontare configurazioni complesse. È sufficiente scegliere il browser DuckDuckGo su tutti i dispositivi personali per avere automaticamente garantita la privacy.\n\nPer saperne di più su come funzionano le protezioni della privacy di DuckDuckGo, è possibile consultare le nostre [pagine di aiuto](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/)."; /* Report a Broken Site screen confirmation button */ "siteFeedback.buttonText" = "Invia segnalazione"; @@ -1237,6 +1627,9 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Quale sito web è danneggiato?"; +/* No comment provided by engineer. */ +"sync.remove-device.message" = "\"%@\" non potrà più accedere ai tuoi dati sincronizzati."; + /* Accessibility label on remove button */ "tab.close.home" = "Chiudi la scheda Home"; @@ -1309,6 +1702,57 @@ /* Voice-search footer note with on-device privacy warning */ "voiceSearch.footer.note" = "L'audio viene elaborato sul dispositivo e non viene memorizzato né condiviso con altri utenti, nemmeno con DuckDuckGo."; +/* Title for the button to enable push notifications in system settings */ +"waitlist.allow-notifications" = "Consenti notifiche"; + +/* Body text for the waitlist notification */ +"waitlist.available.notification.body" = "Apri il tuo invito"; + +/* Title for the copy action */ +"waitlist.copy" = "Copia"; + +/* Label text for the invite code */ +"waitlist.invite-code" = "Codice invito"; + +/* Step title on the invite screen */ +"waitlist.invite-screen.step.title" = "Fase %d"; + +/* Title for the invite code screen */ +"waitlist.invite-screen.youre-invited" = "Hai ricevuto un invito!"; + +/* Title for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.join" = "Iscriviti alla lista d'attesa privata"; + +/* Temporary status text for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.joining" = "Iscrizione alla lista d'attesa..."; + +/* Title for the Share Link button */ +"waitlist.join-waitlist-screen.share-link" = "Condividi link"; + +/* Notification text for the waitlist */ +"waitlist.joined.no-notification.get-notification" = "INVIAMI UNA NOTIFICA"; + +/* Title for the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.get-notification-confirmation-title" = "Vuoi ricevere una notifica per sapere quando è il tuo turno?"; + +/* Cancel button in the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.no-thanks" = "No, grazie"; + +/* Text used for the Notifications Disabled state */ +"waitlist.notification.disabled" = "Possiamo informarti non appena sarà il tuo turno, tuttavia le notifiche sono attualmente disabilitate per DuckDuckGo."; + +/* Privacy disclaimer for the Waitlist feature */ +"waitlist.privacy-disclaimer" = "Per iscriverti alla lista d'attesa non dovrai comunicare alcuna informazione personale. Ti assicurerai un posto in fila con un timestamp presente esclusivamente sul tuo dispositivo, così potremo avvisarti quando sarà il tuo turno."; + +/* Title for the queue screen */ +"waitlist.queue-screen.on-the-list" = "Sei in lista d'attesa."; + +/* Title for the settings subtitle */ +"waitlist.settings.download-available" = "Download disponibile"; + +/* Title for the share sheet entry */ +"waitlist.share-sheet.title" = "Hai ricevuto un invito!"; + /* Confirmation message */ "web.url.remove.favorite.done" = "Preferito rimosso"; @@ -1333,3 +1777,51 @@ /* Alert title explaining the message is shown by a website */ "webJSAlert.website-message.format" = "Un messaggio da parte di %@:"; +/* No comment provided by engineer. */ +"Welcome to the Duck Side!" = "Dax the Duck ti dà il benvenuto!"; + +/* Title for the Windows waitlist notification */ +"windows-waitlist.available.notification.title" = "Prova DuckDuckGo per Windows."; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-1.description" = "Visita questo URL sul tuo dispositivo Windows per scaricarlo:"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-2.description" = "Apri l'installer DuckDuckGo in Download, seleziona Installa, quindi inserisci il tuo codice di invito."; + +/* Subtitle for the Windows Waitlist Invite screen */ +"windows-waitlist.invite-screen.subtitle" = "Vuoi usare DuckDuckGo su Windows?"; + +/* Title for the Windows waitlist button redirecting to Mac waitlist */ +"windows-waitlist.join-waitlist-screen.mac-waitlist" = "Cerchi la versione per Mac?"; + +/* Title for the Join Windows Waitlist screen */ +"windows-waitlist.join-waitlist-screen.try-duckduckgo-for-windows" = "Ottieni l'accesso anticipato per provare DuckDuckGo per Windows!"; + +/* Message for the alert to confirm enabling notifications */ +"windows-waitlist.joined.no-notification.get-notification-confirmation-message" = "Ti invieremo una notifica quando la tua copia di DuckDuckGo per Windows sarà pronta per il download. "; + +/* Label text for the Joined Waitlist state with notifications declined */ +"windows-waitlist.joined.notifications-declined" = "L'invito a provare DuckDuckGo per Windows arriverà qui. Torna a trovarci a breve, oppure possiamo inviarti una notifica non appena sarà il tuo turno."; + +/* Label text for the Joined Waitlist state with notifications enabled */ +"windows-waitlist.joined.notifications-enabled" = "Ti invieremo una notifica quando la tua copia di DuckDuckGo per Windows sarà pronta per il download."; + +/* Title for the settings subtitle */ +"windows-waitlist.settings.browse-privately" = "Naviga privatamente con la nostra app per Windows"; + +/* Message used when sharing to iMessage. Parameter is an eight digit invite code. */ +"windows-waitlist.share-sheet.invite-code-message" = "Hai ricevuto un invito!\n\nVuoi usare DuckDuckGo su Windows?Passaggio 1 \n Visita questo URL sul tuo dispositivo Windows per effettuare il download:\n https://duckduckgo.com/windows\n\nPassaggio 2\nApri l'installer DuckDuckGo in Download, seleziona Installa, quindi inserisci il tuo codice di invito.\n\nCodice invito: %@"; + +/* Message used when sharing to iMessage */ +"windows-waitlist.share-sheet.message" = "Vuoi iniziare a navigare su Windows mantenendo la tua privacy?\n\nVisita questo URL dal tuo computer per effettuare il download:\nhttps://duckgo.com/windows"; + +/* Summary text for the Windows browser waitlist */ +"windows-waitlist.summary" = "DuckDuckGo per Windows ha ciò di cui hai bisogno per navigare con maggiore privacy: ricerca privata, blocco dei sistemi di tracciamento, crittografia forzata e blocco dei popup dei cookie, oltre a ulteriori protezioni di prima classe in arrivo."; + +/* Title for the Windows Waitlist feature */ +"windows-waitlist.title" = "App DuckDuckGo per Windows"; + +/* Title for the Windows browser download link page */ +"windows-waitlist.waitlist-download-screen.try-duckduckgo-for-windows" = "Scarica DuckDuckGo per Windows!"; + diff --git a/DuckDuckGo/it.lproj/Settings.strings b/DuckDuckGo/it.lproj/Settings.strings index e8ac6639a9..c50d0f31a9 100644 --- a/DuckDuckGo/it.lproj/Settings.strings +++ b/DuckDuckGo/it.lproj/Settings.strings @@ -37,6 +37,9 @@ /* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ "9Ko-0g-T3h.text" = "Dimensione testo"; +/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ +"9kt-6R-XiZ.text" = "Titolo"; + /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Avvio dell'app"; @@ -62,7 +65,7 @@ "bIg-0T-UWq.text" = "Chiedi di rendere i siti web resistenti al fuoco al momento dell'accesso"; /* Class = "UINavigationItem"; title = "Manage Cookie Pop-ups"; ObjectID = "btj-ri-kRr"; */ -"btj-ri-kRr.title" = "Gestione dei popup dei cookie"; +"btj-ri-kRr.title" = "Gestione popup dei cookie"; /* Class = "UILabel"; text = "App Icon"; ObjectID = "cKo-er-HNj"; */ "cKo-er-HNj.text" = "Icona dell'app"; @@ -73,6 +76,12 @@ /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Global Privacy Control (GPC)"; +/* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ +"CxT-QK-iVn.title" = "Aggiungi"; + +/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ +"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; + /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Uscita dall'app, inattività di 15 minuti"; @@ -143,7 +152,7 @@ "gji-Fs-EET.text" = "La privacy semplificata. "; /* Class = "UILabel"; text = "Manage Cookie Pop-ups"; ObjectID = "GRv-M2-Kx1"; */ -"GRv-M2-Kx1.text" = "Gestione dei popup dei cookie"; +"GRv-M2-Kx1.text" = "Gestisci popup dei cookie"; /* Class = "UINavigationItem"; title = "Theme"; ObjectID = "gS2-mg-l7R"; */ "gS2-mg-l7R.title" = "Tema"; @@ -151,6 +160,9 @@ /* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ "HLr-R8-xxF.text" = "Anteprime con pressione prolungata"; +/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ +"hoT-Nu-KXP.text" = "Naviga privatamente con la nostra app per Windows"; + /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Tutela della privacy attivata per tutti i siti"; @@ -163,6 +175,12 @@ /* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ "m23-t6-9cb.text" = "Condividi feedback"; +/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ +"mLn-1x-Fl5.title" = "Posizione Barra degli indirizzi"; + +/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ +"mqV-pf-NZ1.text" = "Posizione Barra degli indirizzi"; + /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Consenti a DuckDuckGo di gestire i popup per il consenso ai cookie"; @@ -175,15 +193,27 @@ /* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ "oM7-1o-9oY.text" = "Info su DuckDuckGo"; +/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ +"opn-JO-idF.text" = "Inizio"; + /* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ "OxE-MQ-uJk.headerTitle" = "Ulteriori informazioni su DuckDuckGo"; +/* Class = "UILabel"; text = "Sync"; ObjectID = "oXN-ez-gct"; */ +"oXN-ez-gct.text" = "Sincronizzazione"; + /* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ "P0F-ts-ekd.text" = "Naviga in privato con la nostra app per Mac "; +/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ +"qah-gb-udB.text" = "Network Protection"; + /* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ "qeN-SV-zy7.text" = "Etichetta"; +/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ +"RQ8-H1-Ez1.text" = "App Windows DuckDuckGo"; + /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Dimensione testo"; @@ -232,8 +262,8 @@ /* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ "yvj-LL-MiR.text" = "Aggiungi app al tuo dock"; -/* Class = "UILabel"; text = "DuckDuckGo Desktop App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "App desktop DuckDuckGo"; +/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ +"Yz9-17-qnn.text" = "App Mac DuckDuckGo"; /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Nuova scheda"; diff --git a/DuckDuckGo/lt.lproj/Feedback.strings b/DuckDuckGo/lt.lproj/Feedback.strings index 15fd0596ba..018d11813a 100644 --- a/DuckDuckGo/lt.lproj/Feedback.strings +++ b/DuckDuckGo/lt.lproj/Feedback.strings @@ -28,9 +28,6 @@ /* Class = "UIBarButtonItem"; title = "Back"; ObjectID = "JF8-Ow-zXL"; */ "JF8-Ow-zXL.title" = "Atgal"; -/* Class = "UIBarButtonItem"; title = "Submit"; ObjectID = "jr6-Al-4fr"; */ -"jr6-Al-4fr.title" = "Pateikti"; - /* Class = "UIButton"; normalTitle = "Submit"; ObjectID = "lsB-Kq-U8p"; */ "lsB-Kq-U8p.normalTitle" = "Pateikti"; @@ -58,9 +55,6 @@ /* Class = "UINavigationItem"; title = "Report Broken Site"; ObjectID = "WjT-HM-yim"; */ "WjT-HM-yim.title" = "Pranešti apie sugadintą svetainę"; -/* Class = "UILabel"; text = "Broken Site Reason"; ObjectID = "Wx3-Vb-ItS"; */ -"Wx3-Vb-ItS.text" = "Svetainės trikties priežastis"; - /* Class = "UINavigationItem"; title = "Share Feedback"; ObjectID = "Xb0-Zu-opM"; */ "Xb0-Zu-opM.title" = "Bendrinti atsiliepimą"; diff --git a/DuckDuckGo/lt.lproj/Localizable.strings b/DuckDuckGo/lt.lproj/Localizable.strings index d5a911be54..3a1bb62474 100644 --- a/DuckDuckGo/lt.lproj/Localizable.strings +++ b/DuckDuckGo/lt.lproj/Localizable.strings @@ -1,6 +1,9 @@ /* No comment provided by engineer. */ "%@" = "%@"; +/* No comment provided by engineer. */ +"%@ [%@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)" = "%1$@ [%2$@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)"; + /* Buton label for Edit action */ "action.generic.edit" = "Redaguoti"; @@ -103,6 +106,12 @@ /* Share action */ "action.title.share" = "Bendrinti"; +/* Settings label for bottom position for the address bar */ +"address.bar.bottom" = "Apačia"; + +/* Settings label for top position for the address bar */ +"address.bar.top" = "Viršus"; + /* No comment provided by engineer. */ "addWidget.button" = "Pridėti valdiklį"; @@ -142,6 +151,225 @@ /* Shown on authentication screen */ "app.authentication.unlock" = "Atrakinti „DuckDuckGo“."; +/* First part of about page content (note the trailing space) */ +"appTP.about.content1" = "You’ve probably heard about companies like Google and Facebook tracking you behind the scenes on third-party websites. But did you know they also track your personal information through apps on your device?\n\nIn 2022, DuckDuckGo found that "; + +/* Second part of about page content (note the trailing space) */ +"appTP.about.content2" = "over 85% of free iOS apps tested contained hidden trackers from other companies."; + +/* Third part of about page content (note the leading space) */ +"appTP.about.content3" = " Of the 395 apps tested, 60% sent data to Google. This happens even while you’re not using your device.\n\nTrackers in apps may have access to a lot more information than their website tracker cousins, such as your location down to which floor of a building you're on, how often you play games while at work, and when and how long you sleep each day. Even if you haven’t given apps explicit permission to collect data, they can still take it without your knowledge.\n\nTracking networks like Facebook and Google use these little pieces of information to build a digital profile about you. With it, tracking networks can manipulate what you see online and allow advertisers to bid on access to you based on your data.\n\nTrackers in apps is a BIG problem for privacy. But DuckDuckGo has a solution that can help.\n\nWhen enabled in the DuckDuckGo Privacy Browser app, App Tracking Protection blocks many trackers in other apps, not just the trackers we find on websites when you browse. These dual layers of protection reduce what companies know about you overall, so you can use your apps with more peace of mind, knowing you’re more protected."; + +/* Navigation Title for AppTP about page */ +"appTP.about.navTitle" = "About App Trackers"; + +/* Title for AppTP about page */ +"appTP.about.title" = "What Are App Trackers?"; + +/* Title for 'Report an Issue' button in the activity view. */ +"appTP.activityView.reportIssue" = "Report Issue"; + +/* Text label for switch that turns blocking on or off for a tracker */ +"appTP.blockTrackerText" = "Block this Tracker"; + +/* Detail string describing what AppTP is */ +"appTP.cell.detail" = "Blokuokite programėlių stebėjimo priemones savo įrenginyje"; + +/* String indicating AppTP is disabled when viewed from the settings screen */ +"appTP.cell.disabled" = "Išjungta"; + +/* String indicating AppTP is enabled when viewed from the settings screen */ +"appTP.cell.enabled" = "Įjungta"; + +/* Info string informing the user what App Tracking Protection does. */ +"appTP.empty.disabled.info" = "Įjunkite apsaugą nuo programėlių sekimo, kad galėtume blokuoti įkyrius sekiklius kitose programėlėse."; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.heading" = "We’re blocking hidden trackers"; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.info" = "Come back soon to see a list of all the app trackers we’ve blocked."; + +/* First answer for AppTP FAQ page */ +"appTP.faq.answer1" = "App Tracking Protection blocks app trackers from other companies, like when Facebook tries to track you in a banking app. Companies may still track you in apps they own."; + +/* Second answer for AppTP FAQ page */ +"appTP.faq.answer2" = "Yes! App Tracking Protection works across all apps on your device to block the most common hidden trackers we find trying to collect your personal info."; + +/* Third answer for AppTP FAQ page */ +"appTP.faq.answer3" = "We currently only block the most common trackers that we find on iOS. This helps us to comprehensively test App Tracking Protection and lower frequency of app breakage, while blocking up to 70% of all tracking requests."; + +/* Fourth answer for AppTP FAQ page */ +"appTP.faq.answer4" = "You’ll be asked to set up a virtual private network (VPN) connection, but you don't need to install a VPN app for App Tracking Protection to work.\n\nThis permission, which works only on your device, allows App Tracking Protection to monitor network traffic so that it can block known trackers."; + +/* Fifth answer for AppTP FAQ page */ +"appTP.faq.answer5" = "You can use App Tracking Protection at the same time as using an IKEv2 protocol VPN app on an iOS device. You won’t be able to use App Tracking Protection on an iOS device if you’re using a VPN app that uses a different type of protocol, like WireGuard or OpenVPN type VPNs."; + +/* Sixth answer for AppTP FAQ page */ +"appTP.faq.answer6" = "A VPN sends your data from the device to its own server, where it secures and anonymizes your data from prying eyes. However, this allows the VPN company to see your network traffic.\n\nApp Tracking Protection is different. Instead of sending your data to a VPN server, App Tracking Protection works only on your device, sitting between your apps and the servers they talk to.\n\nWhenever App Tracking Protection recognizes a known tracker, it blocks the tracker from sending personal information (such as your IP address, activity, and device details) off your device. All other traffic reaches its destination, so your apps work normally."; + +/* Seventh answer for AppTP FAQ page */ +"appTP.faq.answer7" = "App Tracking Protection works only on your device and doesn’t send your data off your device to DuckDuckGo. We don’t collect or store any data from your apps."; + +/* First question for AppTP FAQ page */ +"appTP.faq.question1" = "How does App Tracking Protection work?"; + +/* Second question for AppTP FAQ page */ +"appTP.faq.question2" = "Does App Tracking Protection block trackers in all apps on my device?"; + +/* Third question for AppTP FAQ page */ +"appTP.faq.question3" = "Does App Tracking Protection block all app trackers?"; + +/* Fourth question for AppTP FAQ page */ +"appTP.faq.question4" = "Why does App Tracking Protection use a VPN connection?"; + +/* Fifth question for AppTP FAQ page */ +"appTP.faq.question5" = "Will App Tracking Protection work if I also use a VPN app?"; + +/* Sixth question for AppTP FAQ page */ +"appTP.faq.question6" = "How is App Tracking Protection different from a VPN?"; + +/* Seventh question for AppTP FAQ page */ +"appTP.faq.question7" = "Is my data private?"; + +/* Title for AppTP FAQ page */ +"appTP.faq.title" = "App Tracking Protection FAQ"; + +/* Do not translate. StringsDict entry -- Count part of string 'App Tracking Protection blocked x tracking attempts today' */ +"appTP.home.blockedCount" = "appTP.home.blockedCount"; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the trailing space) */ +"appTP.home.blockedPrefix" = "Apsauga nuo programėlių sekimo užblokuota "; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the leading space) */ +"appTP.home.blockedSuffix" = " jūsų programėlėse."; + +/* Prefix of string 'App Tracking Protection disabled. Tap to re-enable.' (note the trailing space) */ +"appTP.home.disabledPrefix" = "App Tracking Protection disabled. "; + +/* Suffix of string 'App Tracking Protection disabled. Tap to re-enable.' */ +"appTP.home.disabledSuffix" = "Tap to continue blocking tracking attempts across your apps."; + +/* Text indicating the tracking event occured 'just now'. Example: Last attempt 'just now' */ +"appTP.justNow" = "Ką tik"; + +/* View to manage trackers for AppTP. Allows the user to turn trackers on or off. */ +"appTP.manageTrackers" = "Tvarkyti sekiklius"; + +/* Button title for AppTP onboarding */ +"appTP.onboarding.continueButton" = "Continue"; + +/* Button title for AppTP onboarding to enable AppTP */ +"appTP.onboarding.enableeButton" = "Enable App Tracking Protection"; + +/* Button title for AppTP onboarding to learn more about AppTP */ +"appTP.onboarding.learnMoreButton" = "Learn More"; + +/* First part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info1" = "Over 85% of free iOS apps"; + +/* Second part of info on the first AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page1Info2" = " we’ve tested allow other companies to track your personal information, even when you’re sleeping."; + +/* Third part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info3" = "See who we catch trying to track you in your apps and take back control."; + +/* First part of info on the second AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page2Info1" = "App Tracking Protection "; + +/* Second part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info2" = "detects and blocks app trackers from other companies,"; + +/* Third part of info on the second AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page2Info3" = " like when Google attempts to track you in a health app."; + +/* Fourth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info4" = "It’s free,"; + +/* Fifth part of info on the second AppTP onboarding page (note the leading and trailing space) */ +"appTP.onboarding.page2Info5" = " and you can enjoy your apps as you normally would. Working in the background, it helps "; + +/* Sixth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info6" = "protect you night and day."; + +/* First part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info1" = "App Tracking Protection is not a VPN."; + +/* Second part of info on the third AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page3Info2" = " However, your device will recognize it as one. This is because it uses a local VPN connection to work."; + +/* Third part of info on the third AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page3Info3" = "App Tracking Protection is different. "; + +/* Fourth part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info4" = "It never routes app data through an external server."; + +/* Title for first AppTP onboarding page */ +"appTP.onboarding.title1" = "One easy step for better app privacy!"; + +/* Title for second AppTP onboarding page */ +"appTP.onboarding.title2" = "How does it work?"; + +/* Title for third AppTP onboarding page */ +"appTP.onboarding.title3" = "Who sees your data?"; + +/* Breakage report app name label */ +"appTP.report.appLabel" = "Which app is having issues?"; + +/* Breakage report app name placeholder */ +"appTP.report.appPlaceholder" = "App name"; + +/* Breakage report category label */ +"appTP.report.categoryLabel" = "Kas vyksta?"; + +/* Breakage report comment label */ +"appTP.report.commentLabel" = "Comments"; + +/* Breakage report comment placeholder */ +"appTP.report.commentPlaceholder" = "Add additional details"; + +/* Breakage report footer explaining what is collected in the breakage report */ +"appTP.report.footer" = "In addition to the details entered into this form, your app issue report will contain:\n• A list of trackers blocked in the last 10 min\n• Whether App Tracking Protection is enabled\n• Aggregate DuckDuckGo app diagnostics"; + +/* Breakage report submit button */ +"appTP.report.submit" = "Pateikti"; + +/* Breakage report form title */ +"appTP.report.title" = "Report Issue"; + +/* Breakage report succcess message */ +"appTP.report.toast" = "Dėkojame! Atsiliepimas pateiktas."; + +/* Cancel button for 'Report an Issue' alert. */ +"appTP.reportAlert.cancel" = "Ne dabar"; + +/* Confirm button for 'Report an Issue' alert. */ +"appTP.reportAlert.confirm" = "Pranešti apie problemą"; + +/* Message for 'Report an Issue' alert. */ +"appTP.reportAlert.message" = "Let us know if you disabled App Tracking Protection for this specific tracker because it caused app issues. Your feedback helps us improve!"; + +/* Title for 'Report an Issue' alert. */ +"appTP.reportAlert.title" = "Report Issue?"; + +/* Toast notification diplayed after restoring the blocklist to default settings */ +"appTP.restoreDefaultsToast" = "Default settings restored"; + +/* Button to restore the blocklist to its default state. */ +"appTP.restoreDefualts" = "Atkurti numatytąsias reikšmes"; + +/* Title for the App Tracking Protection feature */ +"appTP.title" = "Programų sekimo apsauga"; + +/* Text indicating when the tracker was last allowed. Example: Last attempt allowed (timeString) */ +"appTP.trackerAllowedTimestamp" = "Paskutinis bandymas leistas %@"; + +/* Text indicating when the tracker was last blocked. Example: Last attempt blocked (timeString) */ +"appTP.trackerBlockedTimestamp" = "Paskutinis bandymas užblokuotas %@"; + +/* Do not translate. StringsDict entry -- Subtitle for tracking attempts in App Tracking Protection Activity View. Example: (count) tracking attempts */ +"appTP.trackingattempts" = "appTP.trackingattempts"; + /* Authentication Alert Sign In Button */ "auth.alert.login.button" = "Prisijungti"; @@ -301,6 +529,18 @@ /* Toast message when a login item without a title is deleted */ "autofill.logins.list.login-deleted-message-no-title" = "Prisijungimas ištrintas"; +/* Title for a button that allows a user to reset their list of never saved sites */ +"autofill.logins.list.never.saved" = "Iš naujo nustatyti neįtrauktas svetaines"; + +/* Cancel button for resetting list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.cancel" = "Atšaukti"; + +/* Confirm button to reset list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.confirm" = "Iš naujo nustatyti neįtrauktas svetaines"; + +/* Alert title */ +"autofill.logins.list.never.saved.reset.action.title" = "Jei iš naujo nustatysite neįtrauktas svetaines, kitą kartą, kai prisijungsite prie bet kurios iš šių svetainių, būsite paraginti išsaugoti savo prisijungimo vardą."; + /* Placeholder for search field on autofill login listing */ "autofill.logins.list.search-placeholder" = "Ieškoti prisijungimų"; @@ -335,7 +575,7 @@ "autofill.logins.prompt.password.button.title" = "Slaptažodis, skirtas %@"; /* Title for autofill login prompt */ -"autofill.logins.prompt.title" = "Naudoti išsaugotą prisijungimą?"; +"autofill.logins.prompt.title" = "Naudoti išsaugotą slaptažodį?"; /* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ "autofill.logins.search.no-results.subtitle" = "„%@“"; @@ -385,27 +625,24 @@ /* Title for the alert dialog telling the user an updated username is no longer a private email address */ "autofill.removed.duck.address.title" = "Privatus „Duck Address“ naudotojo vardas buvo pašalintas"; +/* CTA displayed on modal asking if the user never wants to be prompted to save a login for this website agin */ +"autofill.save-login.never-prompt.CTA" = "Niekada neklauskite šioje svetainėje"; + /* Message displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.message" = "Prisijungimai saugiai saugomi jūsų įrenginio meniu „Prisijungimai“."; +"autofill.save-login.new-user.message" = "Slaptažodžiai saugiai saugomi jūsų įrenginio meniu „Prisijungimai“."; /* Title displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.title" = "Ar norite, kad „DuckDuckGo“ išsaugotų jūsų prisijungimą?"; +"autofill.save-login.new-user.title" = "Ar norite, kad „DuckDuckGo“ išsaugotų jūsų slaptažodį?"; /* Cancel CTA displayed on modal asking for the user to save the login */ "autofill.save-login.not-now.CTA" = "Neišsaugokite"; -/* Confirm CTA displayed on modal asking for the user to save the login */ -"autofill.save-login.save.CTA" = "Išsaugoti prisijungimą"; - /* Title displayed on modal asking for the user to save the login */ "autofill.save-login.title" = "Išsaugoti prisijungimą?"; /* Confirm CTA displayed on modal asking for the user to save the password */ "autofill.save-password.save.CTA" = "Išsaugoti slaptažodį"; -/* Title displayed on modal asking for the user to save the password */ -"autofill.save-password.title" = "Išsaugoti slaptažodį?"; - /* Accessibility title for a Show Password button displaying actial password instead of ***** */ "autofill.show-password" = "Rodyti slaptažodį"; @@ -413,7 +650,7 @@ "autofill.signin.to.manage" = "%@, kad galėtumėte valdyti savo „Duck Address“ šiame įrenginyje."; /* Message displayed on modal asking for the user to update the password */ -"autofill.update-password.message" = "„DuckDuckGo“ atnaujins šį jūsų įrenginyje įrašytą prisijungimą."; +"autofill.update-password.message" = "„DuckDuckGo“ atnaujins šį jūsų įrenginyje išsaugotą slaptažodį."; /* Confirm CTA displayed on modal asking for the user to update the password */ "autofill.update-password.save.CTA" = "Atnaujinti slaptažodį"; @@ -442,6 +679,15 @@ /* Placeholder in the add bookmark form */ "bookmark.address.placeholder" = "www.example.com"; +/* Delete bookmark alert message */ +"bookmark.delete.alert.message" = "Tai ištrins jūsų žymę „%@“"; + +/* Delete bookmark alert title */ +"bookmark.delete.alert.title" = "Delete?"; + +/* The message shown after a bookmark has been deleted */ +"bookmark.deleted.toast" = "Žymė ištrinta"; + /* Delete bookmark folder alert delete button */ "bookmark.deleteFolderAlert.deleteButton" = "Trinti"; @@ -538,9 +784,21 @@ /* Broken Site Category */ "brokensite.category.videos" = "Vaizdo įrašas nepaleistas"; +/* Broken Site Category Placeholder */ +"brokensite.categoryPlaceholder" = "Pasirinkite problemą iš sąrašo..."; + +/* Broken Site Category Section Title */ +"brokensite.categoryTitle" = "PASIRINKITE KATEGORIJĄ"; + +/* Broken Site Comment Placeholder */ +"brokensite.commentPlaceholder" = "Jei pasidalinsime daugiau informacijos, galime padėti išspręsti šią problemą"; + /* Broken Site Section Title */ "brokensite.sectionTitle" = "APRAŠYKITE, KAS ATSITIKO"; +/* No comment provided by engineer. */ +"bucket: %@" = "talpykla: %@"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Praėjęs mėnuo"; @@ -772,6 +1030,39 @@ /* Empty search placeholder on bookmarks search */ "empty.search" = "Atitikmenų nerasta"; +/* No comment provided by engineer. */ +"Error" = "Klaida"; + +/* Button title to Sign In */ +"error.email-protection-sign-in.action" = "Prisijungti"; + +/* Alert message */ +"error.email-protection-sign-in.body" = "Atsiprašome, dar kartą prisijunkite, kad šioje naršyklėje vėl įjungtumėte „Email Protection“ funkcijas."; + +/* Alert title */ +"error.email-protection-sign-in.title" = "„Email Protection“ klaida"; + +/* Button title to open device settings */ +"error.insufficient-disk-space.action" = "Atidaryti Nustatymus"; + +/* Alert message */ +"error.insufficient-disk-space.body" = "Panašu, kad įrenginyje nebėra laisvos vietos. Jei norite tęsti, atlaisvinkite vietos."; + +/* Alert title */ +"error.insufficient-disk-space.title" = "Nepakanka vietos"; + +/* Button title that is shutting down the app */ +"error.preemptive-crash.action" = "Uždaryti programėlę"; + +/* Alert message */ +"error.preemptive-crash.body" = "Atrodo, kad yra programėlės problema ir ją reikia uždaryti. Norėdami tęsti, atidarykite iš naujo."; + +/* Alert title */ +"error.preemptive-crash.title" = "Aptikta programėlės problema"; + +/* Generic error message on a dialog for when the cause is not known. */ +"error.unknown.try.again" = "An unknown error has occurred"; + /* No comment provided by engineer. */ "favorite" = "Mėgstamas"; @@ -991,6 +1282,18 @@ /* Home tab title */ "homeTab.title" = "Pagrindinis"; +/* OK title for invite screen alert dismissal button */ +"invite.alert.ok.button" = "OK"; + +/* Continue button on an invite dialog */ +"invite.dialog.continue.button" = "Continue"; + +/* Get Started button on an invite dialog */ +"invite.dialog.get.started.button" = "Get Started"; + +/* Message to show after user enters an unrecognized invite code */ +"invite.dialog.unrecognized.code.message" = "We didn’t recognize this Invite Code."; + /* No comment provided by engineer. */ "keyCommandAddBookmark" = "Pridėti žymę"; @@ -1057,15 +1360,15 @@ /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "„Mac“ skirta „DuckDuckGo“ pasižymi jums reikiama sparta, naršymo funkcijomis ir aukščiausios klasės privatumo įrankiais."; -/* Body text for the macOS waitlist notification */ -"mac-waitlist.available.notification.body" = "Atidaryti pakvietimą"; - /* Title for the macOS waitlist notification */ "mac-waitlist.available.notification.title" = "„DuckDuckGo for Mac“ paruošta!"; /* Title for the copy action */ "mac-waitlist.copy" = "Kopijuoti"; +/* Description text above the Share Link button */ +"mac-waitlist.join-waitlist-screen.on-your-computer-go-to" = "„Windows“ kompiuteryje eikite į:"; + /* Description text above the Share Link button */ "mac-waitlist.join-waitlist-screen.on-your-mac-go-to" = "„Mac“ kompiuteryje eikite į:"; @@ -1078,6 +1381,9 @@ /* Disclaimer for the Join Waitlist screen */ "mac-waitlist.join-waitlist-screen.windows" = "„Windows“ jau greitai!"; +/* Title for the macOS waitlist button redirecting to Windows waitlist */ +"mac-waitlist.join-waitlist-screen.windows-waitlist" = "Ieškote „Windows“ versijos?"; + /* Title for the settings subtitle */ "mac-waitlist.settings.browse-privately" = "Naršykite privačiai su mūsų „Mac“ skirta programa"; @@ -1088,7 +1394,7 @@ "mac-waitlist.share-sheet.title" = "„Mac“ skirta „DuckDuckGo“"; /* Title for the Mac Waitlist feature */ -"mac-waitlist.title" = "„DuckDuckGo“ darbalaukio programa"; +"mac-waitlist.title" = "„DuckDuckGo“ programa, skirta „Mac“"; /* No comment provided by engineer. */ "menu.button.hint" = "Naršymo meniu"; @@ -1105,9 +1411,81 @@ /* Edit button */ "navigation.title.edit" = "Redaguoti"; +/* String indicating NetP is connected when viewed from the settings screen */ +"netP.cell.connected" = "Connected"; + +/* String indicating NetP is disconnected when viewed from the settings screen */ +"netP.cell.disconnected" = "Not connected"; + +/* Title for the Network Protection feature */ +"netP.title" = "Network Protection"; + +/* Message for the network protection invite dialog */ +"network.protection.invite.dialog.message" = "Enter your invite code to get started."; + +/* Title for the network protection invite screen */ +"network.protection.invite.dialog.title" = "You're invited to try Network Protection"; + +/* Prompt for the network protection invite code text field */ +"network.protection.invite.field.prompt" = "Invite Code"; + +/* Message explaining that netP is invite only */ +"network.protection.invite.only.message" = "DuckDuckGo Network Protection is currently invite-only."; + +/* Message for the network protection invite success view */ +"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; + +/* Title for the network protection invite success view */ +"network.protection.invite.success.title" = "Success! You’re in."; + +/* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ +"network.protection.status.connected.format" = "Connected - %@"; + +/* The label for the NetP VPN when connecting */ +"network.protection.status.connecting" = "Connecting..."; + +/* The label for the NetP VPN when disconnected */ +"network.protection.status.disconnected" = "Not connected"; + +/* The label for the NetP VPN when disconnecting */ +"network.protection.status.disconnecting" = "Disconnecting..."; + +/* Message label text for the netP status view */ +"network.protection.status.header.message" = "DuckDuckGo's VPN secures all of your device's Internet traffic anytime, anywhere."; + +/* Header title label text for the status view when netP is disconnected */ +"network.protection.status.header.title.off" = "Network Protection is Off"; + +/* Header title label text for the status view when netP is connected */ +"network.protection.status.header.title.on" = "Network Protection is On"; + +/* The status view 'Share Feedback' button which is shown inline on the status view after the \(netPInviteOnlyMessage) text */ +"network.protection.status.menu.share.feedback" = "Share Feedback"; + +/* Connection details label shown in NetworkProtection's status view. */ +"network.protection.status.view.connection.details" = "Connection Details"; + +/* Generic connection failed error message shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.message" = "Please try again later."; + +/* Generic connection failed error title shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; + +/* IP Address label shown in NetworkProtection's status view. */ +"network.protection.status.view.ip.address" = "IP Address"; + +/* Location label shown in NetworkProtection's status view. */ +"network.protection.status.view.location" = "Location"; + +/* Title label text for the status view when netP is disconnected */ +"network.protection.status.view.title" = "Network Protection"; + /* Do not translate - stringsdict entry */ "number.of.tabs" = "number.of.tabs"; +/* No comment provided by engineer. */ +"Ok" = "GERAI"; + /* Text displayed on notification appearing in the address bar when the browser dismissed the cookie popup automatically rejecting it */ "omnibar.notification.cookies-managed" = "Slapukai valdomi"; @@ -1147,6 +1525,18 @@ /* ’Link’ is link on a website */ "open.externally.failed" = "Negalima rasti programos, galinčios atidaryti šią nuorodą"; +/* No comment provided by engineer. */ +"Please enter which app on your device is broken." = "Įveskite, kuri programa jūsų įrenginyje sugedusi."; + +/* Activate button */ +"pm.activate" = "Reactivate"; + +/* Cancel button */ +"pm.cancel" = "Cancel"; + +/* Deactivate button */ +"pm.deactivate" = "Deactivate"; + /* No comment provided by engineer. */ "preserveLogins.domain.list.footer" = "Svetainės pagrįstos slapukais, kad liktumėte prisijungę. Apsaugojus svetainę, slapukai nėra ištrinami ir liksite prisijungę net paspaudę apsaugojimo mygtuką. Vis tiek blokuosime trečiųjų šalių stebėjimo priemones, randamas svetainėse, kuriose pasinaudosite parinktimi „Fireproof“."; @@ -1216,8 +1606,8 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Mėgstami"; -/* No comment provided by engineer. */ -"settings.about.text" = "„DuckDuckGo“ nustatome naują patikimumo tinkle standartą.\n\n„DuckDuckGo“ privatumo naršyklė užtikrina visus svarbiausius įrankius, kurie padės apsaugoti savo privatumą ieškant ir naršant tinkle, įskaitant sekiklių blokavimą, išmanųjį šifravimą ir „DuckDuckGo“ privačias paieškas.\n\nMūsų komanda tiki, kad internetas neturėtų būti toks baisus, o privatumas, kurio nusipelnėte internete, turėtų būti ranka pasiekiamas."; +/* about page */ +"settings.about.text" = "„DuckDuckGo“ yra nepriklausoma interneto privatumo bendrovė, įkurta 2008 metais ir skirta visiems, kurie pavargo nuo sekimo internete ir nori lengvo sprendimo. Esame įrodymas, kad galite gauti tikrą privatumo apsaugą internete be kompromisų.\n\n„DuckDuckGo“ naršyklėje yra funkcijos, kurių tikitės iš pagrindinės naršyklės, pvz., žymės, skirtukai, slaptažodžiai ir dar daugiau, taip pat [keliolika galingų privatumo apsaugos priemonių](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/) pagal numatytuosius nustatymus nėra siūlomas daugelyje populiarių naršyklių. Šis unikalus išsamus privatumo apsaugos rinkinys padeda apsaugoti jūsų veiklą internete – nuo paieškos iki naršymo, el. pašto ir kt.\n\nMūsų privatumo apsaugos priemonės veikia nereikalaudamos nieko žinoti apie technines detales ar naudotis sudėtingais nustatymais. Tereikia visuose įrenginiuose perjungti naršyklę į „DuckDuckGo“ ir privatumas bus užtikrintas pagal numatytuosius nustatymus.\n\nTačiau jei norite žvilgtelėti po gaubtu, daugiau informacijos apie tai, kaip veikia „DuckDuckGo“ privatumo apsauga, galite rasti mūsų [pagalbos puslapiuose] (ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/)."; /* Report a Broken Site screen confirmation button */ "siteFeedback.buttonText" = "Pateikti ataskaitą"; @@ -1237,6 +1627,9 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Kuri svetainė neveikia?"; +/* No comment provided by engineer. */ +"sync.remove-device.message" = "„%@“ nebegalės pasiekti jūsų sinchronizuotų duomenų."; + /* Accessibility label on remove button */ "tab.close.home" = "Uždaryti pagrindinio puslapio skirtuką"; @@ -1309,6 +1702,57 @@ /* Voice-search footer note with on-device privacy warning */ "voiceSearch.footer.note" = "Garsas apdorojamas įrenginyje. Jis nesaugomas ir su niekuo, įskaitant „DuckDuckGo“, nebendrinamas."; +/* Title for the button to enable push notifications in system settings */ +"waitlist.allow-notifications" = "Leisti pranešimus"; + +/* Body text for the waitlist notification */ +"waitlist.available.notification.body" = "Atidaryti pakvietimą"; + +/* Title for the copy action */ +"waitlist.copy" = "Kopijuoti"; + +/* Label text for the invite code */ +"waitlist.invite-code" = "Pakvietimo kodas"; + +/* Step title on the invite screen */ +"waitlist.invite-screen.step.title" = "%d veiksmas"; + +/* Title for the invite code screen */ +"waitlist.invite-screen.youre-invited" = "Esate pakviesti!"; + +/* Title for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.join" = "Prisijungti prie privataus laukimo sąrašo"; + +/* Temporary status text for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.joining" = "Prisijungiama prie laukimo sąrašo..."; + +/* Title for the Share Link button */ +"waitlist.join-waitlist-screen.share-link" = "Bendrinti nuorodą"; + +/* Notification text for the waitlist */ +"waitlist.joined.no-notification.get-notification" = "INFORMUOTI"; + +/* Title for the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.get-notification-confirmation-title" = "Ar norite gauti pranešimą, kai ateis jūsų eilė?"; + +/* Cancel button in the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.no-thanks" = "Ne, dėkoju"; + +/* Text used for the Notifications Disabled state */ +"waitlist.notification.disabled" = "Galime jus informuoti, kai bus jūsų eilė, tačiau šiuo metu pranešimai yra išjungti „DuckDuckGo“."; + +/* Privacy disclaimer for the Waitlist feature */ +"waitlist.privacy-disclaimer" = "Jums nereikia dalintis savo asmenine informacija, kad prisijungtumėte prie laukimo sąrašo. Užsitikrinsite vietą eilėje laiko žyma, kuri egzistuoja tik jūsų įrenginyje, kad galėtume jus įspėti, kai ateis jūsų eilė."; + +/* Title for the queue screen */ +"waitlist.queue-screen.on-the-list" = "Esate sąraše!"; + +/* Title for the settings subtitle */ +"waitlist.settings.download-available" = "Galima atsisiųsti"; + +/* Title for the share sheet entry */ +"waitlist.share-sheet.title" = "Esate pakviesti!"; + /* Confirmation message */ "web.url.remove.favorite.done" = "Mėgstamas pašalintas"; @@ -1333,3 +1777,51 @@ /* Alert title explaining the message is shown by a website */ "webJSAlert.website-message.format" = "Pranešimas iš %@:"; +/* No comment provided by engineer. */ +"Welcome to the Duck Side!" = "Sveiki prisijungę prie „Duck“!"; + +/* Title for the Windows waitlist notification */ +"windows-waitlist.available.notification.title" = "Išbandykite „DuckDuckGo“, skirtą „Windows“!"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-1.description" = "Jei norite atsisiųsti, apsilankykite šiuo URL adresu „Windows“ įrenginyje:"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-2.description" = "Atsidarykite „DuckDuckGo“ diegimo programą atsisiuntimų skiltyje, pasirinkite „Įdiegti“ ir įveskite pakvietimo kodą."; + +/* Subtitle for the Windows Waitlist Invite screen */ +"windows-waitlist.invite-screen.subtitle" = "Pasiruošę naudoti „DuckDuckGo“ sistemoje „Windows“?"; + +/* Title for the Windows waitlist button redirecting to Mac waitlist */ +"windows-waitlist.join-waitlist-screen.mac-waitlist" = "Ieškote „Mac“ versijos?"; + +/* Title for the Join Windows Waitlist screen */ +"windows-waitlist.join-waitlist-screen.try-duckduckgo-for-windows" = "Gaukite ankstyvą prieigą ir išbandykite „DuckDuckGo“, skirtą „Windows“!"; + +/* Message for the alert to confirm enabling notifications */ +"windows-waitlist.joined.no-notification.get-notification-confirmation-message" = "Atsiųsime jums pranešimą, kai jūsų „DuckDuckGo“, skirta „Windows“, kopija bus paruošta atsisiųsti. "; + +/* Label text for the Joined Waitlist state with notifications declined */ +"windows-waitlist.joined.notifications-declined" = "Pakvietimą išbandyti „Mac“ skirtą „DuckDuckGo“ matysite čia. Netrukus patikrinkite arba galime jums atsiųsti pranešimą, kai bus jūsų eilė."; + +/* Label text for the Joined Waitlist state with notifications enabled */ +"windows-waitlist.joined.notifications-enabled" = "Atsiųsime jums pranešimą, kai jūsų „DuckDuckGo“, skirta „Windows“, kopija bus paruošta atsisiųsti."; + +/* Title for the settings subtitle */ +"windows-waitlist.settings.browse-privately" = "Naršykite privačiai naudodami mūsų programą, skirtą „Windows“"; + +/* Message used when sharing to iMessage. Parameter is an eight digit invite code. */ +"windows-waitlist.share-sheet.invite-code-message" = "Esate pakviesti!\n\nPasiruošę naudoti „DuckDuckGo“ sistemoje „Windows“?\n\n1 veiksmas\nNorėdami atsisiųsti, „Windows“ įrenginyje apsilankykite šiuo URL adresu:\nhttps://duckduckgo.com/windows\n\n2 veiksmas
Atsidarykite „DuckDuckGo“ diegimo programą atsisiuntimų skiltyje, pasirinkite „Įdiegti“ ir įveskite pakvietimo kodą.

Pakvietimo kodas: %@"; + +/* Message used when sharing to iMessage */ +"windows-waitlist.share-sheet.message" = "Pasiruošę pradėti privačiai naršyti „Windows“?\n\nApsilankykite šiame URL savo kompiuteryje, kad atsisiųstumėte:\nhttps://duckduckgo.com/windows"; + +/* Summary text for the Windows browser waitlist */ +"windows-waitlist.summary" = "„Windows“ skirta „DuckDuckGo“ turi viską, ko reikia norint naršyti dar privačiau – privačią paiešką, stebėjimo priemonių blokavimą, priverstinį šifravimą ir iššokančiųjų slapukų langų blokavimą bei dar daugiau geriausių savo klasėje apsaugos priemonių."; + +/* Title for the Windows Waitlist feature */ +"windows-waitlist.title" = "„DuckDuckGo“ programa, skirta „Windows“"; + +/* Title for the Windows browser download link page */ +"windows-waitlist.waitlist-download-screen.try-duckduckgo-for-windows" = "Gaukite „DuckDuckGo“, skirtą „Windows“!"; + diff --git a/DuckDuckGo/lt.lproj/Settings.strings b/DuckDuckGo/lt.lproj/Settings.strings index d35e9b3a23..c5ccd972b3 100644 --- a/DuckDuckGo/lt.lproj/Settings.strings +++ b/DuckDuckGo/lt.lproj/Settings.strings @@ -37,6 +37,9 @@ /* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ "9Ko-0g-T3h.text" = "Teksto dydis"; +/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ +"9kt-6R-XiZ.text" = "Pavadinimas"; + /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Programos paleidimas"; @@ -73,6 +76,12 @@ /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Visuotinė privatumo kontrolė (VPK)"; +/* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ +"CxT-QK-iVn.title" = "Papildyti"; + +/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ +"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; + /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Išeinant iš programos, po 15 min. neveikimo"; @@ -151,6 +160,9 @@ /* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ "HLr-R8-xxF.text" = "Peržiūros ilgai spaudžiant"; +/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ +"hoT-Nu-KXP.text" = "Naršykite privačiai naudodami mūsų programą, skirtą „Windows“"; + /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Privatumo apsauga įjungta visose svetainėse"; @@ -163,6 +175,12 @@ /* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ "m23-t6-9cb.text" = "Bendrinti atsiliepimą"; +/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ +"mLn-1x-Fl5.title" = "Adreso juostos padėtis"; + +/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ +"mqV-pf-NZ1.text" = "Adreso juostos padėtis"; + /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Leiskite „DuckDuckGo“ valdyti sutikimo su slapukais iššokančiuosius langus"; @@ -175,15 +193,27 @@ /* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ "oM7-1o-9oY.text" = "Apie DuckDuckGo"; +/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ +"opn-JO-idF.text" = "Viršus"; + /* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ "OxE-MQ-uJk.headerTitle" = "Daugiau iš „DuckDuckGo“"; +/* Class = "UILabel"; text = "Sync"; ObjectID = "oXN-ez-gct"; */ +"oXN-ez-gct.text" = "Sinchronizuoti"; + /* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ "P0F-ts-ekd.text" = "Naršykite privačiai su mūsų „Mac“ skirta programa "; +/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ +"qah-gb-udB.text" = "Network Protection"; + /* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ "qeN-SV-zy7.text" = "Etiketė"; +/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ +"RQ8-H1-Ez1.text" = "„DuckDuckGo“ programa „Windows“"; + /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Teksto dydis"; @@ -232,8 +262,8 @@ /* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ "yvj-LL-MiR.text" = "Įtraukti programėlę į stotelę"; -/* Class = "UILabel"; text = "DuckDuckGo Desktop App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "„DuckDuckGo“ darbalaukio programa"; +/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ +"Yz9-17-qnn.text" = "„DuckDuckGo“ programa „Mac“"; /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Nauja kortelė"; diff --git a/DuckDuckGo/lv.lproj/Feedback.strings b/DuckDuckGo/lv.lproj/Feedback.strings index 4cfd3647ff..b497d9cf11 100644 --- a/DuckDuckGo/lv.lproj/Feedback.strings +++ b/DuckDuckGo/lv.lproj/Feedback.strings @@ -28,9 +28,6 @@ /* Class = "UIBarButtonItem"; title = "Back"; ObjectID = "JF8-Ow-zXL"; */ "JF8-Ow-zXL.title" = "Atpakaļ"; -/* Class = "UIBarButtonItem"; title = "Submit"; ObjectID = "jr6-Al-4fr"; */ -"jr6-Al-4fr.title" = "Iesniegt"; - /* Class = "UIButton"; normalTitle = "Submit"; ObjectID = "lsB-Kq-U8p"; */ "lsB-Kq-U8p.normalTitle" = "Iesniegt"; @@ -58,9 +55,6 @@ /* Class = "UINavigationItem"; title = "Report Broken Site"; ObjectID = "WjT-HM-yim"; */ "WjT-HM-yim.title" = "Ziņot par bojātu vietni"; -/* Class = "UILabel"; text = "Broken Site Reason"; ObjectID = "Wx3-Vb-ItS"; */ -"Wx3-Vb-ItS.text" = "Vietnes bojājuma iemesls"; - /* Class = "UINavigationItem"; title = "Share Feedback"; ObjectID = "Xb0-Zu-opM"; */ "Xb0-Zu-opM.title" = "Kopīgot atsauksmi"; diff --git a/DuckDuckGo/lv.lproj/Localizable.strings b/DuckDuckGo/lv.lproj/Localizable.strings index b5f3dbfa2e..02d64bd141 100644 --- a/DuckDuckGo/lv.lproj/Localizable.strings +++ b/DuckDuckGo/lv.lproj/Localizable.strings @@ -1,6 +1,9 @@ /* No comment provided by engineer. */ "%@" = "%@"; +/* No comment provided by engineer. */ +"%@ [%@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)" = "%1$@ [%2$@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)"; + /* Buton label for Edit action */ "action.generic.edit" = "Rediģēt"; @@ -103,6 +106,12 @@ /* Share action */ "action.title.share" = "Kopīgot"; +/* Settings label for bottom position for the address bar */ +"address.bar.bottom" = "Apakšā"; + +/* Settings label for top position for the address bar */ +"address.bar.top" = "Populārākie"; + /* No comment provided by engineer. */ "addWidget.button" = "Pievienot logrīku"; @@ -142,6 +151,225 @@ /* Shown on authentication screen */ "app.authentication.unlock" = "Atbloķēt DuckDuckGo."; +/* First part of about page content (note the trailing space) */ +"appTP.about.content1" = "You’ve probably heard about companies like Google and Facebook tracking you behind the scenes on third-party websites. But did you know they also track your personal information through apps on your device?\n\nIn 2022, DuckDuckGo found that "; + +/* Second part of about page content (note the trailing space) */ +"appTP.about.content2" = "over 85% of free iOS apps tested contained hidden trackers from other companies."; + +/* Third part of about page content (note the leading space) */ +"appTP.about.content3" = " Of the 395 apps tested, 60% sent data to Google. This happens even while you’re not using your device.\n\nTrackers in apps may have access to a lot more information than their website tracker cousins, such as your location down to which floor of a building you're on, how often you play games while at work, and when and how long you sleep each day. Even if you haven’t given apps explicit permission to collect data, they can still take it without your knowledge.\n\nTracking networks like Facebook and Google use these little pieces of information to build a digital profile about you. With it, tracking networks can manipulate what you see online and allow advertisers to bid on access to you based on your data.\n\nTrackers in apps is a BIG problem for privacy. But DuckDuckGo has a solution that can help.\n\nWhen enabled in the DuckDuckGo Privacy Browser app, App Tracking Protection blocks many trackers in other apps, not just the trackers we find on websites when you browse. These dual layers of protection reduce what companies know about you overall, so you can use your apps with more peace of mind, knowing you’re more protected."; + +/* Navigation Title for AppTP about page */ +"appTP.about.navTitle" = "About App Trackers"; + +/* Title for AppTP about page */ +"appTP.about.title" = "What Are App Trackers?"; + +/* Title for 'Report an Issue' button in the activity view. */ +"appTP.activityView.reportIssue" = "Report Issue"; + +/* Text label for switch that turns blocking on or off for a tracker */ +"appTP.blockTrackerText" = "Block this Tracker"; + +/* Detail string describing what AppTP is */ +"appTP.cell.detail" = "Lietotņu izsekotāju bloķēšana ierīcē"; + +/* String indicating AppTP is disabled when viewed from the settings screen */ +"appTP.cell.disabled" = "Atspējota"; + +/* String indicating AppTP is enabled when viewed from the settings screen */ +"appTP.cell.enabled" = "Iespējota"; + +/* Info string informing the user what App Tracking Protection does. */ +"appTP.empty.disabled.info" = "Iespējo lietotņu izsekošanas aizsardzību, lai mēs varētu bloķēt nepatīkamos izsekotājus citās lietotnēs."; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.heading" = "We’re blocking hidden trackers"; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.info" = "Come back soon to see a list of all the app trackers we’ve blocked."; + +/* First answer for AppTP FAQ page */ +"appTP.faq.answer1" = "App Tracking Protection blocks app trackers from other companies, like when Facebook tries to track you in a banking app. Companies may still track you in apps they own."; + +/* Second answer for AppTP FAQ page */ +"appTP.faq.answer2" = "Yes! App Tracking Protection works across all apps on your device to block the most common hidden trackers we find trying to collect your personal info."; + +/* Third answer for AppTP FAQ page */ +"appTP.faq.answer3" = "We currently only block the most common trackers that we find on iOS. This helps us to comprehensively test App Tracking Protection and lower frequency of app breakage, while blocking up to 70% of all tracking requests."; + +/* Fourth answer for AppTP FAQ page */ +"appTP.faq.answer4" = "You’ll be asked to set up a virtual private network (VPN) connection, but you don't need to install a VPN app for App Tracking Protection to work.\n\nThis permission, which works only on your device, allows App Tracking Protection to monitor network traffic so that it can block known trackers."; + +/* Fifth answer for AppTP FAQ page */ +"appTP.faq.answer5" = "You can use App Tracking Protection at the same time as using an IKEv2 protocol VPN app on an iOS device. You won’t be able to use App Tracking Protection on an iOS device if you’re using a VPN app that uses a different type of protocol, like WireGuard or OpenVPN type VPNs."; + +/* Sixth answer for AppTP FAQ page */ +"appTP.faq.answer6" = "A VPN sends your data from the device to its own server, where it secures and anonymizes your data from prying eyes. However, this allows the VPN company to see your network traffic.\n\nApp Tracking Protection is different. Instead of sending your data to a VPN server, App Tracking Protection works only on your device, sitting between your apps and the servers they talk to.\n\nWhenever App Tracking Protection recognizes a known tracker, it blocks the tracker from sending personal information (such as your IP address, activity, and device details) off your device. All other traffic reaches its destination, so your apps work normally."; + +/* Seventh answer for AppTP FAQ page */ +"appTP.faq.answer7" = "App Tracking Protection works only on your device and doesn’t send your data off your device to DuckDuckGo. We don’t collect or store any data from your apps."; + +/* First question for AppTP FAQ page */ +"appTP.faq.question1" = "How does App Tracking Protection work?"; + +/* Second question for AppTP FAQ page */ +"appTP.faq.question2" = "Does App Tracking Protection block trackers in all apps on my device?"; + +/* Third question for AppTP FAQ page */ +"appTP.faq.question3" = "Does App Tracking Protection block all app trackers?"; + +/* Fourth question for AppTP FAQ page */ +"appTP.faq.question4" = "Why does App Tracking Protection use a VPN connection?"; + +/* Fifth question for AppTP FAQ page */ +"appTP.faq.question5" = "Will App Tracking Protection work if I also use a VPN app?"; + +/* Sixth question for AppTP FAQ page */ +"appTP.faq.question6" = "How is App Tracking Protection different from a VPN?"; + +/* Seventh question for AppTP FAQ page */ +"appTP.faq.question7" = "Is my data private?"; + +/* Title for AppTP FAQ page */ +"appTP.faq.title" = "App Tracking Protection FAQ"; + +/* Do not translate. StringsDict entry -- Count part of string 'App Tracking Protection blocked x tracking attempts today' */ +"appTP.home.blockedCount" = "appTP.home.blockedCount"; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the trailing space) */ +"appTP.home.blockedPrefix" = "Lietotņu pretizsekošanas aizsardzība bloķējusi "; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the leading space) */ +"appTP.home.blockedSuffix" = " tavās lietotnēs šodien."; + +/* Prefix of string 'App Tracking Protection disabled. Tap to re-enable.' (note the trailing space) */ +"appTP.home.disabledPrefix" = "App Tracking Protection disabled. "; + +/* Suffix of string 'App Tracking Protection disabled. Tap to re-enable.' */ +"appTP.home.disabledSuffix" = "Tap to continue blocking tracking attempts across your apps."; + +/* Text indicating the tracking event occured 'just now'. Example: Last attempt 'just now' */ +"appTP.justNow" = "Tikko"; + +/* View to manage trackers for AppTP. Allows the user to turn trackers on or off. */ +"appTP.manageTrackers" = "Pārvaldīt izsekotājus"; + +/* Button title for AppTP onboarding */ +"appTP.onboarding.continueButton" = "Continue"; + +/* Button title for AppTP onboarding to enable AppTP */ +"appTP.onboarding.enableeButton" = "Enable App Tracking Protection"; + +/* Button title for AppTP onboarding to learn more about AppTP */ +"appTP.onboarding.learnMoreButton" = "Learn More"; + +/* First part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info1" = "Over 85% of free iOS apps"; + +/* Second part of info on the first AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page1Info2" = " we’ve tested allow other companies to track your personal information, even when you’re sleeping."; + +/* Third part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info3" = "See who we catch trying to track you in your apps and take back control."; + +/* First part of info on the second AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page2Info1" = "App Tracking Protection "; + +/* Second part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info2" = "detects and blocks app trackers from other companies,"; + +/* Third part of info on the second AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page2Info3" = " like when Google attempts to track you in a health app."; + +/* Fourth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info4" = "It’s free,"; + +/* Fifth part of info on the second AppTP onboarding page (note the leading and trailing space) */ +"appTP.onboarding.page2Info5" = " and you can enjoy your apps as you normally would. Working in the background, it helps "; + +/* Sixth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info6" = "protect you night and day."; + +/* First part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info1" = "App Tracking Protection is not a VPN."; + +/* Second part of info on the third AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page3Info2" = " However, your device will recognize it as one. This is because it uses a local VPN connection to work."; + +/* Third part of info on the third AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page3Info3" = "App Tracking Protection is different. "; + +/* Fourth part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info4" = "It never routes app data through an external server."; + +/* Title for first AppTP onboarding page */ +"appTP.onboarding.title1" = "One easy step for better app privacy!"; + +/* Title for second AppTP onboarding page */ +"appTP.onboarding.title2" = "How does it work?"; + +/* Title for third AppTP onboarding page */ +"appTP.onboarding.title3" = "Who sees your data?"; + +/* Breakage report app name label */ +"appTP.report.appLabel" = "Which app is having issues?"; + +/* Breakage report app name placeholder */ +"appTP.report.appPlaceholder" = "App name"; + +/* Breakage report category label */ +"appTP.report.categoryLabel" = "Kas notiek?"; + +/* Breakage report comment label */ +"appTP.report.commentLabel" = "Comments"; + +/* Breakage report comment placeholder */ +"appTP.report.commentPlaceholder" = "Add additional details"; + +/* Breakage report footer explaining what is collected in the breakage report */ +"appTP.report.footer" = "In addition to the details entered into this form, your app issue report will contain:\n• A list of trackers blocked in the last 10 min\n• Whether App Tracking Protection is enabled\n• Aggregate DuckDuckGo app diagnostics"; + +/* Breakage report submit button */ +"appTP.report.submit" = "Iesniegt"; + +/* Breakage report form title */ +"appTP.report.title" = "Report Issue"; + +/* Breakage report succcess message */ +"appTP.report.toast" = "Paldies! Atsauksme ir iesniegta."; + +/* Cancel button for 'Report an Issue' alert. */ +"appTP.reportAlert.cancel" = "Ne tagad"; + +/* Confirm button for 'Report an Issue' alert. */ +"appTP.reportAlert.confirm" = "Ziņot par problēmu"; + +/* Message for 'Report an Issue' alert. */ +"appTP.reportAlert.message" = "Let us know if you disabled App Tracking Protection for this specific tracker because it caused app issues. Your feedback helps us improve!"; + +/* Title for 'Report an Issue' alert. */ +"appTP.reportAlert.title" = "Report Issue?"; + +/* Toast notification diplayed after restoring the blocklist to default settings */ +"appTP.restoreDefaultsToast" = "Default settings restored"; + +/* Button to restore the blocklist to its default state. */ +"appTP.restoreDefualts" = "Atjaunot noklusējuma iestatījumus"; + +/* Title for the App Tracking Protection feature */ +"appTP.title" = "Lietotņu pretizsekošanas aizsardzība"; + +/* Text indicating when the tracker was last allowed. Example: Last attempt allowed (timeString) */ +"appTP.trackerAllowedTimestamp" = "Pēdējais atļautais mēģinājums %@"; + +/* Text indicating when the tracker was last blocked. Example: Last attempt blocked (timeString) */ +"appTP.trackerBlockedTimestamp" = "Pēdējais bloķētais mēģinājums % @"; + +/* Do not translate. StringsDict entry -- Subtitle for tracking attempts in App Tracking Protection Activity View. Example: (count) tracking attempts */ +"appTP.trackingattempts" = "appTP.trackingattempts"; + /* Authentication Alert Sign In Button */ "auth.alert.login.button" = "Pierakstīties"; @@ -301,6 +529,18 @@ /* Toast message when a login item without a title is deleted */ "autofill.logins.list.login-deleted-message-no-title" = "Pieteikšanās dati izdzēsti"; +/* Title for a button that allows a user to reset their list of never saved sites */ +"autofill.logins.list.never.saved" = "Atiestatīt izslēgtās vietnes"; + +/* Cancel button for resetting list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.cancel" = "Atcelt"; + +/* Confirm button to reset list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.confirm" = "Atiestatīt izslēgtās vietnes"; + +/* Alert title */ +"autofill.logins.list.never.saved.reset.action.title" = "Ja atiestatīsi izslēgtās vietnes, nākamreiz pierakstoties kādā no šīm vietnēm, tev tiks piedāvāts saglabāt savus pieteikšanās datus."; + /* Placeholder for search field on autofill login listing */ "autofill.logins.list.search-placeholder" = "Meklēt pieteikšanās datus"; @@ -335,7 +575,7 @@ "autofill.logins.prompt.password.button.title" = "%@ parole"; /* Title for autofill login prompt */ -"autofill.logins.prompt.title" = "Vai izmantot saglabātos pieteikšanās datus?"; +"autofill.logins.prompt.title" = "Izmantot saglabāto paroli?"; /* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ "autofill.logins.search.no-results.subtitle" = "meklējumam \"%@\""; @@ -385,27 +625,24 @@ /* Title for the alert dialog telling the user an updated username is no longer a private email address */ "autofill.removed.duck.address.title" = "Privātās Duck adreses lietotājvārds tika noņemts"; +/* CTA displayed on modal asking if the user never wants to be prompted to save a login for this website agin */ +"autofill.save-login.never-prompt.CTA" = "Nekad nejautāt par šo vietni"; + /* Message displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.message" = "Pieteikšanās dati tiek droši saglabāti tavā ierīcē, izvēlnē Pieteikšanās dati."; +"autofill.save-login.new-user.message" = "Paroles tiek droši saglabātas tavā ierīcē, izvēlnē Pieteikšanās dati."; /* Title displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.title" = "Vai vēlies, lai DuckDuckGo saglabātu tavus pieteikšanās datus?"; +"autofill.save-login.new-user.title" = "Vai vēlies, lai DuckDuckGo saglabātu tavu paroli?"; /* Cancel CTA displayed on modal asking for the user to save the login */ "autofill.save-login.not-now.CTA" = "Nesaglabāt"; -/* Confirm CTA displayed on modal asking for the user to save the login */ -"autofill.save-login.save.CTA" = "Saglabāt pieteikšanās datus"; - /* Title displayed on modal asking for the user to save the login */ "autofill.save-login.title" = "Saglabāt pieteikšanās datus?"; /* Confirm CTA displayed on modal asking for the user to save the password */ "autofill.save-password.save.CTA" = "Saglabāt paroli"; -/* Title displayed on modal asking for the user to save the password */ -"autofill.save-password.title" = "Saglabāt paroli?"; - /* Accessibility title for a Show Password button displaying actial password instead of ***** */ "autofill.show-password" = "Rādīt paroli"; @@ -413,7 +650,7 @@ "autofill.signin.to.manage" = "%@, lai pārvaldītu savas Duck adreses šajā ierīcē."; /* Message displayed on modal asking for the user to update the password */ -"autofill.update-password.message" = "DuckDuckGo atjauninās šos saglabātos pieteikšanās datus tavā ierīcē."; +"autofill.update-password.message" = "DuckDuckGo atjauninās tavā ierīcē saglabāto paroli."; /* Confirm CTA displayed on modal asking for the user to update the password */ "autofill.update-password.save.CTA" = "Atjaunināt paroli"; @@ -442,6 +679,15 @@ /* Placeholder in the add bookmark form */ "bookmark.address.placeholder" = "www.piemērs.com"; +/* Delete bookmark alert message */ +"bookmark.delete.alert.message" = "Tādējādi tiks izdzēsta tava \"%@\" grāmatzīme"; + +/* Delete bookmark alert title */ +"bookmark.delete.alert.title" = "Delete?"; + +/* The message shown after a bookmark has been deleted */ +"bookmark.deleted.toast" = "Grāmatzīme izdzēsta"; + /* Delete bookmark folder alert delete button */ "bookmark.deleteFolderAlert.deleteButton" = "Dzēst"; @@ -538,9 +784,21 @@ /* Broken Site Category */ "brokensite.category.videos" = "Video netika demonstrēts"; +/* Broken Site Category Placeholder */ +"brokensite.categoryPlaceholder" = "Norādi problēmu sarakstā..."; + +/* Broken Site Category Section Title */ +"brokensite.categoryTitle" = "ATLASĪT KATEGORIJU"; + +/* Broken Site Comment Placeholder */ +"brokensite.commentPlaceholder" = "Sīkākas informācijas sniegšana var mums palīdzēt atrisināt šo problēmu"; + /* Broken Site Section Title */ "brokensite.sectionTitle" = "APRAKSTI NOTIKUŠO"; +/* No comment provided by engineer. */ +"bucket: %@" = "bucket: %@"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Iepriekšējais mēnesis"; @@ -772,6 +1030,39 @@ /* Empty search placeholder on bookmarks search */ "empty.search" = "Sakritības nav atrastas"; +/* No comment provided by engineer. */ +"Error" = "Kļūda"; + +/* Button title to Sign In */ +"error.email-protection-sign-in.action" = "Pierakstīties"; + +/* Alert message */ +"error.email-protection-sign-in.body" = "Lūdzu, pieraksties vēlreiz, lai šajā pārlūkprogrammā no jauna iespējotu Email Protection funkcijas."; + +/* Alert title */ +"error.email-protection-sign-in.title" = "Email Protection kļūda"; + +/* Button title to open device settings */ +"error.insufficient-disk-space.action" = "Atvērt iestatījumus"; + +/* Alert message */ +"error.insufficient-disk-space.body" = "Šķiet, ka ierīcē ir beigusies brīvā vieta. Lūdzu, atbrīvo vietu, lai turpinātu."; + +/* Alert title */ +"error.insufficient-disk-space.title" = "Nepietiek vietas"; + +/* Button title that is shutting down the app */ +"error.preemptive-crash.action" = "Aizvērt lietotni"; + +/* Alert message */ +"error.preemptive-crash.body" = "Izskatās, ka ar lietotni ir problēmas un tā ir jāaizver. Lūdzu, atver no jauna, lai turpinātu."; + +/* Alert title */ +"error.preemptive-crash.title" = "Konstatēta lietotnes problēma"; + +/* Generic error message on a dialog for when the cause is not known. */ +"error.unknown.try.again" = "An unknown error has occurred"; + /* No comment provided by engineer. */ "favorite" = "Izlase"; @@ -991,6 +1282,18 @@ /* Home tab title */ "homeTab.title" = "Sākums"; +/* OK title for invite screen alert dismissal button */ +"invite.alert.ok.button" = "OK"; + +/* Continue button on an invite dialog */ +"invite.dialog.continue.button" = "Continue"; + +/* Get Started button on an invite dialog */ +"invite.dialog.get.started.button" = "Get Started"; + +/* Message to show after user enters an unrecognized invite code */ +"invite.dialog.unrecognized.code.message" = "We didn’t recognize this Invite Code."; + /* No comment provided by engineer. */ "keyCommandAddBookmark" = "Pievienot grāmatzīmi"; @@ -1057,15 +1360,15 @@ /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "DuckDuckGo Mac datoram sniedz nepieciešamo ātrumu, ierastās pārlūkošanas funkcijas un savā klasē labākās privātuma pamatfunkcijas."; -/* Body text for the macOS waitlist notification */ -"mac-waitlist.available.notification.body" = "Atver savu ielūgumu"; - /* Title for the macOS waitlist notification */ "mac-waitlist.available.notification.title" = "DuckDuckGo Mac datoram ir gatavs!"; /* Title for the copy action */ "mac-waitlist.copy" = "Kopēt"; +/* Description text above the Share Link button */ +"mac-waitlist.join-waitlist-screen.on-your-computer-go-to" = "Savā Windows datorā dodies uz:"; + /* Description text above the Share Link button */ "mac-waitlist.join-waitlist-screen.on-your-mac-go-to" = "Savā Mac datorā dodies uz:"; @@ -1078,6 +1381,9 @@ /* Disclaimer for the Join Waitlist screen */ "mac-waitlist.join-waitlist-screen.windows" = "Windows drīzumā!"; +/* Title for the macOS waitlist button redirecting to Windows waitlist */ +"mac-waitlist.join-waitlist-screen.windows-waitlist" = "Vai meklē Windows versiju?"; + /* Title for the settings subtitle */ "mac-waitlist.settings.browse-privately" = "Pārlūko privāti, izmantojot mūsu Mac lietotni"; @@ -1088,7 +1394,7 @@ "mac-waitlist.share-sheet.title" = "DuckDuckGo Mac datoram"; /* Title for the Mac Waitlist feature */ -"mac-waitlist.title" = "DuckDuckGo darbvirsmas lietotne"; +"mac-waitlist.title" = "DuckDuckGo lietotne Mac datoram"; /* No comment provided by engineer. */ "menu.button.hint" = "Pārlūkošanas izvēlne"; @@ -1105,9 +1411,81 @@ /* Edit button */ "navigation.title.edit" = "Rediģēt"; +/* String indicating NetP is connected when viewed from the settings screen */ +"netP.cell.connected" = "Connected"; + +/* String indicating NetP is disconnected when viewed from the settings screen */ +"netP.cell.disconnected" = "Not connected"; + +/* Title for the Network Protection feature */ +"netP.title" = "Network Protection"; + +/* Message for the network protection invite dialog */ +"network.protection.invite.dialog.message" = "Enter your invite code to get started."; + +/* Title for the network protection invite screen */ +"network.protection.invite.dialog.title" = "You're invited to try Network Protection"; + +/* Prompt for the network protection invite code text field */ +"network.protection.invite.field.prompt" = "Invite Code"; + +/* Message explaining that netP is invite only */ +"network.protection.invite.only.message" = "DuckDuckGo Network Protection is currently invite-only."; + +/* Message for the network protection invite success view */ +"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; + +/* Title for the network protection invite success view */ +"network.protection.invite.success.title" = "Success! You’re in."; + +/* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ +"network.protection.status.connected.format" = "Connected - %@"; + +/* The label for the NetP VPN when connecting */ +"network.protection.status.connecting" = "Connecting..."; + +/* The label for the NetP VPN when disconnected */ +"network.protection.status.disconnected" = "Not connected"; + +/* The label for the NetP VPN when disconnecting */ +"network.protection.status.disconnecting" = "Disconnecting..."; + +/* Message label text for the netP status view */ +"network.protection.status.header.message" = "DuckDuckGo's VPN secures all of your device's Internet traffic anytime, anywhere."; + +/* Header title label text for the status view when netP is disconnected */ +"network.protection.status.header.title.off" = "Network Protection is Off"; + +/* Header title label text for the status view when netP is connected */ +"network.protection.status.header.title.on" = "Network Protection is On"; + +/* The status view 'Share Feedback' button which is shown inline on the status view after the \(netPInviteOnlyMessage) text */ +"network.protection.status.menu.share.feedback" = "Share Feedback"; + +/* Connection details label shown in NetworkProtection's status view. */ +"network.protection.status.view.connection.details" = "Connection Details"; + +/* Generic connection failed error message shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.message" = "Please try again later."; + +/* Generic connection failed error title shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; + +/* IP Address label shown in NetworkProtection's status view. */ +"network.protection.status.view.ip.address" = "IP Address"; + +/* Location label shown in NetworkProtection's status view. */ +"network.protection.status.view.location" = "Location"; + +/* Title label text for the status view when netP is disconnected */ +"network.protection.status.view.title" = "Network Protection"; + /* Do not translate - stringsdict entry */ "number.of.tabs" = "number.of.tabs"; +/* No comment provided by engineer. */ +"Ok" = "Labi"; + /* Text displayed on notification appearing in the address bar when the browser dismissed the cookie popup automatically rejecting it */ "omnibar.notification.cookies-managed" = "Pārvaldītie sīkfaili"; @@ -1147,6 +1525,18 @@ /* ’Link’ is link on a website */ "open.externally.failed" = "Nevar atrast lietotni, kas nepieciešama šīs saites atvēršanai"; +/* No comment provided by engineer. */ +"Please enter which app on your device is broken." = "Lūdzu, norādi, kurai lietotnei tavā ierīcē ir traucējumi."; + +/* Activate button */ +"pm.activate" = "Reactivate"; + +/* Cancel button */ +"pm.cancel" = "Cancel"; + +/* Deactivate button */ +"pm.deactivate" = "Deactivate"; + /* No comment provided by engineer. */ "preserveLogins.domain.list.footer" = "Vietnes lieto sīkfailus, lai tu varētu turpināt būt pierakstījies(-usies). Padarot vietni ugunsdrošu, sīkfaili netiek dzēsti, bet tu vari turpināt būt pierakstījies(-usies) arī pēc Uguns pogas lietošanas. Mēs joprojām bloķēsim trešo pušu izsekotājus, kas atrodami ugunsdrošās vietnēs."; @@ -1216,8 +1606,8 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Izlase"; -/* No comment provided by engineer. */ -"settings.about.text" = "DuckDuckGo mērķis ir veidot jaunu tiešsaistes uzticēšanās standartu.\n\nDuckDuckGo konfidencialitātes pārlūks nodrošina visu nepieciešamo privātumu, lai aizsargātu tevi, meklējot un pārlūkojot informāciju tīmeklī, ieskaitot izsekotāju bloķēšanu, viedāku šifrēšanu un DuckDuckGo privāto meklēšanu.\n\nGalu galā – internetam nevajadzētu iedvest bailes, un tava godam nopelnītā tiešsaistes privātuma iegūšanai vajadzētu būt tikpat vienkāršai kā žalūziju aizvēršanai."; +/* about page */ +"settings.about.text" = "DuckDuckGo ir neatkarīgs 2008. gadā dibināts interneta privātuma uzņēmums ikvienam, kam ir apnikusi izsekošana tiešsaistē un kas vēlas vienkāršu risinājumu. Mēs esam pierādījums tam, ka tiešsaistē var iegūt reālu privātuma aizsardzību bez kompromisiem.\n\nDuckDuckGo pārlūks sniedz visas iespējas, ko gaidi no pārlūkprogrammas, piemēram, grāmatzīmes, cilnes, paroles un citas, kā arī vairāk nekā [duci spēcīgu privātuma aizsardzības funkciju](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/) pēc noklusējuma netiek piedāvāts lielākajā daļā populārāko pārlūkprogrammu. Šis unikāli visaptverošais privātuma aizsardzības komplekts palīdz aizsargāt tavas darbības tiešsaistē, sākot no meklēšanas līdz pārlūkošanai, e-pasta sūtīšanai u.c.\n\nMūsu privātuma aizsardzība darbojas vienmēr – tev nav jāpārzina sarežģīti tehniskie aspekti vai iestatījumi. Tev vienkārši jālieto DuckDuckGo pārlūks visās savās ierīcēs, un privātums būs tavs standarta risinājums.\n\nBet, ja vēlies paskatīties, kas lācītim vēderā, vairāk informācijas par to, kā darbojas DuckDuckGo privātuma aizsardzība, vari atrast mūsu [palīdzības lapās](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/)."; /* Report a Broken Site screen confirmation button */ "siteFeedback.buttonText" = "Iesniegt ziņojumu"; @@ -1237,6 +1627,9 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Kura vietne ir bojāta?"; +/* No comment provided by engineer. */ +"sync.remove-device.message" = "\"%@\" vairs nevarēs piekļūt taviem sinhronizētajiem datiem."; + /* Accessibility label on remove button */ "tab.close.home" = "Aizvērt sākuma cilni"; @@ -1309,6 +1702,57 @@ /* Voice-search footer note with on-device privacy warning */ "voiceSearch.footer.note" = "Audio tiek apstrādāts ierīcē. Tas netiek glabāts, un tam nevar piekļūt neviens, pat ne DuckDuckGo."; +/* Title for the button to enable push notifications in system settings */ +"waitlist.allow-notifications" = "Atļaut paziņojumus"; + +/* Body text for the waitlist notification */ +"waitlist.available.notification.body" = "Atver savu ielūgumu"; + +/* Title for the copy action */ +"waitlist.copy" = "Kopēt"; + +/* Label text for the invite code */ +"waitlist.invite-code" = "Uzaicinājuma kods"; + +/* Step title on the invite screen */ +"waitlist.invite-screen.step.title" = "%d. solis"; + +/* Title for the invite code screen */ +"waitlist.invite-screen.youre-invited" = "Tu esi uzaicināts!"; + +/* Title for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.join" = "Pievienojies privātajam nogaides sarakstam"; + +/* Temporary status text for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.joining" = "Notiek pievienošanās gaidīšanas sarakstam..."; + +/* Title for the Share Link button */ +"waitlist.join-waitlist-screen.share-link" = "Kopīgot saiti"; + +/* Notification text for the waitlist */ +"waitlist.joined.no-notification.get-notification" = "INFORMĒT MANI"; + +/* Title for the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.get-notification-confirmation-title" = "Vai saņemt paziņojumu, kad pienāks tava kārta?"; + +/* Cancel button in the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.no-thanks" = "Nē, paldies"; + +/* Text used for the Notifications Disabled state */ +"waitlist.notification.disabled" = "Mēs varam tev paziņot, kad būs pienākusi tava kārta, taču pašlaik DuckDuckGo paziņojumi ir atspējoti."; + +/* Privacy disclaimer for the Waitlist feature */ +"waitlist.privacy-disclaimer" = "Lai pievienotos gaidīšanas sarakstam, tev nav jādalās ar personisku informāciju. Tu ieņemsi vietu rindā, izmantojot laikspiedolu, kas pastāv tikai tavā ierīcē, lai mēs varētu tevi informēt, kad pienāks tava kārta."; + +/* Title for the queue screen */ +"waitlist.queue-screen.on-the-list" = "Tu esi sarakstā!"; + +/* Title for the settings subtitle */ +"waitlist.settings.download-available" = "Pieejama lejupielāde"; + +/* Title for the share sheet entry */ +"waitlist.share-sheet.title" = "Tu esi uzaicināts!"; + /* Confirmation message */ "web.url.remove.favorite.done" = "Izlase ir noņemta"; @@ -1333,3 +1777,51 @@ /* Alert title explaining the message is shown by a website */ "webJSAlert.website-message.format" = "Ziņojums no %@:"; +/* No comment provided by engineer. */ +"Welcome to the Duck Side!" = "Sveicināti Duck pusē!"; + +/* Title for the Windows waitlist notification */ +"windows-waitlist.available.notification.title" = "Iegūsti DuckDuckGo operētājsistēmai Windows!"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-1.description" = "Apmeklē šo URL savā Windows ierīcē, lai lejupielādētu:"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-2.description" = "Atver DuckDuckGo instalētāju lejupielāžu sadaļā, pēc tam ievadi uzaicinājuma kodu."; + +/* Subtitle for the Windows Waitlist Invite screen */ +"windows-waitlist.invite-screen.subtitle" = "Vai esat gatavs lietot DuckDuckGo operētājsistēmā Windows?"; + +/* Title for the Windows waitlist button redirecting to Mac waitlist */ +"windows-waitlist.join-waitlist-screen.mac-waitlist" = "Vai meklē Mac versiju?"; + +/* Title for the Join Windows Waitlist screen */ +"windows-waitlist.join-waitlist-screen.try-duckduckgo-for-windows" = "Iegūsti agrīnu piekļuvi, lai izmēģinātu DuckDuckGo operētājsistēmai Windows!"; + +/* Message for the alert to confirm enabling notifications */ +"windows-waitlist.joined.no-notification.get-notification-confirmation-message" = "Mēs nosūtīsim tev paziņojumu, kad tavs DuckDuckGo Mac datoram būs gatavs lejupielādei. "; + +/* Label text for the Joined Waitlist state with notifications declined */ +"windows-waitlist.joined.notifications-declined" = "Tavs uzaicinājums izmēģināt DuckDuckGo Mac datoram parādīsies šeit. Drīzumā pārbaudi vēlreiz, vai arī mēs varam tev nosūtīt paziņojumu, kad pienāks tava kārta."; + +/* Label text for the Joined Waitlist state with notifications enabled */ +"windows-waitlist.joined.notifications-enabled" = "Mēs nosūtīsim tev paziņojumu, kad tavs DuckDuckGo operētājsistēmai Windows būs gatavs lejupielādei."; + +/* Title for the settings subtitle */ +"windows-waitlist.settings.browse-privately" = "Pārlūko privāti ar mūsu Windows lietotni"; + +/* Message used when sharing to iMessage. Parameter is an eight digit invite code. */ +"windows-waitlist.share-sheet.invite-code-message" = "Tu esi uzaicināts!\n\nVai esi gatavs lietot DuckDuckGo operētājsistēmā Windows?\n\n1. solis\nApmeklē šo URL savā Windows ierīcē, lai lejupielādētu:\nhttps://duckduckgo.com/windows\n\n2. solis\nAtver DuckDuckGo instalētāju lejupielāžu sadaļā, izvēlies Instalēt un ievadi uzaicinājuma kodu.\n\nUzaicinājuma kods: %@"; + +/* Message used when sharing to iMessage */ +"windows-waitlist.share-sheet.message" = "Vai esi gatavs sākt privātu pārlūkošanu operētājsistēmā Windows?\n\nApmeklē šo URL savā datorā, lai lejupielādētu:\nhttps://duckduckgo.com/windows"; + +/* Summary text for the Windows browser waitlist */ +"windows-waitlist.summary" = "DuckDuckGo operētājsistēmai Windows sniedz visu, kas nepieciešams, lai pārlūkotu ar lielāku konfidencialitāti – privātu meklēšanu, izsekošanas bloķēšanu, piespiedu šifrēšanu un sīkfailu uznirstošo logu bloķēšanu, kā arī citas savā klasē labākās aizsardzības iespējas."; + +/* Title for the Windows Waitlist feature */ +"windows-waitlist.title" = "DuckDuckGo lietotne operētājsistēmai Windows"; + +/* Title for the Windows browser download link page */ +"windows-waitlist.waitlist-download-screen.try-duckduckgo-for-windows" = "Iegūsti DuckDuckGo operētājsistēmai Windows!"; + diff --git a/DuckDuckGo/lv.lproj/Settings.strings b/DuckDuckGo/lv.lproj/Settings.strings index 7f5e71f7bb..1cb5ae932d 100644 --- a/DuckDuckGo/lv.lproj/Settings.strings +++ b/DuckDuckGo/lv.lproj/Settings.strings @@ -37,6 +37,9 @@ /* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ "9Ko-0g-T3h.text" = "Burtu izmērs"; +/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ +"9kt-6R-XiZ.text" = "Nosaukums"; + /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Lietotnes palaišana"; @@ -73,6 +76,12 @@ /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Globālā privātuma kontrole (GPC)"; +/* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ +"CxT-QK-iVn.title" = "Pievienot"; + +/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ +"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; + /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Iziešana no lietotnes, neaktīva 15 minūtes"; @@ -151,6 +160,9 @@ /* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ "HLr-R8-xxF.text" = "Ilgas nospiešanas rezultātu priekšskatījumi"; +/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ +"hoT-Nu-KXP.text" = "Pārlūko privāti ar mūsu Windows lietotni"; + /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Privātuma aizsardzība ir iespējota visām vietnēm"; @@ -163,6 +175,12 @@ /* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ "m23-t6-9cb.text" = "Kopīgot atsauksmes"; +/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ +"mLn-1x-Fl5.title" = "Adreses joslas pozīcija"; + +/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ +"mqV-pf-NZ1.text" = "Adreses joslas pozīcija"; + /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Ļaut DuckDuckGo pārvaldīt sīkfailu piekrišanas uznirstošos logus"; @@ -175,15 +193,27 @@ /* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ "oM7-1o-9oY.text" = "Par DuckDuckGo"; +/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ +"opn-JO-idF.text" = "Populārākie"; + /* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ "OxE-MQ-uJk.headerTitle" = "Vairāk no DuckDuckGo"; +/* Class = "UILabel"; text = "Sync"; ObjectID = "oXN-ez-gct"; */ +"oXN-ez-gct.text" = "Sinhronizēt"; + /* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ "P0F-ts-ekd.text" = "Pārlūko privāti, izmantojot mūsu Mac lietotni "; +/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ +"qah-gb-udB.text" = "Network Protection"; + /* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ "qeN-SV-zy7.text" = "Etiķete"; +/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ +"RQ8-H1-Ez1.text" = "DuckDuckGo Windows lietotne"; + /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Burtu izmērs"; @@ -232,8 +262,8 @@ /* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ "yvj-LL-MiR.text" = "Pievienot lietotni dokam"; -/* Class = "UILabel"; text = "DuckDuckGo Desktop App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "DuckDuckGo darbvirsmas lietotne"; +/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ +"Yz9-17-qnn.text" = "DuckDuckGo Mac lietotne"; /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Jauna cilne"; diff --git a/DuckDuckGo/menu_dark.json b/DuckDuckGo/menu_dark.json index 0f9ef9e598..1f974cc489 100644 --- a/DuckDuckGo/menu_dark.json +++ b/DuckDuckGo/menu_dark.json @@ -1 +1 @@ -{"v":"5.7.6","fr":60,"ip":0,"op":21,"w":28,"h":28,"nm":"v1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"_ctrl-MASTER","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"t":20,"s":[45]}],"ix":10},"p":{"a":0,"k":[14.000000000000002,14.000000000000002,0],"ix":2,"l":2},"a":{"a":0,"k":[50,50,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":240,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"_ctrl-round-rect","parent":1,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2,"l":2},"a":{"a":0,"k":[50,50,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"t":10,"s":[48,50,100]}],"ix":6,"l":2}},"ao":0,"ip":0,"op":11,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"round-rect (main)","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[58.165,50,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":0,"s":[4.7,4.667]},{"t":10,"s":[10,4.667]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":3,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.8,0.8,0.8,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"round-rect","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"round-rect","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[4.67,4.667],"ix":2,"x":"var $bm_rt;\n$bm_rt = thisComp.layer('round-rect (main)').content('round-rect').content('Rectangle Path 1').size;"},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":3,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.8,0.8,0.8,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"round-rect","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":16,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"round-rect","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[41.835,50,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[4.67,4.667],"ix":2,"x":"var $bm_rt;\n$bm_rt = thisComp.layer('round-rect (main)').content('round-rect').content('Rectangle Path 1').size;"},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":3,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.8,0.8,0.8,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"round-rect","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":16,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":3,"nm":"_ctrl-rect","parent":1,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":-45,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2,"l":2},"a":{"a":0,"k":[50,50,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":240,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Rectangle 2","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":45,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":5,"s":[2.4,2.4]},{"t":20,"s":[20.8,2.4]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":2,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.8,0.8,0.8,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":-89.98,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":5,"op":240,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Rectangle 1","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":45,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":11,"s":[12.4,2.4]},{"t":20,"s":[20.8,2.4]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":2,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.8,0.8,0.8,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":11,"op":240,"st":0,"bm":0}],"markers":[]} \ No newline at end of file +{"v":"5.9.0","fr":60,"ip":0,"op":31,"w":24,"h":24,"nm":"menu_dark","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"_ctrl-MASTER","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[12,12,0],"ix":2,"l":2},"a":{"a":0,"k":[50,50,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":240,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"_ctrl-round-rect","parent":1,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2,"l":2},"a":{"a":0,"k":[50,50,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":8,"s":[100,100,100]},{"t":15,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"ip":0,"op":240,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"round-rect","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[58.165,50,0],"to":[-0.496,0,0],"ti":[1.187,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":8,"s":[59.192,50,0],"to":[-2.066,0,0],"ti":[0.864,0,0]},{"t":15,"s":[50,50,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[4.7,4.667],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":3,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.8,0.8,0.8,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"round-rect","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":16,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"round-rect (center)","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[4.67,4.667],"ix":2,"x":"var $bm_rt;\n$bm_rt = thisComp.layer('round-rect').content('round-rect').content('Rectangle Path 1').size;"},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":3,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.8,0.8,0.8,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"round-rect","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":16,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"round-rect","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[41.835,50,0],"to":[0.496,0,0],"ti":[-1.186,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":8,"s":[40.807,50,0],"to":[2.066,0,0],"ti":[-0.864,0,0]},{"t":15,"s":[50,50,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[4.67,4.667],"ix":2,"x":"var $bm_rt;\n$bm_rt = thisComp.layer('round-rect').content('round-rect').content('Rectangle Path 1').size;"},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":3,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.8,0.8,0.8,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"round-rect","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":16,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":3,"nm":"_ctrl-rect","parent":1,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2,"l":2},"a":{"a":0,"k":[50,50,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":240,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Rectangle 2","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":45,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":15,"s":[2.4,2.4]},{"t":30,"s":[20.8,2.4]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":2,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.8,0.8,0.8,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":-89.98,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":15,"op":240,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Rectangle 1","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":45,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":15,"s":[2.4,2.4]},{"t":30,"s":[20.8,2.4]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":2,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.8,0.8,0.8,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":15,"op":240,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/DuckDuckGo/menu_light.json b/DuckDuckGo/menu_light.json index f556f1ddc1..6a42fecd4b 100644 --- a/DuckDuckGo/menu_light.json +++ b/DuckDuckGo/menu_light.json @@ -1 +1 @@ -{"v":"5.7.6","fr":60,"ip":0,"op":21,"w":28,"h":28,"nm":"v1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"_ctrl-MASTER","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[0]},{"t":20,"s":[45]}],"ix":10},"p":{"a":0,"k":[14,14,0],"ix":2,"l":2},"a":{"a":0,"k":[50,50,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":240,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"_ctrl-round-rect","parent":1,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2,"l":2},"a":{"a":0,"k":[50,50,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"t":10,"s":[48,50,100]}],"ix":6,"l":2}},"ao":0,"ip":0,"op":11,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"round-rect (main)","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[58.165,50,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":0,"s":[4.7,4.667]},{"t":10,"s":[10,4.667]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":3,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.20000000298,0.20000000298,0.20000000298,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"round-rect","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":11,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"round-rect","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[4.67,4.667],"ix":2,"x":"var $bm_rt;\n$bm_rt = thisComp.layer('round-rect (main)').content('round-rect').content('Rectangle Path 1').size;"},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":3,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.20000000298,0.20000000298,0.20000000298,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"round-rect","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":16,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"round-rect","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[41.835,50,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[4.67,4.667],"ix":2,"x":"var $bm_rt;\n$bm_rt = thisComp.layer('round-rect (main)').content('round-rect').content('Rectangle Path 1').size;"},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":3,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.20000000298,0.20000000298,0.20000000298,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"round-rect","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":16,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":3,"nm":"_ctrl-rect","parent":1,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":-45,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2,"l":2},"a":{"a":0,"k":[50,50,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":240,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Rectangle 2","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":45,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":5,"s":[2.4,2.4]},{"t":20,"s":[20.8,2.4]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":2,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.20000000298,0.20000000298,0.20000000298,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":-89.98,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":5,"op":240,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Rectangle 1","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":45,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":11,"s":[12.4,2.4]},{"t":20,"s":[20.8,2.4]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":2,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":11,"op":240,"st":0,"bm":0}],"markers":[]} \ No newline at end of file +{"v":"5.9.0","fr":60,"ip":0,"op":31,"w":24,"h":24,"nm":"menu_light","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":3,"nm":"_ctrl-MASTER","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[12,12,0],"ix":2,"l":2},"a":{"a":0,"k":[50,50,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":240,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":3,"nm":"_ctrl-round-rect","parent":1,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2,"l":2},"a":{"a":0,"k":[50,50,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":8,"s":[100,100,100]},{"t":15,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"ip":0,"op":240,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"round-rect","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[58.165,50,0],"to":[-0.496,0,0],"ti":[1.187,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":8,"s":[59.192,50,0],"to":[-2.066,0,0],"ti":[0.864,0,0]},{"t":15,"s":[50,50,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[4.7,4.667],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":3,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.20000000298,0.20000000298,0.20000000298,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"round-rect","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":16,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"round-rect (center)","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[4.67,4.667],"ix":2,"x":"var $bm_rt;\n$bm_rt = thisComp.layer('round-rect').content('round-rect').content('Rectangle Path 1').size;"},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":3,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.20000000298,0.20000000298,0.20000000298,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"round-rect","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":16,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"round-rect","parent":2,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[41.835,50,0],"to":[0.496,0,0],"ti":[-1.186,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.167,"y":0.167},"t":8,"s":[40.807,50,0],"to":[2.066,0,0],"ti":[-0.864,0,0]},{"t":15,"s":[50,50,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[4.67,4.667],"ix":2,"x":"var $bm_rt;\n$bm_rt = thisComp.layer('round-rect').content('round-rect').content('Rectangle Path 1').size;"},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":3,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.20000000298,0.20000000298,0.20000000298,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"round-rect","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":16,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":3,"nm":"_ctrl-rect","parent":1,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2,"l":2},"a":{"a":0,"k":[50,50,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":240,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"Rectangle 2","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":45,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":15,"s":[2.4,2.4]},{"t":30,"s":[20.8,2.4]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":2,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.20000000298,0.20000000298,0.20000000298,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":-89.98,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":15,"op":240,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"Rectangle 1","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":45,"ix":10},"p":{"a":0,"k":[50,50,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"t":15,"s":[2.4,2.4]},{"t":30,"s":[20.8,2.4]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":2,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.2,0.2,0.2,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":15,"op":240,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/DuckDuckGo/nb.lproj/Feedback.strings b/DuckDuckGo/nb.lproj/Feedback.strings index ee08d1d2bb..20b8629dd4 100644 --- a/DuckDuckGo/nb.lproj/Feedback.strings +++ b/DuckDuckGo/nb.lproj/Feedback.strings @@ -28,9 +28,6 @@ /* Class = "UIBarButtonItem"; title = "Back"; ObjectID = "JF8-Ow-zXL"; */ "JF8-Ow-zXL.title" = "Tilbake"; -/* Class = "UIBarButtonItem"; title = "Submit"; ObjectID = "jr6-Al-4fr"; */ -"jr6-Al-4fr.title" = "Send"; - /* Class = "UIButton"; normalTitle = "Submit"; ObjectID = "lsB-Kq-U8p"; */ "lsB-Kq-U8p.normalTitle" = "Send"; @@ -58,9 +55,6 @@ /* Class = "UINavigationItem"; title = "Report Broken Site"; ObjectID = "WjT-HM-yim"; */ "WjT-HM-yim.title" = "Rapporter nettstedfeil"; -/* Class = "UILabel"; text = "Broken Site Reason"; ObjectID = "Wx3-Vb-ItS"; */ -"Wx3-Vb-ItS.text" = "Årsak til nettstedfeil"; - /* Class = "UINavigationItem"; title = "Share Feedback"; ObjectID = "Xb0-Zu-opM"; */ "Xb0-Zu-opM.title" = "Del tilbakemelding"; diff --git a/DuckDuckGo/nb.lproj/Localizable.strings b/DuckDuckGo/nb.lproj/Localizable.strings index 38521571c3..5ace68b096 100644 --- a/DuckDuckGo/nb.lproj/Localizable.strings +++ b/DuckDuckGo/nb.lproj/Localizable.strings @@ -1,6 +1,9 @@ /* No comment provided by engineer. */ "%@" = "%@"; +/* No comment provided by engineer. */ +"%@ [%@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)" = "%1$@ [%2$@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)"; + /* Buton label for Edit action */ "action.generic.edit" = "Rediger"; @@ -103,6 +106,12 @@ /* Share action */ "action.title.share" = "Del"; +/* Settings label for bottom position for the address bar */ +"address.bar.bottom" = "Nederst"; + +/* Settings label for top position for the address bar */ +"address.bar.top" = "Topp"; + /* No comment provided by engineer. */ "addWidget.button" = "Legg til widget"; @@ -142,6 +151,225 @@ /* Shown on authentication screen */ "app.authentication.unlock" = "Lås opp DuckDuckGo."; +/* First part of about page content (note the trailing space) */ +"appTP.about.content1" = "You’ve probably heard about companies like Google and Facebook tracking you behind the scenes on third-party websites. But did you know they also track your personal information through apps on your device?\n\nIn 2022, DuckDuckGo found that "; + +/* Second part of about page content (note the trailing space) */ +"appTP.about.content2" = "over 85% of free iOS apps tested contained hidden trackers from other companies."; + +/* Third part of about page content (note the leading space) */ +"appTP.about.content3" = " Of the 395 apps tested, 60% sent data to Google. This happens even while you’re not using your device.\n\nTrackers in apps may have access to a lot more information than their website tracker cousins, such as your location down to which floor of a building you're on, how often you play games while at work, and when and how long you sleep each day. Even if you haven’t given apps explicit permission to collect data, they can still take it without your knowledge.\n\nTracking networks like Facebook and Google use these little pieces of information to build a digital profile about you. With it, tracking networks can manipulate what you see online and allow advertisers to bid on access to you based on your data.\n\nTrackers in apps is a BIG problem for privacy. But DuckDuckGo has a solution that can help.\n\nWhen enabled in the DuckDuckGo Privacy Browser app, App Tracking Protection blocks many trackers in other apps, not just the trackers we find on websites when you browse. These dual layers of protection reduce what companies know about you overall, so you can use your apps with more peace of mind, knowing you’re more protected."; + +/* Navigation Title for AppTP about page */ +"appTP.about.navTitle" = "About App Trackers"; + +/* Title for AppTP about page */ +"appTP.about.title" = "What Are App Trackers?"; + +/* Title for 'Report an Issue' button in the activity view. */ +"appTP.activityView.reportIssue" = "Report Issue"; + +/* Text label for switch that turns blocking on or off for a tracker */ +"appTP.blockTrackerText" = "Block this Tracker"; + +/* Detail string describing what AppTP is */ +"appTP.cell.detail" = "Blokker appsporere på enheten din"; + +/* String indicating AppTP is disabled when viewed from the settings screen */ +"appTP.cell.disabled" = "Deaktivert"; + +/* String indicating AppTP is enabled when viewed from the settings screen */ +"appTP.cell.enabled" = "Aktivert"; + +/* Info string informing the user what App Tracking Protection does. */ +"appTP.empty.disabled.info" = "Aktiver App Tracking Protection, slik at vi kan blokkere lumske sporere i andre apper."; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.heading" = "We’re blocking hidden trackers"; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.info" = "Come back soon to see a list of all the app trackers we’ve blocked."; + +/* First answer for AppTP FAQ page */ +"appTP.faq.answer1" = "App Tracking Protection blocks app trackers from other companies, like when Facebook tries to track you in a banking app. Companies may still track you in apps they own."; + +/* Second answer for AppTP FAQ page */ +"appTP.faq.answer2" = "Yes! App Tracking Protection works across all apps on your device to block the most common hidden trackers we find trying to collect your personal info."; + +/* Third answer for AppTP FAQ page */ +"appTP.faq.answer3" = "We currently only block the most common trackers that we find on iOS. This helps us to comprehensively test App Tracking Protection and lower frequency of app breakage, while blocking up to 70% of all tracking requests."; + +/* Fourth answer for AppTP FAQ page */ +"appTP.faq.answer4" = "You’ll be asked to set up a virtual private network (VPN) connection, but you don't need to install a VPN app for App Tracking Protection to work.\n\nThis permission, which works only on your device, allows App Tracking Protection to monitor network traffic so that it can block known trackers."; + +/* Fifth answer for AppTP FAQ page */ +"appTP.faq.answer5" = "You can use App Tracking Protection at the same time as using an IKEv2 protocol VPN app on an iOS device. You won’t be able to use App Tracking Protection on an iOS device if you’re using a VPN app that uses a different type of protocol, like WireGuard or OpenVPN type VPNs."; + +/* Sixth answer for AppTP FAQ page */ +"appTP.faq.answer6" = "A VPN sends your data from the device to its own server, where it secures and anonymizes your data from prying eyes. However, this allows the VPN company to see your network traffic.\n\nApp Tracking Protection is different. Instead of sending your data to a VPN server, App Tracking Protection works only on your device, sitting between your apps and the servers they talk to.\n\nWhenever App Tracking Protection recognizes a known tracker, it blocks the tracker from sending personal information (such as your IP address, activity, and device details) off your device. All other traffic reaches its destination, so your apps work normally."; + +/* Seventh answer for AppTP FAQ page */ +"appTP.faq.answer7" = "App Tracking Protection works only on your device and doesn’t send your data off your device to DuckDuckGo. We don’t collect or store any data from your apps."; + +/* First question for AppTP FAQ page */ +"appTP.faq.question1" = "How does App Tracking Protection work?"; + +/* Second question for AppTP FAQ page */ +"appTP.faq.question2" = "Does App Tracking Protection block trackers in all apps on my device?"; + +/* Third question for AppTP FAQ page */ +"appTP.faq.question3" = "Does App Tracking Protection block all app trackers?"; + +/* Fourth question for AppTP FAQ page */ +"appTP.faq.question4" = "Why does App Tracking Protection use a VPN connection?"; + +/* Fifth question for AppTP FAQ page */ +"appTP.faq.question5" = "Will App Tracking Protection work if I also use a VPN app?"; + +/* Sixth question for AppTP FAQ page */ +"appTP.faq.question6" = "How is App Tracking Protection different from a VPN?"; + +/* Seventh question for AppTP FAQ page */ +"appTP.faq.question7" = "Is my data private?"; + +/* Title for AppTP FAQ page */ +"appTP.faq.title" = "App Tracking Protection FAQ"; + +/* Do not translate. StringsDict entry -- Count part of string 'App Tracking Protection blocked x tracking attempts today' */ +"appTP.home.blockedCount" = "appTP.home.blockedCount"; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the trailing space) */ +"appTP.home.blockedPrefix" = "App Tracking Protection blokkerte "; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the leading space) */ +"appTP.home.blockedSuffix" = " i appene dine i dag."; + +/* Prefix of string 'App Tracking Protection disabled. Tap to re-enable.' (note the trailing space) */ +"appTP.home.disabledPrefix" = "App Tracking Protection disabled. "; + +/* Suffix of string 'App Tracking Protection disabled. Tap to re-enable.' */ +"appTP.home.disabledSuffix" = "Tap to continue blocking tracking attempts across your apps."; + +/* Text indicating the tracking event occured 'just now'. Example: Last attempt 'just now' */ +"appTP.justNow" = "Akkurat nå"; + +/* View to manage trackers for AppTP. Allows the user to turn trackers on or off. */ +"appTP.manageTrackers" = "Administrer sporere"; + +/* Button title for AppTP onboarding */ +"appTP.onboarding.continueButton" = "Continue"; + +/* Button title for AppTP onboarding to enable AppTP */ +"appTP.onboarding.enableeButton" = "Enable App Tracking Protection"; + +/* Button title for AppTP onboarding to learn more about AppTP */ +"appTP.onboarding.learnMoreButton" = "Learn More"; + +/* First part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info1" = "Over 85% of free iOS apps"; + +/* Second part of info on the first AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page1Info2" = " we’ve tested allow other companies to track your personal information, even when you’re sleeping."; + +/* Third part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info3" = "See who we catch trying to track you in your apps and take back control."; + +/* First part of info on the second AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page2Info1" = "App Tracking Protection "; + +/* Second part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info2" = "detects and blocks app trackers from other companies,"; + +/* Third part of info on the second AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page2Info3" = " like when Google attempts to track you in a health app."; + +/* Fourth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info4" = "It’s free,"; + +/* Fifth part of info on the second AppTP onboarding page (note the leading and trailing space) */ +"appTP.onboarding.page2Info5" = " and you can enjoy your apps as you normally would. Working in the background, it helps "; + +/* Sixth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info6" = "protect you night and day."; + +/* First part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info1" = "App Tracking Protection is not a VPN."; + +/* Second part of info on the third AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page3Info2" = " However, your device will recognize it as one. This is because it uses a local VPN connection to work."; + +/* Third part of info on the third AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page3Info3" = "App Tracking Protection is different. "; + +/* Fourth part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info4" = "It never routes app data through an external server."; + +/* Title for first AppTP onboarding page */ +"appTP.onboarding.title1" = "One easy step for better app privacy!"; + +/* Title for second AppTP onboarding page */ +"appTP.onboarding.title2" = "How does it work?"; + +/* Title for third AppTP onboarding page */ +"appTP.onboarding.title3" = "Who sees your data?"; + +/* Breakage report app name label */ +"appTP.report.appLabel" = "Which app is having issues?"; + +/* Breakage report app name placeholder */ +"appTP.report.appPlaceholder" = "App name"; + +/* Breakage report category label */ +"appTP.report.categoryLabel" = "Hva er det som skjer?"; + +/* Breakage report comment label */ +"appTP.report.commentLabel" = "Comments"; + +/* Breakage report comment placeholder */ +"appTP.report.commentPlaceholder" = "Add additional details"; + +/* Breakage report footer explaining what is collected in the breakage report */ +"appTP.report.footer" = "In addition to the details entered into this form, your app issue report will contain:\n• A list of trackers blocked in the last 10 min\n• Whether App Tracking Protection is enabled\n• Aggregate DuckDuckGo app diagnostics"; + +/* Breakage report submit button */ +"appTP.report.submit" = "Send"; + +/* Breakage report form title */ +"appTP.report.title" = "Report Issue"; + +/* Breakage report succcess message */ +"appTP.report.toast" = "Takk! Tilbakemeldingen er sendt."; + +/* Cancel button for 'Report an Issue' alert. */ +"appTP.reportAlert.cancel" = "Ikke nå"; + +/* Confirm button for 'Report an Issue' alert. */ +"appTP.reportAlert.confirm" = "Rapporter problem"; + +/* Message for 'Report an Issue' alert. */ +"appTP.reportAlert.message" = "Let us know if you disabled App Tracking Protection for this specific tracker because it caused app issues. Your feedback helps us improve!"; + +/* Title for 'Report an Issue' alert. */ +"appTP.reportAlert.title" = "Report Issue?"; + +/* Toast notification diplayed after restoring the blocklist to default settings */ +"appTP.restoreDefaultsToast" = "Default settings restored"; + +/* Button to restore the blocklist to its default state. */ +"appTP.restoreDefualts" = "Gjenopprett standardinnstillinger"; + +/* Title for the App Tracking Protection feature */ +"appTP.title" = "Beskyttelse mot appsporing"; + +/* Text indicating when the tracker was last allowed. Example: Last attempt allowed (timeString) */ +"appTP.trackerAllowedTimestamp" = "Siste tillatte forsøk: %@"; + +/* Text indicating when the tracker was last blocked. Example: Last attempt blocked (timeString) */ +"appTP.trackerBlockedTimestamp" = "Siste blokkerte forsøk: %@"; + +/* Do not translate. StringsDict entry -- Subtitle for tracking attempts in App Tracking Protection Activity View. Example: (count) tracking attempts */ +"appTP.trackingattempts" = "appTP.trackingattempts"; + /* Authentication Alert Sign In Button */ "auth.alert.login.button" = "Logg inn"; @@ -301,6 +529,18 @@ /* Toast message when a login item without a title is deleted */ "autofill.logins.list.login-deleted-message-no-title" = "Påloggingen er slettet"; +/* Title for a button that allows a user to reset their list of never saved sites */ +"autofill.logins.list.never.saved" = "Tilbakestill ekskluderte nettsteder"; + +/* Cancel button for resetting list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.cancel" = "Avbryt"; + +/* Confirm button to reset list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.confirm" = "Tilbakestill ekskluderte nettsteder"; + +/* Alert title */ +"autofill.logins.list.never.saved.reset.action.title" = "Hvis du tilbakestiller ekskluderte nettsteder, blir du bedt om å lagre påloggingsinformasjonen din neste gang du logger på disse nettstedene."; + /* Placeholder for search field on autofill login listing */ "autofill.logins.list.search-placeholder" = "Søk i pålogginger"; @@ -335,7 +575,7 @@ "autofill.logins.prompt.password.button.title" = "Passord for %@"; /* Title for autofill login prompt */ -"autofill.logins.prompt.title" = "Vil du bruke en lagret pålogging?"; +"autofill.logins.prompt.title" = "Vil du bruke et lagret passord?"; /* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ "autofill.logins.search.no-results.subtitle" = "for «%@»"; @@ -385,27 +625,24 @@ /* Title for the alert dialog telling the user an updated username is no longer a private email address */ "autofill.removed.duck.address.title" = "Brukernavn for privat Duck-adresse ble fjernet"; +/* CTA displayed on modal asking if the user never wants to be prompted to save a login for this website agin */ +"autofill.save-login.never-prompt.CTA" = "Aldri spør for dette nettstedet"; + /* Message displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.message" = "Pålogginger lagres trygt på enheten din i påloggingsmenyen."; +"autofill.save-login.new-user.message" = "Passord lagres trygt på enheten din i påloggingsmenyen."; /* Title displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.title" = "Vil du at DuckDuckGo skal lagre påloggingen din?"; +"autofill.save-login.new-user.title" = "Vil du at DuckDuckGo skal lagre passordet ditt?"; /* Cancel CTA displayed on modal asking for the user to save the login */ "autofill.save-login.not-now.CTA" = "Ikke lagre"; -/* Confirm CTA displayed on modal asking for the user to save the login */ -"autofill.save-login.save.CTA" = "Lagre påloggingen"; - /* Title displayed on modal asking for the user to save the login */ "autofill.save-login.title" = "Vil du lagre påloggingen?"; /* Confirm CTA displayed on modal asking for the user to save the password */ "autofill.save-password.save.CTA" = "Lagre passordet"; -/* Title displayed on modal asking for the user to save the password */ -"autofill.save-password.title" = "Vil du lagre passordet?"; - /* Accessibility title for a Show Password button displaying actial password instead of ***** */ "autofill.show-password" = "Vis passord"; @@ -413,7 +650,7 @@ "autofill.signin.to.manage" = "%@ for å administrere Duck-adressene dine på denne enheten."; /* Message displayed on modal asking for the user to update the password */ -"autofill.update-password.message" = "DuckDuckGo oppdaterer denne lagrede påloggingen på enheten din."; +"autofill.update-password.message" = "DuckDuckGo oppdaterer dette lagrede passordet på enheten din."; /* Confirm CTA displayed on modal asking for the user to update the password */ "autofill.update-password.save.CTA" = "Oppdater passordet"; @@ -442,6 +679,15 @@ /* Placeholder in the add bookmark form */ "bookmark.address.placeholder" = "www.example.com"; +/* Delete bookmark alert message */ +"bookmark.delete.alert.message" = "Dette vil slette bokmerket for \"%@\""; + +/* Delete bookmark alert title */ +"bookmark.delete.alert.title" = "Delete?"; + +/* The message shown after a bookmark has been deleted */ +"bookmark.deleted.toast" = "Bokmerket er slettet"; + /* Delete bookmark folder alert delete button */ "bookmark.deleteFolderAlert.deleteButton" = "Slett"; @@ -538,9 +784,21 @@ /* Broken Site Category */ "brokensite.category.videos" = "Video ble ikke avspilt"; +/* Broken Site Category Placeholder */ +"brokensite.categoryPlaceholder" = "Velg problemet ditt fra listen …"; + +/* Broken Site Category Section Title */ +"brokensite.categoryTitle" = "VELG EN KATEGORI"; + +/* Broken Site Comment Placeholder */ +"brokensite.commentPlaceholder" = "Hvis du gir oss flere detaljer, kan det hjelpe oss å løse dette problemet"; + /* Broken Site Section Title */ "brokensite.sectionTitle" = "BESKRIV HVA SOM SKJEDDE"; +/* No comment provided by engineer. */ +"bucket: %@" = "bucket: %@"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Foregående måned"; @@ -772,6 +1030,39 @@ /* Empty search placeholder on bookmarks search */ "empty.search" = "Ingen match ble funnet"; +/* No comment provided by engineer. */ +"Error" = "Feil"; + +/* Button title to Sign In */ +"error.email-protection-sign-in.action" = "Logg inn"; + +/* Alert message */ +"error.email-protection-sign-in.body" = "Beklager, du må logge på igjen for å aktivere Email Protection-funksjoner på denne nettleseren på nytt."; + +/* Alert title */ +"error.email-protection-sign-in.title" = "Feil ved Email Protection"; + +/* Button title to open device settings */ +"error.insufficient-disk-space.action" = "Åpne innstillingene"; + +/* Alert message */ +"error.insufficient-disk-space.body" = "Det ser ut til at enheten din ikke har mer lagringsplass igjen. Frigjør plass for å fortsette."; + +/* Alert title */ +"error.insufficient-disk-space.title" = "Ikke nok lagringsplass"; + +/* Button title that is shutting down the app */ +"error.preemptive-crash.action" = "Lukk appen"; + +/* Alert message */ +"error.preemptive-crash.body" = "Det har oppstått et problem med appen, så den må lukkes. Åpne på nytt for å fortsette."; + +/* Alert title */ +"error.preemptive-crash.title" = "Problem med appen er oppdaget"; + +/* Generic error message on a dialog for when the cause is not known. */ +"error.unknown.try.again" = "An unknown error has occurred"; + /* No comment provided by engineer. */ "favorite" = "Favoritt"; @@ -991,6 +1282,18 @@ /* Home tab title */ "homeTab.title" = "Hjem"; +/* OK title for invite screen alert dismissal button */ +"invite.alert.ok.button" = "OK"; + +/* Continue button on an invite dialog */ +"invite.dialog.continue.button" = "Continue"; + +/* Get Started button on an invite dialog */ +"invite.dialog.get.started.button" = "Get Started"; + +/* Message to show after user enters an unrecognized invite code */ +"invite.dialog.unrecognized.code.message" = "We didn’t recognize this Invite Code."; + /* No comment provided by engineer. */ "keyCommandAddBookmark" = "Legg til bokmerke"; @@ -1057,15 +1360,15 @@ /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "DuckDuckGo for Mac har hastigheten du trenger, nettleserfunksjonene du forventer, og er stappfull av våre førsteklasses personvernfunksjoner."; -/* Body text for the macOS waitlist notification */ -"mac-waitlist.available.notification.body" = "Åpne invitasjonen din"; - /* Title for the macOS waitlist notification */ "mac-waitlist.available.notification.title" = "DuckDuckGo for Mac er klar!"; /* Title for the copy action */ "mac-waitlist.copy" = "Kopier"; +/* Description text above the Share Link button */ +"mac-waitlist.join-waitlist-screen.on-your-computer-go-to" = "På en Windows-datamaskin går du til:"; + /* Description text above the Share Link button */ "mac-waitlist.join-waitlist-screen.on-your-mac-go-to" = "På Mac går du til:"; @@ -1078,6 +1381,9 @@ /* Disclaimer for the Join Waitlist screen */ "mac-waitlist.join-waitlist-screen.windows" = "Windows kommer snart!"; +/* Title for the macOS waitlist button redirecting to Windows waitlist */ +"mac-waitlist.join-waitlist-screen.windows-waitlist" = "Ser du etter Windows-versjonen?"; + /* Title for the settings subtitle */ "mac-waitlist.settings.browse-privately" = "Surf privat med vår app for Mac"; @@ -1088,7 +1394,7 @@ "mac-waitlist.share-sheet.title" = "DuckDuckGo for Mac"; /* Title for the Mac Waitlist feature */ -"mac-waitlist.title" = "DuckDuckGo skrivebordsapp"; +"mac-waitlist.title" = "DuckDuckGo-app for Mac"; /* No comment provided by engineer. */ "menu.button.hint" = "Alternativer"; @@ -1105,9 +1411,81 @@ /* Edit button */ "navigation.title.edit" = "Rediger"; +/* String indicating NetP is connected when viewed from the settings screen */ +"netP.cell.connected" = "Connected"; + +/* String indicating NetP is disconnected when viewed from the settings screen */ +"netP.cell.disconnected" = "Not connected"; + +/* Title for the Network Protection feature */ +"netP.title" = "Network Protection"; + +/* Message for the network protection invite dialog */ +"network.protection.invite.dialog.message" = "Enter your invite code to get started."; + +/* Title for the network protection invite screen */ +"network.protection.invite.dialog.title" = "You're invited to try Network Protection"; + +/* Prompt for the network protection invite code text field */ +"network.protection.invite.field.prompt" = "Invite Code"; + +/* Message explaining that netP is invite only */ +"network.protection.invite.only.message" = "DuckDuckGo Network Protection is currently invite-only."; + +/* Message for the network protection invite success view */ +"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; + +/* Title for the network protection invite success view */ +"network.protection.invite.success.title" = "Success! You’re in."; + +/* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ +"network.protection.status.connected.format" = "Connected - %@"; + +/* The label for the NetP VPN when connecting */ +"network.protection.status.connecting" = "Connecting..."; + +/* The label for the NetP VPN when disconnected */ +"network.protection.status.disconnected" = "Not connected"; + +/* The label for the NetP VPN when disconnecting */ +"network.protection.status.disconnecting" = "Disconnecting..."; + +/* Message label text for the netP status view */ +"network.protection.status.header.message" = "DuckDuckGo's VPN secures all of your device's Internet traffic anytime, anywhere."; + +/* Header title label text for the status view when netP is disconnected */ +"network.protection.status.header.title.off" = "Network Protection is Off"; + +/* Header title label text for the status view when netP is connected */ +"network.protection.status.header.title.on" = "Network Protection is On"; + +/* The status view 'Share Feedback' button which is shown inline on the status view after the \(netPInviteOnlyMessage) text */ +"network.protection.status.menu.share.feedback" = "Share Feedback"; + +/* Connection details label shown in NetworkProtection's status view. */ +"network.protection.status.view.connection.details" = "Connection Details"; + +/* Generic connection failed error message shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.message" = "Please try again later."; + +/* Generic connection failed error title shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; + +/* IP Address label shown in NetworkProtection's status view. */ +"network.protection.status.view.ip.address" = "IP Address"; + +/* Location label shown in NetworkProtection's status view. */ +"network.protection.status.view.location" = "Location"; + +/* Title label text for the status view when netP is disconnected */ +"network.protection.status.view.title" = "Network Protection"; + /* Do not translate - stringsdict entry */ "number.of.tabs" = "number.of.tabs"; +/* No comment provided by engineer. */ +"Ok" = "OK"; + /* Text displayed on notification appearing in the address bar when the browser dismissed the cookie popup automatically rejecting it */ "omnibar.notification.cookies-managed" = "Informasjonskapsler administrert"; @@ -1147,6 +1525,18 @@ /* ’Link’ is link on a website */ "open.externally.failed" = "Appen som kreves for å åpne koblingen ble ikke funnet"; +/* No comment provided by engineer. */ +"Please enter which app on your device is broken." = "Angi hvilken app på enheten din som ikke virker som den skal."; + +/* Activate button */ +"pm.activate" = "Reactivate"; + +/* Cancel button */ +"pm.cancel" = "Cancel"; + +/* Deactivate button */ +"pm.deactivate" = "Deactivate"; + /* No comment provided by engineer. */ "preserveLogins.domain.list.footer" = "Nettsider anvender informasjonskapsler for å holde deg innlogget. Når du gjør en side brannsikker, slettes ikke informasjonskapslene, og du forblir innlogget, selv etter å ha brukt brannknappen. Vi blokkerer fortsatt tredjeparters sporingsanordninger som blir funnet på Brannsikre nettsteder."; @@ -1216,8 +1606,8 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Favoritter"; -/* No comment provided by engineer. */ -"settings.about.text" = "Hos DuckDuckGo setter vi den nye standarden for tillit på nett.\n\nDuckDuckGo privatnettleser gir deg verktøyene du trenger for å sikre personvernet mens du søker og surfer på nett, med funksjoner som blokkering av sporingsanordninger, smartere kryptering og DuckDuckGo privatsøk.\n\nInternett burde ikke føles ekkelt å bruke, og det å få privatlivet du fortjener på nett, bør være like enkelt som å lukke persiennene."; +/* about page */ +"settings.about.text" = "DuckDuckGo er et uavhengig personvernselskap som ble grunnlagt i 2008, for alle som er lei av å bli sporet på nettet og vil ha en enkel løsning. Vi er beviset på at ekte personvern er mulig på nettet uten å fire på kravene.\n\nDuckDuckGo-nettleseren har funksjoner du kan forvente i en vanlig nettleser, som bokmerker, faner, passord med mer, pluss over [en rekke kraftige personvernfunksjoner](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/) ikke tilbys i de fleste populære nettlesere som standard. Disse usedvanlig omfattende personvernfunksjonene bidrar til å beskytte nettaktivitetene dine, fra søk til surfing, e-poster med mer.\n\nPersonvernfunksjonene fungerer uten at du behøver å kunne noe om de tekniske detaljene eller forholde deg til kompliserte innstillinger. Det eneste du behøver å gjøre, er å bytte nettleser til DuckDuckGo på alle enhetene dine, så får du personvern som standard.\n\nMen hvis du *vil* ta en titt under panseret, kan du finne mer informasjon om hvordan DuckDuckGos personvern fungerer på våre [hjelpesider](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/)."; /* Report a Broken Site screen confirmation button */ "siteFeedback.buttonText" = "Send inn rapport"; @@ -1237,6 +1627,9 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Hvilket nettsted fungerer ikke?"; +/* No comment provided by engineer. */ +"sync.remove-device.message" = "\"%@\" vil ikke lenger ha tilgang til dine synkroniserte data."; + /* Accessibility label on remove button */ "tab.close.home" = "Lukk hjem-fane"; @@ -1309,6 +1702,57 @@ /* Voice-search footer note with on-device privacy warning */ "voiceSearch.footer.note" = "Lyd behandles på enheten. Den lagres ikke og deles ikke med noen, heller ikke med DuckDuckGo."; +/* Title for the button to enable push notifications in system settings */ +"waitlist.allow-notifications" = "Tillat varsler"; + +/* Body text for the waitlist notification */ +"waitlist.available.notification.body" = "Åpne invitasjonen din"; + +/* Title for the copy action */ +"waitlist.copy" = "Kopier"; + +/* Label text for the invite code */ +"waitlist.invite-code" = "Invitasjonskode"; + +/* Step title on the invite screen */ +"waitlist.invite-screen.step.title" = "Trinn %d"; + +/* Title for the invite code screen */ +"waitlist.invite-screen.youre-invited" = "Du er invitert!"; + +/* Title for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.join" = "Sett deg på den private ventelisten"; + +/* Temporary status text for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.joining" = "Setter deg på ventelisten…"; + +/* Title for the Share Link button */ +"waitlist.join-waitlist-screen.share-link" = "Del lenke"; + +/* Notification text for the waitlist */ +"waitlist.joined.no-notification.get-notification" = "VARSLE MEG"; + +/* Title for the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.get-notification-confirmation-title" = "Skal vi varsle deg når det er din tur?"; + +/* Cancel button in the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.no-thanks" = "Nei takk"; + +/* Text used for the Notifications Disabled state */ +"waitlist.notification.disabled" = "Vi kan varsle deg når det er din tur, men varsler er for øyeblikket deaktivert for DuckDuckGo."; + +/* Privacy disclaimer for the Waitlist feature */ +"waitlist.privacy-disclaimer" = "Du behøver ikke å dele noen personopplysninger for å sette deg på ventelisten. Du sikrer plassen din i køen med et tidsstempel som kun eksisterer på enheten din, så vi kan varsle deg når det er din tur."; + +/* Title for the queue screen */ +"waitlist.queue-screen.on-the-list" = "Du står på listen!"; + +/* Title for the settings subtitle */ +"waitlist.settings.download-available" = "Nedlasting tilgjengelig"; + +/* Title for the share sheet entry */ +"waitlist.share-sheet.title" = "Du er invitert!"; + /* Confirmation message */ "web.url.remove.favorite.done" = "Favoritt er fjernet"; @@ -1333,3 +1777,51 @@ /* Alert title explaining the message is shown by a website */ "webJSAlert.website-message.format" = "En melding fra %@:"; +/* No comment provided by engineer. */ +"Welcome to the Duck Side!" = "Velkommen til «the Duck Side»!"; + +/* Title for the Windows waitlist notification */ +"windows-waitlist.available.notification.title" = "Prøv DuckDuckGo for Windows!"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-1.description" = "Besøk denne URL-adressen på Windows-enheten din for å laste ned:"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-2.description" = "Åpne DuckDuckGo-installasjonsprogrammet i Nedlastinger, velg Installer og angi deretter invitasjonskoden."; + +/* Subtitle for the Windows Waitlist Invite screen */ +"windows-waitlist.invite-screen.subtitle" = "Klar for å bruke DuckDuckGo i Windows?\n\n"; + +/* Title for the Windows waitlist button redirecting to Mac waitlist */ +"windows-waitlist.join-waitlist-screen.mac-waitlist" = "Ser du etter Mac-versjonen?"; + +/* Title for the Join Windows Waitlist screen */ +"windows-waitlist.join-waitlist-screen.try-duckduckgo-for-windows" = "Få tidlig tilgang til å prøve DuckDuckGo for Windows!"; + +/* Message for the alert to confirm enabling notifications */ +"windows-waitlist.joined.no-notification.get-notification-confirmation-message" = "Vi sender deg et varsel når din utgave av DuckDuckGo for Windows er klar til nedlasting. "; + +/* Label text for the Joined Waitlist state with notifications declined */ +"windows-waitlist.joined.notifications-declined" = "Invitasjonen din til å prøve DuckDuckGo for Mac kommer hit. Kom tilbake snart, eller vi kan sende deg et varsel når det er din tur."; + +/* Label text for the Joined Waitlist state with notifications enabled */ +"windows-waitlist.joined.notifications-enabled" = "Vi sender deg et varsel når du kan laste ned DuckDuckGo for Windows."; + +/* Title for the settings subtitle */ +"windows-waitlist.settings.browse-privately" = "Surf privat med vår app for Windows"; + +/* Message used when sharing to iMessage. Parameter is an eight digit invite code. */ +"windows-waitlist.share-sheet.invite-code-message" = "Du er invitert!\n\nKlar for å bruke DuckDuckGo i Windows?\n\nTrinn 1\nBesøk denne URL-adressen på Windows-enheten din for å laste ned:\nhttps://duckduckgo.com/windows\n\nTrinn 2\nÅpne DuckDuckGo-installasjonsprogrammet i Nedlastinger, velg Installer og angi deretter invitasjonskoden.\n\nInvitasjonskode: %@"; + +/* Message used when sharing to iMessage */ +"windows-waitlist.share-sheet.message" = "Klar for å begynne å surfe privat i Windows?\n\nfBesøk denne URL-adressen på datamaskinen din for å laste ned:\nhttps://duckduckgo.com/windows"; + +/* Summary text for the Windows browser waitlist */ +"windows-waitlist.summary" = "DuckDuckGo for Windows har det du trenger for å surfe med mer personvern – privat søk, sporingsblokkering, tvungen kryptering, blokkering av popup-vinduer om informasjonskapsler og enda flere førsteklasses beskyttelser som er på vei."; + +/* Title for the Windows Waitlist feature */ +"windows-waitlist.title" = "DuckDuckGo-app for Windows"; + +/* Title for the Windows browser download link page */ +"windows-waitlist.waitlist-download-screen.try-duckduckgo-for-windows" = "Skaff deg DuckDuckGo for Windows!"; + diff --git a/DuckDuckGo/nb.lproj/Settings.strings b/DuckDuckGo/nb.lproj/Settings.strings index d3b39adacc..bbd8470e81 100644 --- a/DuckDuckGo/nb.lproj/Settings.strings +++ b/DuckDuckGo/nb.lproj/Settings.strings @@ -37,6 +37,9 @@ /* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ "9Ko-0g-T3h.text" = "Tekststørrelse"; +/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ +"9kt-6R-XiZ.text" = "Tittel"; + /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Appoversikt"; @@ -73,6 +76,12 @@ /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Global Privacy Control (GPC)"; +/* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ +"CxT-QK-iVn.title" = "Legg til"; + +/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ +"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; + /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "når appen lukkes eller har vært inaktiv i 15 minutter"; @@ -151,6 +160,9 @@ /* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ "HLr-R8-xxF.text" = "Forhåndsvisning ved å trykke og holde"; +/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ +"hoT-Nu-KXP.text" = "Surf privat med vår app for Windows"; + /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Personvernbeskyttelse aktivert for alle nettsteder"; @@ -163,6 +175,12 @@ /* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ "m23-t6-9cb.text" = "Del tilbakemelding"; +/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ +"mLn-1x-Fl5.title" = "Plassering av adressefelt"; + +/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ +"mqV-pf-NZ1.text" = "Plassering av adressefelt"; + /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "La DuckDuckGo administrere popup-vinduer om samtykke til informasjonskapsler"; @@ -175,15 +193,27 @@ /* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ "oM7-1o-9oY.text" = "Om DuckDuckGo"; +/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ +"opn-JO-idF.text" = "Topp"; + /* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ "OxE-MQ-uJk.headerTitle" = "Mer fra DuckDuckGo"; +/* Class = "UILabel"; text = "Sync"; ObjectID = "oXN-ez-gct"; */ +"oXN-ez-gct.text" = "Synkr."; + /* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ "P0F-ts-ekd.text" = "Surf privat med vår app for Mac "; +/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ +"qah-gb-udB.text" = "Network Protection"; + /* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ "qeN-SV-zy7.text" = "Etikett"; +/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ +"RQ8-H1-Ez1.text" = "DuckDuckGo Windows-app"; + /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Tekststørrelse"; @@ -232,8 +262,8 @@ /* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ "yvj-LL-MiR.text" = "Legg til appen i docken"; -/* Class = "UILabel"; text = "DuckDuckGo Desktop App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "DuckDuckGo skrivebordsapp"; +/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ +"Yz9-17-qnn.text" = "DuckDuckGo Mac-app"; /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Ny fane"; diff --git a/DuckDuckGo/nl.lproj/Feedback.strings b/DuckDuckGo/nl.lproj/Feedback.strings index 435ff9f481..0aa9568bb8 100644 --- a/DuckDuckGo/nl.lproj/Feedback.strings +++ b/DuckDuckGo/nl.lproj/Feedback.strings @@ -28,9 +28,6 @@ /* Class = "UIBarButtonItem"; title = "Back"; ObjectID = "JF8-Ow-zXL"; */ "JF8-Ow-zXL.title" = "Terug"; -/* Class = "UIBarButtonItem"; title = "Submit"; ObjectID = "jr6-Al-4fr"; */ -"jr6-Al-4fr.title" = "Verzenden"; - /* Class = "UIButton"; normalTitle = "Submit"; ObjectID = "lsB-Kq-U8p"; */ "lsB-Kq-U8p.normalTitle" = "Verzenden"; @@ -58,9 +55,6 @@ /* Class = "UINavigationItem"; title = "Report Broken Site"; ObjectID = "WjT-HM-yim"; */ "WjT-HM-yim.title" = "Defecte website melden"; -/* Class = "UILabel"; text = "Broken Site Reason"; ObjectID = "Wx3-Vb-ItS"; */ -"Wx3-Vb-ItS.text" = "Reden voor defecte website"; - /* Class = "UINavigationItem"; title = "Share Feedback"; ObjectID = "Xb0-Zu-opM"; */ "Xb0-Zu-opM.title" = "Feedback delen"; diff --git a/DuckDuckGo/nl.lproj/Localizable.strings b/DuckDuckGo/nl.lproj/Localizable.strings index 6c18ca909d..0a52970964 100644 --- a/DuckDuckGo/nl.lproj/Localizable.strings +++ b/DuckDuckGo/nl.lproj/Localizable.strings @@ -1,6 +1,9 @@ /* No comment provided by engineer. */ "%@" = "%@"; +/* No comment provided by engineer. */ +"%@ [%@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)" = "%1$@ [%2$@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)"; + /* Buton label for Edit action */ "action.generic.edit" = "Bewerken"; @@ -103,6 +106,12 @@ /* Share action */ "action.title.share" = "Delen"; +/* Settings label for bottom position for the address bar */ +"address.bar.bottom" = "Onderkant"; + +/* Settings label for top position for the address bar */ +"address.bar.top" = "Boven"; + /* No comment provided by engineer. */ "addWidget.button" = "Widget toevoegen"; @@ -142,6 +151,225 @@ /* Shown on authentication screen */ "app.authentication.unlock" = "DuckDuckGo ontgrendelen."; +/* First part of about page content (note the trailing space) */ +"appTP.about.content1" = "You’ve probably heard about companies like Google and Facebook tracking you behind the scenes on third-party websites. But did you know they also track your personal information through apps on your device?\n\nIn 2022, DuckDuckGo found that "; + +/* Second part of about page content (note the trailing space) */ +"appTP.about.content2" = "over 85% of free iOS apps tested contained hidden trackers from other companies."; + +/* Third part of about page content (note the leading space) */ +"appTP.about.content3" = " Of the 395 apps tested, 60% sent data to Google. This happens even while you’re not using your device.\n\nTrackers in apps may have access to a lot more information than their website tracker cousins, such as your location down to which floor of a building you're on, how often you play games while at work, and when and how long you sleep each day. Even if you haven’t given apps explicit permission to collect data, they can still take it without your knowledge.\n\nTracking networks like Facebook and Google use these little pieces of information to build a digital profile about you. With it, tracking networks can manipulate what you see online and allow advertisers to bid on access to you based on your data.\n\nTrackers in apps is a BIG problem for privacy. But DuckDuckGo has a solution that can help.\n\nWhen enabled in the DuckDuckGo Privacy Browser app, App Tracking Protection blocks many trackers in other apps, not just the trackers we find on websites when you browse. These dual layers of protection reduce what companies know about you overall, so you can use your apps with more peace of mind, knowing you’re more protected."; + +/* Navigation Title for AppTP about page */ +"appTP.about.navTitle" = "About App Trackers"; + +/* Title for AppTP about page */ +"appTP.about.title" = "What Are App Trackers?"; + +/* Title for 'Report an Issue' button in the activity view. */ +"appTP.activityView.reportIssue" = "Report Issue"; + +/* Text label for switch that turns blocking on or off for a tracker */ +"appTP.blockTrackerText" = "Block this Tracker"; + +/* Detail string describing what AppTP is */ +"appTP.cell.detail" = "App trackers blokkeren op je apparaat"; + +/* String indicating AppTP is disabled when viewed from the settings screen */ +"appTP.cell.disabled" = "Uitgeschakeld"; + +/* String indicating AppTP is enabled when viewed from the settings screen */ +"appTP.cell.enabled" = "Ingeschakeld"; + +/* Info string informing the user what App Tracking Protection does. */ +"appTP.empty.disabled.info" = "Schakel App Tracking Protection in zodat we vervelende trackers in andere apps kunnen blokkeren."; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.heading" = "We’re blocking hidden trackers"; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.info" = "Come back soon to see a list of all the app trackers we’ve blocked."; + +/* First answer for AppTP FAQ page */ +"appTP.faq.answer1" = "App Tracking Protection blocks app trackers from other companies, like when Facebook tries to track you in a banking app. Companies may still track you in apps they own."; + +/* Second answer for AppTP FAQ page */ +"appTP.faq.answer2" = "Yes! App Tracking Protection works across all apps on your device to block the most common hidden trackers we find trying to collect your personal info."; + +/* Third answer for AppTP FAQ page */ +"appTP.faq.answer3" = "We currently only block the most common trackers that we find on iOS. This helps us to comprehensively test App Tracking Protection and lower frequency of app breakage, while blocking up to 70% of all tracking requests."; + +/* Fourth answer for AppTP FAQ page */ +"appTP.faq.answer4" = "You’ll be asked to set up a virtual private network (VPN) connection, but you don't need to install a VPN app for App Tracking Protection to work.\n\nThis permission, which works only on your device, allows App Tracking Protection to monitor network traffic so that it can block known trackers."; + +/* Fifth answer for AppTP FAQ page */ +"appTP.faq.answer5" = "You can use App Tracking Protection at the same time as using an IKEv2 protocol VPN app on an iOS device. You won’t be able to use App Tracking Protection on an iOS device if you’re using a VPN app that uses a different type of protocol, like WireGuard or OpenVPN type VPNs."; + +/* Sixth answer for AppTP FAQ page */ +"appTP.faq.answer6" = "A VPN sends your data from the device to its own server, where it secures and anonymizes your data from prying eyes. However, this allows the VPN company to see your network traffic.\n\nApp Tracking Protection is different. Instead of sending your data to a VPN server, App Tracking Protection works only on your device, sitting between your apps and the servers they talk to.\n\nWhenever App Tracking Protection recognizes a known tracker, it blocks the tracker from sending personal information (such as your IP address, activity, and device details) off your device. All other traffic reaches its destination, so your apps work normally."; + +/* Seventh answer for AppTP FAQ page */ +"appTP.faq.answer7" = "App Tracking Protection works only on your device and doesn’t send your data off your device to DuckDuckGo. We don’t collect or store any data from your apps."; + +/* First question for AppTP FAQ page */ +"appTP.faq.question1" = "How does App Tracking Protection work?"; + +/* Second question for AppTP FAQ page */ +"appTP.faq.question2" = "Does App Tracking Protection block trackers in all apps on my device?"; + +/* Third question for AppTP FAQ page */ +"appTP.faq.question3" = "Does App Tracking Protection block all app trackers?"; + +/* Fourth question for AppTP FAQ page */ +"appTP.faq.question4" = "Why does App Tracking Protection use a VPN connection?"; + +/* Fifth question for AppTP FAQ page */ +"appTP.faq.question5" = "Will App Tracking Protection work if I also use a VPN app?"; + +/* Sixth question for AppTP FAQ page */ +"appTP.faq.question6" = "How is App Tracking Protection different from a VPN?"; + +/* Seventh question for AppTP FAQ page */ +"appTP.faq.question7" = "Is my data private?"; + +/* Title for AppTP FAQ page */ +"appTP.faq.title" = "App Tracking Protection FAQ"; + +/* Do not translate. StringsDict entry -- Count part of string 'App Tracking Protection blocked x tracking attempts today' */ +"appTP.home.blockedCount" = "appTP.home.blockedCount"; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the trailing space) */ +"appTP.home.blockedPrefix" = "App Tracking Protection heeft vandaag "; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the leading space) */ +"appTP.home.blockedSuffix" = " in je apps geblokkeerd."; + +/* Prefix of string 'App Tracking Protection disabled. Tap to re-enable.' (note the trailing space) */ +"appTP.home.disabledPrefix" = "App Tracking Protection disabled. "; + +/* Suffix of string 'App Tracking Protection disabled. Tap to re-enable.' */ +"appTP.home.disabledSuffix" = "Tap to continue blocking tracking attempts across your apps."; + +/* Text indicating the tracking event occured 'just now'. Example: Last attempt 'just now' */ +"appTP.justNow" = "Zojuist"; + +/* View to manage trackers for AppTP. Allows the user to turn trackers on or off. */ +"appTP.manageTrackers" = "Trackers beheren"; + +/* Button title for AppTP onboarding */ +"appTP.onboarding.continueButton" = "Continue"; + +/* Button title for AppTP onboarding to enable AppTP */ +"appTP.onboarding.enableeButton" = "Enable App Tracking Protection"; + +/* Button title for AppTP onboarding to learn more about AppTP */ +"appTP.onboarding.learnMoreButton" = "Learn More"; + +/* First part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info1" = "Over 85% of free iOS apps"; + +/* Second part of info on the first AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page1Info2" = " we’ve tested allow other companies to track your personal information, even when you’re sleeping."; + +/* Third part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info3" = "See who we catch trying to track you in your apps and take back control."; + +/* First part of info on the second AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page2Info1" = "App Tracking Protection "; + +/* Second part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info2" = "detects and blocks app trackers from other companies,"; + +/* Third part of info on the second AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page2Info3" = " like when Google attempts to track you in a health app."; + +/* Fourth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info4" = "It’s free,"; + +/* Fifth part of info on the second AppTP onboarding page (note the leading and trailing space) */ +"appTP.onboarding.page2Info5" = " and you can enjoy your apps as you normally would. Working in the background, it helps "; + +/* Sixth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info6" = "protect you night and day."; + +/* First part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info1" = "App Tracking Protection is not a VPN."; + +/* Second part of info on the third AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page3Info2" = " However, your device will recognize it as one. This is because it uses a local VPN connection to work."; + +/* Third part of info on the third AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page3Info3" = "App Tracking Protection is different. "; + +/* Fourth part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info4" = "It never routes app data through an external server."; + +/* Title for first AppTP onboarding page */ +"appTP.onboarding.title1" = "One easy step for better app privacy!"; + +/* Title for second AppTP onboarding page */ +"appTP.onboarding.title2" = "How does it work?"; + +/* Title for third AppTP onboarding page */ +"appTP.onboarding.title3" = "Who sees your data?"; + +/* Breakage report app name label */ +"appTP.report.appLabel" = "Which app is having issues?"; + +/* Breakage report app name placeholder */ +"appTP.report.appPlaceholder" = "App name"; + +/* Breakage report category label */ +"appTP.report.categoryLabel" = "Wat gebeurt er?"; + +/* Breakage report comment label */ +"appTP.report.commentLabel" = "Comments"; + +/* Breakage report comment placeholder */ +"appTP.report.commentPlaceholder" = "Add additional details"; + +/* Breakage report footer explaining what is collected in the breakage report */ +"appTP.report.footer" = "In addition to the details entered into this form, your app issue report will contain:\n• A list of trackers blocked in the last 10 min\n• Whether App Tracking Protection is enabled\n• Aggregate DuckDuckGo app diagnostics"; + +/* Breakage report submit button */ +"appTP.report.submit" = "Verzenden"; + +/* Breakage report form title */ +"appTP.report.title" = "Report Issue"; + +/* Breakage report succcess message */ +"appTP.report.toast" = "Dank je wel! Feedback verzonden."; + +/* Cancel button for 'Report an Issue' alert. */ +"appTP.reportAlert.cancel" = "Niet nu"; + +/* Confirm button for 'Report an Issue' alert. */ +"appTP.reportAlert.confirm" = "Probleem melden"; + +/* Message for 'Report an Issue' alert. */ +"appTP.reportAlert.message" = "Let us know if you disabled App Tracking Protection for this specific tracker because it caused app issues. Your feedback helps us improve!"; + +/* Title for 'Report an Issue' alert. */ +"appTP.reportAlert.title" = "Report Issue?"; + +/* Toast notification diplayed after restoring the blocklist to default settings */ +"appTP.restoreDefaultsToast" = "Default settings restored"; + +/* Button to restore the blocklist to its default state. */ +"appTP.restoreDefualts" = "Standaardinstellingen herstellen"; + +/* Title for the App Tracking Protection feature */ +"appTP.title" = "Bescherming tegen app-tracking"; + +/* Text indicating when the tracker was last allowed. Example: Last attempt allowed (timeString) */ +"appTP.trackerAllowedTimestamp" = "Laatste poging toegestaan %@"; + +/* Text indicating when the tracker was last blocked. Example: Last attempt blocked (timeString) */ +"appTP.trackerBlockedTimestamp" = "Laatste poging geblokkeerd %@"; + +/* Do not translate. StringsDict entry -- Subtitle for tracking attempts in App Tracking Protection Activity View. Example: (count) tracking attempts */ +"appTP.trackingattempts" = "appTP.trackingattempts"; + /* Authentication Alert Sign In Button */ "auth.alert.login.button" = "Aanmelden"; @@ -301,6 +529,18 @@ /* Toast message when a login item without a title is deleted */ "autofill.logins.list.login-deleted-message-no-title" = "Aanmeldgegevens verwijderd"; +/* Title for a button that allows a user to reset their list of never saved sites */ +"autofill.logins.list.never.saved" = "Uitgesloten sites opnieuw instellen"; + +/* Cancel button for resetting list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.cancel" = "Annuleren"; + +/* Confirm button to reset list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.confirm" = "Uitgesloten sites opnieuw instellen"; + +/* Alert title */ +"autofill.logins.list.never.saved.reset.action.title" = "Als je uitgesloten sites opnieuw instelt, zie je de volgende keer dat je bij een van deze sites inlogt een melding om je inloggegevens op te slaan."; + /* Placeholder for search field on autofill login listing */ "autofill.logins.list.search-placeholder" = "Aanmeldgegevens zoeken"; @@ -335,7 +575,7 @@ "autofill.logins.prompt.password.button.title" = "Wachtwoord voor %@"; /* Title for autofill login prompt */ -"autofill.logins.prompt.title" = "Opgeslagen aanmeldgegevens gebruiken?"; +"autofill.logins.prompt.title" = "Een opgeslagen wachtwoord gebruiken?"; /* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ "autofill.logins.search.no-results.subtitle" = "voor '%@'"; @@ -385,27 +625,24 @@ /* Title for the alert dialog telling the user an updated username is no longer a private email address */ "autofill.removed.duck.address.title" = "De persoonlijke gebruikersnaam voor het Duck Address is verwijderd"; +/* CTA displayed on modal asking if the user never wants to be prompted to save a login for this website agin */ +"autofill.save-login.never-prompt.CTA" = "Nooit vragen voor deze site"; + /* Message displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.message" = "Aanmeldgegevens worden veilig opgeslagen op je apparaat in het menu 'Aanmeldingen'."; +"autofill.save-login.new-user.message" = "Wachtwoorden worden veilig opgeslagen op je apparaat in het menu 'Aanmeldingen'."; /* Title displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.title" = "Wil je dat DuckDuckGo je login opslaat?"; +"autofill.save-login.new-user.title" = "Wil je dat DuckDuckGo je wachtwoord opslaat?"; /* Cancel CTA displayed on modal asking for the user to save the login */ "autofill.save-login.not-now.CTA" = "Niet opslaan"; -/* Confirm CTA displayed on modal asking for the user to save the login */ -"autofill.save-login.save.CTA" = "Login opslaan"; - /* Title displayed on modal asking for the user to save the login */ "autofill.save-login.title" = "Login opslaan?"; /* Confirm CTA displayed on modal asking for the user to save the password */ "autofill.save-password.save.CTA" = "Wachtwoord opslaan"; -/* Title displayed on modal asking for the user to save the password */ -"autofill.save-password.title" = "Wachtwoord opslaan?"; - /* Accessibility title for a Show Password button displaying actial password instead of ***** */ "autofill.show-password" = "Wachtwoord weergeven"; @@ -413,7 +650,7 @@ "autofill.signin.to.manage" = "%@ om je Duck-adressen op dit apparaat te beheren."; /* Message displayed on modal asking for the user to update the password */ -"autofill.update-password.message" = "DuckDuckGo werkt deze opgeslagen aanmeldgegevens op je apparaat bij."; +"autofill.update-password.message" = "DuckDuckGo werkt dit opgeslagen wachtwoord op je apparaat bij."; /* Confirm CTA displayed on modal asking for the user to update the password */ "autofill.update-password.save.CTA" = "Wachtwoord bijwerken"; @@ -442,6 +679,15 @@ /* Placeholder in the add bookmark form */ "bookmark.address.placeholder" = "www.voorbeeld.com"; +/* Delete bookmark alert message */ +"bookmark.delete.alert.message" = "Hiermee wordt je bladwijzer voor \"%@\" verwijderd"; + +/* Delete bookmark alert title */ +"bookmark.delete.alert.title" = "Delete?"; + +/* The message shown after a bookmark has been deleted */ +"bookmark.deleted.toast" = "Bladwijzer verwijderd"; + /* Delete bookmark folder alert delete button */ "bookmark.deleteFolderAlert.deleteButton" = "Verwijderen"; @@ -538,9 +784,21 @@ /* Broken Site Category */ "brokensite.category.videos" = "Video werd niet afgespeeld"; +/* Broken Site Category Placeholder */ +"brokensite.categoryPlaceholder" = "Kies je probleem in de lijst ..."; + +/* Broken Site Category Section Title */ +"brokensite.categoryTitle" = "SELECTEER EEN CATEGORIE"; + +/* Broken Site Comment Placeholder */ +"brokensite.commentPlaceholder" = "Deel meer details om ons te helpen dit probleem op te lossen"; + /* Broken Site Section Title */ "brokensite.sectionTitle" = "BESCHRIJF WAT ER IS GEBEURD"; +/* No comment provided by engineer. */ +"bucket: %@" = "bucket: %@"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Afgelopen maand"; @@ -772,6 +1030,39 @@ /* Empty search placeholder on bookmarks search */ "empty.search" = "Geen overeenkomsten gevonden"; +/* No comment provided by engineer. */ +"Error" = "Fout"; + +/* Button title to Sign In */ +"error.email-protection-sign-in.action" = "Aanmelden"; + +/* Alert message */ +"error.email-protection-sign-in.body" = "Sorry, meld je opnieuw aan om de functies voor e-mailbeveiliging in deze browser weer in te schakelen."; + +/* Alert title */ +"error.email-protection-sign-in.title" = "Fout in Email Protection"; + +/* Button title to open device settings */ +"error.insufficient-disk-space.action" = "Open Instellingen"; + +/* Alert message */ +"error.insufficient-disk-space.body" = "Het lijkt erop dat je apparaat geen opslagruimte meer heeft. Maak ruimte vrij om door te gaan."; + +/* Alert title */ +"error.insufficient-disk-space.title" = "Niet genoeg opslagruimte"; + +/* Button title that is shutting down the app */ +"error.preemptive-crash.action" = "App sluiten"; + +/* Alert message */ +"error.preemptive-crash.body" = "Het lijkt erop dat er een probleem is met de app en dat deze moet worden gesloten. Open de app opnieuw om door te gaan."; + +/* Alert title */ +"error.preemptive-crash.title" = "Probleem met app gedetecteerd"; + +/* Generic error message on a dialog for when the cause is not known. */ +"error.unknown.try.again" = "An unknown error has occurred"; + /* No comment provided by engineer. */ "favorite" = "Favoriet"; @@ -991,6 +1282,18 @@ /* Home tab title */ "homeTab.title" = "Home"; +/* OK title for invite screen alert dismissal button */ +"invite.alert.ok.button" = "OK"; + +/* Continue button on an invite dialog */ +"invite.dialog.continue.button" = "Continue"; + +/* Get Started button on an invite dialog */ +"invite.dialog.get.started.button" = "Get Started"; + +/* Message to show after user enters an unrecognized invite code */ +"invite.dialog.unrecognized.code.message" = "We didn’t recognize this Invite Code."; + /* No comment provided by engineer. */ "keyCommandAddBookmark" = "Bladwijzer toevoegen"; @@ -1057,15 +1360,15 @@ /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "DuckDuckGo voor Mac heeft de snelheid die je nodig hebt en de browsefuncties die je verwacht, en zit boordevol met onze allerbeste privacy essentials."; -/* Body text for the macOS waitlist notification */ -"mac-waitlist.available.notification.body" = "Open je uitnodiging"; - /* Title for the macOS waitlist notification */ "mac-waitlist.available.notification.title" = "DuckDuckGo voor Mac staat klaar!"; /* Title for the copy action */ "mac-waitlist.copy" = "Kopiëren"; +/* Description text above the Share Link button */ +"mac-waitlist.join-waitlist-screen.on-your-computer-go-to" = "Ga op je Windows-computer naar:"; + /* Description text above the Share Link button */ "mac-waitlist.join-waitlist-screen.on-your-mac-go-to" = "Ga op je Mac naar:"; @@ -1078,6 +1381,9 @@ /* Disclaimer for the Join Waitlist screen */ "mac-waitlist.join-waitlist-screen.windows" = "Binnenkort ook beschikbaar voor Windows!"; +/* Title for the macOS waitlist button redirecting to Windows waitlist */ +"mac-waitlist.join-waitlist-screen.windows-waitlist" = "Op zoek naar de versie voor Windows?"; + /* Title for the settings subtitle */ "mac-waitlist.settings.browse-privately" = "Browse privé met onze app voor Mac"; @@ -1088,7 +1394,7 @@ "mac-waitlist.share-sheet.title" = "DuckDuckGo voor Mac"; /* Title for the Mac Waitlist feature */ -"mac-waitlist.title" = "DuckDuckGo-bureaubladapp"; +"mac-waitlist.title" = "DuckDuckGo-app voor Mac"; /* No comment provided by engineer. */ "menu.button.hint" = "Optiemenu"; @@ -1105,9 +1411,81 @@ /* Edit button */ "navigation.title.edit" = "Bewerken"; +/* String indicating NetP is connected when viewed from the settings screen */ +"netP.cell.connected" = "Connected"; + +/* String indicating NetP is disconnected when viewed from the settings screen */ +"netP.cell.disconnected" = "Not connected"; + +/* Title for the Network Protection feature */ +"netP.title" = "Network Protection"; + +/* Message for the network protection invite dialog */ +"network.protection.invite.dialog.message" = "Enter your invite code to get started."; + +/* Title for the network protection invite screen */ +"network.protection.invite.dialog.title" = "You're invited to try Network Protection"; + +/* Prompt for the network protection invite code text field */ +"network.protection.invite.field.prompt" = "Invite Code"; + +/* Message explaining that netP is invite only */ +"network.protection.invite.only.message" = "DuckDuckGo Network Protection is currently invite-only."; + +/* Message for the network protection invite success view */ +"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; + +/* Title for the network protection invite success view */ +"network.protection.invite.success.title" = "Success! You’re in."; + +/* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ +"network.protection.status.connected.format" = "Connected - %@"; + +/* The label for the NetP VPN when connecting */ +"network.protection.status.connecting" = "Connecting..."; + +/* The label for the NetP VPN when disconnected */ +"network.protection.status.disconnected" = "Not connected"; + +/* The label for the NetP VPN when disconnecting */ +"network.protection.status.disconnecting" = "Disconnecting..."; + +/* Message label text for the netP status view */ +"network.protection.status.header.message" = "DuckDuckGo's VPN secures all of your device's Internet traffic anytime, anywhere."; + +/* Header title label text for the status view when netP is disconnected */ +"network.protection.status.header.title.off" = "Network Protection is Off"; + +/* Header title label text for the status view when netP is connected */ +"network.protection.status.header.title.on" = "Network Protection is On"; + +/* The status view 'Share Feedback' button which is shown inline on the status view after the \(netPInviteOnlyMessage) text */ +"network.protection.status.menu.share.feedback" = "Share Feedback"; + +/* Connection details label shown in NetworkProtection's status view. */ +"network.protection.status.view.connection.details" = "Connection Details"; + +/* Generic connection failed error message shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.message" = "Please try again later."; + +/* Generic connection failed error title shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; + +/* IP Address label shown in NetworkProtection's status view. */ +"network.protection.status.view.ip.address" = "IP Address"; + +/* Location label shown in NetworkProtection's status view. */ +"network.protection.status.view.location" = "Location"; + +/* Title label text for the status view when netP is disconnected */ +"network.protection.status.view.title" = "Network Protection"; + /* Do not translate - stringsdict entry */ "number.of.tabs" = "number.of.tabs"; +/* No comment provided by engineer. */ +"Ok" = "OK"; + /* Text displayed on notification appearing in the address bar when the browser dismissed the cookie popup automatically rejecting it */ "omnibar.notification.cookies-managed" = "Beheerde cookies"; @@ -1147,6 +1525,18 @@ /* ’Link’ is link on a website */ "open.externally.failed" = "De app die nodig is om die link te openen, is niet gevonden"; +/* No comment provided by engineer. */ +"Please enter which app on your device is broken." = "Voer in welke app op je apparaat niet meer werkt."; + +/* Activate button */ +"pm.activate" = "Reactivate"; + +/* Cancel button */ +"pm.cancel" = "Cancel"; + +/* Deactivate button */ +"pm.deactivate" = "Deactivate"; + /* No comment provided by engineer. */ "preserveLogins.domain.list.footer" = "Websites gebruiken cookies om te zorgen dat je ingelogd blijft. Als je een site brandveilig maakt, worden de cookies niet gewist en blijf je ingelogd, zelfs nadat je de vuurknop hebt gebruikt. Trackers van derden worden ook op brandveilige websites geblokkeerd."; @@ -1216,8 +1606,8 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Favorieten"; -/* No comment provided by engineer. */ -"settings.about.text" = "Bij DuckDuckGo zetten we de nieuwe standaard voor vertrouwen online.\n\nDe DuckDuckGo Privacy Browser biedt alle essentiële facetten van privacy die je nodig hebt om jezelf te beschermen tijdens het zoeken en browsen op het internet, zoals het blokkeren van trackers, slimmere versleuteling en privézoekopdrachten.\n\nHet internet zou namelijk helemaal niet zo griezelig moeten zijn en de privacy die je online verdient, moet net zo eenvoudig zijn als het sluiten van de gordijnen."; +/* about page */ +"settings.about.text" = "DuckDuckGo is het onafhankelijke internetprivacybedrijf, opgericht in 2008, voor iedereen die het beu is om online gevolgd te worden en daar een eenvoudige oplossing voor wil. Wij zijn het bewijs dat je privacy op internet écht beschermd kan worden zonder compromissen.\n\nDe DuckDuckGo-browser wordt geleverd met de functies die je van een webbrowser verwacht, zoals bladwijzers, tabbladen, wachtwoorden en meer, plus meer dan [een dozijn krachtige tools om je privacy te beschermen](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/) die niet standaard worden aangeboden in de meeste populaire browsers. Deze unieke, uitgebreide privacybescherming helpt je online activiteiten te beschermen – of je nu zoekt, surft of mailt.\n\nJe hoeft niets te weten over de technische details of ingewikkelde instellingen om te profiteren van onze privacybescherming. Je hoeft alleen maar DuckDuckGo op al je apparaten te installeren en geniet dan standaard van privacy.\n\nMaar als je *een kijkje onder de motorkap* wilt, vind je meer informatie over de privacybescherming van DuckDuckGo op onze [hulppagina's](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/)."; /* Report a Broken Site screen confirmation button */ "siteFeedback.buttonText" = "Rapport versturen"; @@ -1237,6 +1627,9 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Welke website is defect?"; +/* No comment provided by engineer. */ +"sync.remove-device.message" = "'%@' heeft geen toegang meer tot je gesynchroniseerde gegevens."; + /* Accessibility label on remove button */ "tab.close.home" = "Starttabblad sluiten"; @@ -1309,6 +1702,57 @@ /* Voice-search footer note with on-device privacy warning */ "voiceSearch.footer.note" = "Audio wordt op het apparaat verwerkt. Het wordt niet opgeslagen of met iemand gedeeld, ook niet met DuckDuckGo."; +/* Title for the button to enable push notifications in system settings */ +"waitlist.allow-notifications" = "Meldingen toestaan"; + +/* Body text for the waitlist notification */ +"waitlist.available.notification.body" = "Open je uitnodiging"; + +/* Title for the copy action */ +"waitlist.copy" = "Kopiëren"; + +/* Label text for the invite code */ +"waitlist.invite-code" = "Uitnodigingscode"; + +/* Step title on the invite screen */ +"waitlist.invite-screen.step.title" = "Stap %d"; + +/* Title for the invite code screen */ +"waitlist.invite-screen.youre-invited" = "Je bent uitgenodigd!"; + +/* Title for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.join" = "Schrijf je in voor de privéwachtlijst"; + +/* Temporary status text for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.joining" = "Deelnemen aan wachtlijst ..."; + +/* Title for the Share Link button */ +"waitlist.join-waitlist-screen.share-link" = "Link delen"; + +/* Notification text for the waitlist */ +"waitlist.joined.no-notification.get-notification" = "MELDING STUREN"; + +/* Title for the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.get-notification-confirmation-title" = "Een melding krijgen als je aan de beurt bent?"; + +/* Cancel button in the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.no-thanks" = "Nee, bedankt"; + +/* Text used for the Notifications Disabled state */ +"waitlist.notification.disabled" = "We kunnen je informeren wanneer je aan de beurt bent, maar meldingen zijn momenteel uitgeschakeld voor DuckDuckGo."; + +/* Privacy disclaimer for the Waitlist feature */ +"waitlist.privacy-disclaimer" = "Je hoeft geen persoonsgegevens te delen om op de wachtlijst te komen. Je stelt je plaats veilig met een tijdstempel die alleen op jouw apparaat bestaat. Zo kunnen we je informeren wanneer je aan de beurt bent."; + +/* Title for the queue screen */ +"waitlist.queue-screen.on-the-list" = "Je staat op de lijst!"; + +/* Title for the settings subtitle */ +"waitlist.settings.download-available" = "Download beschikbaar"; + +/* Title for the share sheet entry */ +"waitlist.share-sheet.title" = "Je bent uitgenodigd!"; + /* Confirmation message */ "web.url.remove.favorite.done" = "Favoriet verwijderd"; @@ -1333,3 +1777,51 @@ /* Alert title explaining the message is shown by a website */ "webJSAlert.website-message.format" = "Een bericht van %@:"; +/* No comment provided by engineer. */ +"Welcome to the Duck Side!" = "Welkom bij de Duck kant!"; + +/* Title for the Windows waitlist notification */ +"windows-waitlist.available.notification.title" = "Probeer DuckDuckGo voor Windows!"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-1.description" = "Bezoek deze URL op je Windows-apparaat om te downloaden:"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-2.description" = "Open de DuckDuckGo Installer in Downloads, selecteer Installeren en voer vervolgens je uitnodigingscode in."; + +/* Subtitle for the Windows Waitlist Invite screen */ +"windows-waitlist.invite-screen.subtitle" = "Klaar om DuckDuckGo op Windows te gebruiken?"; + +/* Title for the Windows waitlist button redirecting to Mac waitlist */ +"windows-waitlist.join-waitlist-screen.mac-waitlist" = "Op zoek naar de versie voor Mac?"; + +/* Title for the Join Windows Waitlist screen */ +"windows-waitlist.join-waitlist-screen.try-duckduckgo-for-windows" = "Krijg vroege toegang om DuckDuckGo voor Windows uit te proberen!"; + +/* Message for the alert to confirm enabling notifications */ +"windows-waitlist.joined.no-notification.get-notification-confirmation-message" = "We sturen je een melding wanneer je DuckDuckGo voor Windows kunt downloaden. "; + +/* Label text for the Joined Waitlist state with notifications declined */ +"windows-waitlist.joined.notifications-declined" = "Je vindt hier je uitnodiging om DuckDuckGo voor Windows te proberen. Kom binnenkort terug, of ontvang een melding wanneer jij aan de beurt bent."; + +/* Label text for the Joined Waitlist state with notifications enabled */ +"windows-waitlist.joined.notifications-enabled" = "We sturen je een melding wanneer je DuckDuckGo voor Windows kunt downloaden."; + +/* Title for the settings subtitle */ +"windows-waitlist.settings.browse-privately" = "Browse privé met onze app voor Windows"; + +/* Message used when sharing to iMessage. Parameter is an eight digit invite code. */ +"windows-waitlist.share-sheet.invite-code-message" = "Je bent uitgenodigd!\n\nKlaar om DuckDuckGo op Windows te gebruiken?Stap 1\nBezoek deze URL op je Windows-apparaat om te downloaden:\nhttps://duckduckgo.com/windows\n\nStap 2\nOpen de DuckDuckGo Installer in Downloads, selecteer Installeren en voer vervolgens je uitnodigingscode in.\n\nUitnodigingscode: %@"; + +/* Message used when sharing to iMessage */ +"windows-waitlist.share-sheet.message" = "Klaar om privé te browsen in Windows?\n\nBezoek deze URL op je computer om te downloaden:\nhttps://duckduckgo.com/windows"; + +/* Summary text for the Windows browser waitlist */ +"windows-waitlist.summary" = "DuckDuckGo voor Windows heeft alles wat je nodig hebt om met meer privacy te browsen: privézoekopdrachten, blokkering van trackers, geforceerde versleuteling, blokkering van cookiepop-ups, plus nog meer uitstekende bescherming onderweg."; + +/* Title for the Windows Waitlist feature */ +"windows-waitlist.title" = "DuckDuckGo-app voor Windows"; + +/* Title for the Windows browser download link page */ +"windows-waitlist.waitlist-download-screen.try-duckduckgo-for-windows" = "Download DuckDuckGo voor Windows!"; + diff --git a/DuckDuckGo/nl.lproj/Settings.strings b/DuckDuckGo/nl.lproj/Settings.strings index 74dbff7fd0..96c60260cb 100644 --- a/DuckDuckGo/nl.lproj/Settings.strings +++ b/DuckDuckGo/nl.lproj/Settings.strings @@ -37,6 +37,9 @@ /* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ "9Ko-0g-T3h.text" = "Lettergrootte"; +/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ +"9kt-6R-XiZ.text" = "Titel"; + /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Starten van app"; @@ -73,6 +76,12 @@ /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Global Privacy Control (GPC)"; +/* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ +"CxT-QK-iVn.title" = "Toevoegen"; + +/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ +"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; + /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "App afsluiten, 15 minuten inactief"; @@ -151,6 +160,9 @@ /* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ "HLr-R8-xxF.text" = "Voorbeeldweergave bij lang indrukken"; +/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ +"hoT-Nu-KXP.text" = "Browse privé met onze app voor Windows"; + /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Privacybescherming ingeschakeld voor alle sites"; @@ -163,6 +175,12 @@ /* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ "m23-t6-9cb.text" = "Feedback delen"; +/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ +"mLn-1x-Fl5.title" = "Positie van adresbalk"; + +/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ +"mqV-pf-NZ1.text" = "Positie van adresbalk"; + /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Laat DuckDuckGo pop-ups voor cookietoestemming beheren"; @@ -175,15 +193,27 @@ /* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ "oM7-1o-9oY.text" = "Over DuckDuckGo"; +/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ +"opn-JO-idF.text" = "Boven"; + /* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ "OxE-MQ-uJk.headerTitle" = "Meer over DuckDuckGo"; +/* Class = "UILabel"; text = "Sync"; ObjectID = "oXN-ez-gct"; */ +"oXN-ez-gct.text" = "Synchroniseren"; + /* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ "P0F-ts-ekd.text" = "Browse privé met onze app voor Mac "; +/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ +"qah-gb-udB.text" = "Network Protection"; + /* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ "qeN-SV-zy7.text" = "Label"; +/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ +"RQ8-H1-Ez1.text" = "DuckDuckGo-app voor Windows"; + /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Lettergrootte"; @@ -232,8 +262,8 @@ /* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ "yvj-LL-MiR.text" = "App toevoegen aan je dock"; -/* Class = "UILabel"; text = "DuckDuckGo Desktop App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "DuckDuckGo-bureaubladapp"; +/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ +"Yz9-17-qnn.text" = "DuckDuckGo-app voor Mac"; /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Nieuw tabblad"; diff --git a/DuckDuckGo/pl.lproj/Feedback.strings b/DuckDuckGo/pl.lproj/Feedback.strings index 8c3b3f913a..020beeb122 100644 --- a/DuckDuckGo/pl.lproj/Feedback.strings +++ b/DuckDuckGo/pl.lproj/Feedback.strings @@ -28,9 +28,6 @@ /* Class = "UIBarButtonItem"; title = "Back"; ObjectID = "JF8-Ow-zXL"; */ "JF8-Ow-zXL.title" = "Wstecz"; -/* Class = "UIBarButtonItem"; title = "Submit"; ObjectID = "jr6-Al-4fr"; */ -"jr6-Al-4fr.title" = "Prześlij"; - /* Class = "UIButton"; normalTitle = "Submit"; ObjectID = "lsB-Kq-U8p"; */ "lsB-Kq-U8p.normalTitle" = "Prześlij"; @@ -58,9 +55,6 @@ /* Class = "UINavigationItem"; title = "Report Broken Site"; ObjectID = "WjT-HM-yim"; */ "WjT-HM-yim.title" = "Zgłoś uszkodzoną witrynę"; -/* Class = "UILabel"; text = "Broken Site Reason"; ObjectID = "Wx3-Vb-ItS"; */ -"Wx3-Vb-ItS.text" = "Przyczyna uszkodzenia witryny"; - /* Class = "UINavigationItem"; title = "Share Feedback"; ObjectID = "Xb0-Zu-opM"; */ "Xb0-Zu-opM.title" = "Podziel się opinią"; diff --git a/DuckDuckGo/pl.lproj/Localizable.strings b/DuckDuckGo/pl.lproj/Localizable.strings index e1c239da53..a9577094e3 100644 --- a/DuckDuckGo/pl.lproj/Localizable.strings +++ b/DuckDuckGo/pl.lproj/Localizable.strings @@ -1,6 +1,9 @@ /* No comment provided by engineer. */ "%@" = "%@"; +/* No comment provided by engineer. */ +"%@ [%@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)" = "%1$@ [%2$@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)"; + /* Buton label for Edit action */ "action.generic.edit" = "Edytuj"; @@ -103,6 +106,12 @@ /* Share action */ "action.title.share" = "Udostępnij"; +/* Settings label for bottom position for the address bar */ +"address.bar.bottom" = "Dół"; + +/* Settings label for top position for the address bar */ +"address.bar.top" = "Do góry"; + /* No comment provided by engineer. */ "addWidget.button" = "Dodaj widżet"; @@ -142,6 +151,225 @@ /* Shown on authentication screen */ "app.authentication.unlock" = "Odblokuj DuckDuckGo."; +/* First part of about page content (note the trailing space) */ +"appTP.about.content1" = "You’ve probably heard about companies like Google and Facebook tracking you behind the scenes on third-party websites. But did you know they also track your personal information through apps on your device?\n\nIn 2022, DuckDuckGo found that "; + +/* Second part of about page content (note the trailing space) */ +"appTP.about.content2" = "over 85% of free iOS apps tested contained hidden trackers from other companies."; + +/* Third part of about page content (note the leading space) */ +"appTP.about.content3" = " Of the 395 apps tested, 60% sent data to Google. This happens even while you’re not using your device.\n\nTrackers in apps may have access to a lot more information than their website tracker cousins, such as your location down to which floor of a building you're on, how often you play games while at work, and when and how long you sleep each day. Even if you haven’t given apps explicit permission to collect data, they can still take it without your knowledge.\n\nTracking networks like Facebook and Google use these little pieces of information to build a digital profile about you. With it, tracking networks can manipulate what you see online and allow advertisers to bid on access to you based on your data.\n\nTrackers in apps is a BIG problem for privacy. But DuckDuckGo has a solution that can help.\n\nWhen enabled in the DuckDuckGo Privacy Browser app, App Tracking Protection blocks many trackers in other apps, not just the trackers we find on websites when you browse. These dual layers of protection reduce what companies know about you overall, so you can use your apps with more peace of mind, knowing you’re more protected."; + +/* Navigation Title for AppTP about page */ +"appTP.about.navTitle" = "About App Trackers"; + +/* Title for AppTP about page */ +"appTP.about.title" = "What Are App Trackers?"; + +/* Title for 'Report an Issue' button in the activity view. */ +"appTP.activityView.reportIssue" = "Report Issue"; + +/* Text label for switch that turns blocking on or off for a tracker */ +"appTP.blockTrackerText" = "Block this Tracker"; + +/* Detail string describing what AppTP is */ +"appTP.cell.detail" = "Blokowanie skryptów śledzących aplikacji na urządzeniu"; + +/* String indicating AppTP is disabled when viewed from the settings screen */ +"appTP.cell.disabled" = "Wyłączone"; + +/* String indicating AppTP is enabled when viewed from the settings screen */ +"appTP.cell.enabled" = "Włączone"; + +/* Info string informing the user what App Tracking Protection does. */ +"appTP.empty.disabled.info" = "Włącz ochronę przed śledzeniem w aplikacjach, aby zablokować natrętne mechanizmy śledzące w innych aplikacjach."; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.heading" = "We’re blocking hidden trackers"; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.info" = "Come back soon to see a list of all the app trackers we’ve blocked."; + +/* First answer for AppTP FAQ page */ +"appTP.faq.answer1" = "App Tracking Protection blocks app trackers from other companies, like when Facebook tries to track you in a banking app. Companies may still track you in apps they own."; + +/* Second answer for AppTP FAQ page */ +"appTP.faq.answer2" = "Yes! App Tracking Protection works across all apps on your device to block the most common hidden trackers we find trying to collect your personal info."; + +/* Third answer for AppTP FAQ page */ +"appTP.faq.answer3" = "We currently only block the most common trackers that we find on iOS. This helps us to comprehensively test App Tracking Protection and lower frequency of app breakage, while blocking up to 70% of all tracking requests."; + +/* Fourth answer for AppTP FAQ page */ +"appTP.faq.answer4" = "You’ll be asked to set up a virtual private network (VPN) connection, but you don't need to install a VPN app for App Tracking Protection to work.\n\nThis permission, which works only on your device, allows App Tracking Protection to monitor network traffic so that it can block known trackers."; + +/* Fifth answer for AppTP FAQ page */ +"appTP.faq.answer5" = "You can use App Tracking Protection at the same time as using an IKEv2 protocol VPN app on an iOS device. You won’t be able to use App Tracking Protection on an iOS device if you’re using a VPN app that uses a different type of protocol, like WireGuard or OpenVPN type VPNs."; + +/* Sixth answer for AppTP FAQ page */ +"appTP.faq.answer6" = "A VPN sends your data from the device to its own server, where it secures and anonymizes your data from prying eyes. However, this allows the VPN company to see your network traffic.\n\nApp Tracking Protection is different. Instead of sending your data to a VPN server, App Tracking Protection works only on your device, sitting between your apps and the servers they talk to.\n\nWhenever App Tracking Protection recognizes a known tracker, it blocks the tracker from sending personal information (such as your IP address, activity, and device details) off your device. All other traffic reaches its destination, so your apps work normally."; + +/* Seventh answer for AppTP FAQ page */ +"appTP.faq.answer7" = "App Tracking Protection works only on your device and doesn’t send your data off your device to DuckDuckGo. We don’t collect or store any data from your apps."; + +/* First question for AppTP FAQ page */ +"appTP.faq.question1" = "How does App Tracking Protection work?"; + +/* Second question for AppTP FAQ page */ +"appTP.faq.question2" = "Does App Tracking Protection block trackers in all apps on my device?"; + +/* Third question for AppTP FAQ page */ +"appTP.faq.question3" = "Does App Tracking Protection block all app trackers?"; + +/* Fourth question for AppTP FAQ page */ +"appTP.faq.question4" = "Why does App Tracking Protection use a VPN connection?"; + +/* Fifth question for AppTP FAQ page */ +"appTP.faq.question5" = "Will App Tracking Protection work if I also use a VPN app?"; + +/* Sixth question for AppTP FAQ page */ +"appTP.faq.question6" = "How is App Tracking Protection different from a VPN?"; + +/* Seventh question for AppTP FAQ page */ +"appTP.faq.question7" = "Is my data private?"; + +/* Title for AppTP FAQ page */ +"appTP.faq.title" = "App Tracking Protection FAQ"; + +/* Do not translate. StringsDict entry -- Count part of string 'App Tracking Protection blocked x tracking attempts today' */ +"appTP.home.blockedCount" = "appTP.home.blockedCount"; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the trailing space) */ +"appTP.home.blockedPrefix" = "Ochrona przed śledzeniem w aplikacji zablokowana "; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the leading space) */ +"appTP.home.blockedSuffix" = " w Twoich aplikacjach dzisiaj."; + +/* Prefix of string 'App Tracking Protection disabled. Tap to re-enable.' (note the trailing space) */ +"appTP.home.disabledPrefix" = "App Tracking Protection disabled. "; + +/* Suffix of string 'App Tracking Protection disabled. Tap to re-enable.' */ +"appTP.home.disabledSuffix" = "Tap to continue blocking tracking attempts across your apps."; + +/* Text indicating the tracking event occured 'just now'. Example: Last attempt 'just now' */ +"appTP.justNow" = "Teraz"; + +/* View to manage trackers for AppTP. Allows the user to turn trackers on or off. */ +"appTP.manageTrackers" = "Zarządzaj mechanizmami śledzącymi"; + +/* Button title for AppTP onboarding */ +"appTP.onboarding.continueButton" = "Continue"; + +/* Button title for AppTP onboarding to enable AppTP */ +"appTP.onboarding.enableeButton" = "Enable App Tracking Protection"; + +/* Button title for AppTP onboarding to learn more about AppTP */ +"appTP.onboarding.learnMoreButton" = "Learn More"; + +/* First part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info1" = "Over 85% of free iOS apps"; + +/* Second part of info on the first AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page1Info2" = " we’ve tested allow other companies to track your personal information, even when you’re sleeping."; + +/* Third part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info3" = "See who we catch trying to track you in your apps and take back control."; + +/* First part of info on the second AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page2Info1" = "App Tracking Protection "; + +/* Second part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info2" = "detects and blocks app trackers from other companies,"; + +/* Third part of info on the second AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page2Info3" = " like when Google attempts to track you in a health app."; + +/* Fourth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info4" = "It’s free,"; + +/* Fifth part of info on the second AppTP onboarding page (note the leading and trailing space) */ +"appTP.onboarding.page2Info5" = " and you can enjoy your apps as you normally would. Working in the background, it helps "; + +/* Sixth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info6" = "protect you night and day."; + +/* First part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info1" = "App Tracking Protection is not a VPN."; + +/* Second part of info on the third AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page3Info2" = " However, your device will recognize it as one. This is because it uses a local VPN connection to work."; + +/* Third part of info on the third AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page3Info3" = "App Tracking Protection is different. "; + +/* Fourth part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info4" = "It never routes app data through an external server."; + +/* Title for first AppTP onboarding page */ +"appTP.onboarding.title1" = "One easy step for better app privacy!"; + +/* Title for second AppTP onboarding page */ +"appTP.onboarding.title2" = "How does it work?"; + +/* Title for third AppTP onboarding page */ +"appTP.onboarding.title3" = "Who sees your data?"; + +/* Breakage report app name label */ +"appTP.report.appLabel" = "Which app is having issues?"; + +/* Breakage report app name placeholder */ +"appTP.report.appPlaceholder" = "App name"; + +/* Breakage report category label */ +"appTP.report.categoryLabel" = "Co się dzieje?"; + +/* Breakage report comment label */ +"appTP.report.commentLabel" = "Comments"; + +/* Breakage report comment placeholder */ +"appTP.report.commentPlaceholder" = "Add additional details"; + +/* Breakage report footer explaining what is collected in the breakage report */ +"appTP.report.footer" = "In addition to the details entered into this form, your app issue report will contain:\n• A list of trackers blocked in the last 10 min\n• Whether App Tracking Protection is enabled\n• Aggregate DuckDuckGo app diagnostics"; + +/* Breakage report submit button */ +"appTP.report.submit" = "Prześlij"; + +/* Breakage report form title */ +"appTP.report.title" = "Report Issue"; + +/* Breakage report succcess message */ +"appTP.report.toast" = "Dziękujemy! Informacje zostały przekazane."; + +/* Cancel button for 'Report an Issue' alert. */ +"appTP.reportAlert.cancel" = "Nie teraz"; + +/* Confirm button for 'Report an Issue' alert. */ +"appTP.reportAlert.confirm" = "Zgłoś błąd"; + +/* Message for 'Report an Issue' alert. */ +"appTP.reportAlert.message" = "Let us know if you disabled App Tracking Protection for this specific tracker because it caused app issues. Your feedback helps us improve!"; + +/* Title for 'Report an Issue' alert. */ +"appTP.reportAlert.title" = "Report Issue?"; + +/* Toast notification diplayed after restoring the blocklist to default settings */ +"appTP.restoreDefaultsToast" = "Default settings restored"; + +/* Button to restore the blocklist to its default state. */ +"appTP.restoreDefualts" = "Przywróć ustawienia domyślne"; + +/* Title for the App Tracking Protection feature */ +"appTP.title" = "Ochrona przed śledzeniem przez aplikacje"; + +/* Text indicating when the tracker was last allowed. Example: Last attempt allowed (timeString) */ +"appTP.trackerAllowedTimestamp" = "Ostatnia dopuszczona próba: %@"; + +/* Text indicating when the tracker was last blocked. Example: Last attempt blocked (timeString) */ +"appTP.trackerBlockedTimestamp" = "Ostatnia zablokowana próba: % @"; + +/* Do not translate. StringsDict entry -- Subtitle for tracking attempts in App Tracking Protection Activity View. Example: (count) tracking attempts */ +"appTP.trackingattempts" = "appTP.trackingattempts"; + /* Authentication Alert Sign In Button */ "auth.alert.login.button" = "Zaloguj się"; @@ -281,7 +509,7 @@ "autofill.logins.empty-view.subtitle" = "Dane logowania są bezpiecznie przechowywane na Twoim urządzeniu."; /* Title for view displayed when autofill has no items */ -"autofill.logins.empty-view.title" = "Nie zapisano jeszcze żadnych loginów."; +"autofill.logins.empty-view.title" = "Nie zapisano jeszcze żadnych loginów"; /* Cancel button for auth when opening login list */ "autofill.logins.list.auth.cancel" = "Anuluj"; @@ -301,6 +529,18 @@ /* Toast message when a login item without a title is deleted */ "autofill.logins.list.login-deleted-message-no-title" = "Login usunięty"; +/* Title for a button that allows a user to reset their list of never saved sites */ +"autofill.logins.list.never.saved" = "Resetowanie wykluczonych witryn"; + +/* Cancel button for resetting list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.cancel" = "Anuluj"; + +/* Confirm button to reset list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.confirm" = "Resetowanie wykluczonych witryn"; + +/* Alert title */ +"autofill.logins.list.never.saved.reset.action.title" = "Jeśli zresetujesz wykluczone witryny, przy następnym logowaniu do dowolnej z nich pojawi się monit o zapisanie danych logowania."; + /* Placeholder for search field on autofill login listing */ "autofill.logins.list.search-placeholder" = "Wyszukiwanie loginów"; @@ -335,7 +575,7 @@ "autofill.logins.prompt.password.button.title" = "Hasło %@"; /* Title for autofill login prompt */ -"autofill.logins.prompt.title" = "Czy chcesz użyć zapisanego loginu?"; +"autofill.logins.prompt.title" = "Użyć zapisanego hasła?"; /* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ "autofill.logins.search.no-results.subtitle" = "dla frazy: %@"; @@ -385,27 +625,24 @@ /* Title for the alert dialog telling the user an updated username is no longer a private email address */ "autofill.removed.duck.address.title" = "Nazwa użytkownika prywatnego adresu Duck Address została usunięta"; +/* CTA displayed on modal asking if the user never wants to be prompted to save a login for this website agin */ +"autofill.save-login.never-prompt.CTA" = "Nigdy nie pytaj o tę witrynę"; + /* Message displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.message" = "Loginy są bezpiecznie przechowywane na Twoim urządzeniu w menu Loginy."; +"autofill.save-login.new-user.message" = "Hasła są bezpiecznie przechowywane na Twoim urządzeniu w menu Loginy."; /* Title displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.title" = "Czy chcesz zapisać dane logowania w DuckDuckGo?"; +"autofill.save-login.new-user.title" = "Czy chcesz zapisać hasło w DuckDuckGo?"; /* Cancel CTA displayed on modal asking for the user to save the login */ "autofill.save-login.not-now.CTA" = "Nie zapisuj"; -/* Confirm CTA displayed on modal asking for the user to save the login */ -"autofill.save-login.save.CTA" = "Zapisz logowanie"; - /* Title displayed on modal asking for the user to save the login */ "autofill.save-login.title" = "Zapisać logowanie?"; /* Confirm CTA displayed on modal asking for the user to save the password */ "autofill.save-password.save.CTA" = "Zapisz hasło"; -/* Title displayed on modal asking for the user to save the password */ -"autofill.save-password.title" = "Zapisać hasło?"; - /* Accessibility title for a Show Password button displaying actial password instead of ***** */ "autofill.show-password" = "Pokaż hasło"; @@ -413,7 +650,7 @@ "autofill.signin.to.manage" = "%@, aby zarządzać adresami Duck Address na tym urządzeniu."; /* Message displayed on modal asking for the user to update the password */ -"autofill.update-password.message" = "DuckDuckGo zaktualizuje ten zapisany login na Twoim urządzeniu."; +"autofill.update-password.message" = "DuckDuckGo zaktualizuje to zapisane hasło na Twoim urządzeniu."; /* Confirm CTA displayed on modal asking for the user to update the password */ "autofill.update-password.save.CTA" = "Aktualizuj hasło"; @@ -442,6 +679,15 @@ /* Placeholder in the add bookmark form */ "bookmark.address.placeholder" = "www.domena.pl"; +/* Delete bookmark alert message */ +"bookmark.delete.alert.message" = "Spowoduje to usunięcie zakładki „%@”"; + +/* Delete bookmark alert title */ +"bookmark.delete.alert.title" = "Delete?"; + +/* The message shown after a bookmark has been deleted */ +"bookmark.deleted.toast" = "Usunięto zakładkę"; + /* Delete bookmark folder alert delete button */ "bookmark.deleteFolderAlert.deleteButton" = "Usuń"; @@ -538,9 +784,21 @@ /* Broken Site Category */ "brokensite.category.videos" = "Film nie został odtworzony"; +/* Broken Site Category Placeholder */ +"brokensite.categoryPlaceholder" = "Wybierz problem z listy..."; + +/* Broken Site Category Section Title */ +"brokensite.categoryTitle" = "WYBIERZ KATEGORIĘ"; + +/* Broken Site Comment Placeholder */ +"brokensite.commentPlaceholder" = "Udostępnienie szczegółowych informacji może pomóc nam rozwiązać ten problem"; + /* Broken Site Section Title */ "brokensite.sectionTitle" = "OPISZ, CO SIĘ STAŁO"; +/* No comment provided by engineer. */ +"bucket: %@" = "Zbiór: %@"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Ostatni miesiąc"; @@ -772,6 +1030,39 @@ /* Empty search placeholder on bookmarks search */ "empty.search" = "Brak pasujących wyników"; +/* No comment provided by engineer. */ +"Error" = "Błąd"; + +/* Button title to Sign In */ +"error.email-protection-sign-in.action" = "Zaloguj się"; + +/* Alert message */ +"error.email-protection-sign-in.body" = "Zaloguj się jeszcze raz, aby ponownie włączyć funkcje ochrony poczty Email Protection w tej przeglądarce."; + +/* Alert title */ +"error.email-protection-sign-in.title" = "Błąd ochrony poczty Email Protection"; + +/* Button title to open device settings */ +"error.insufficient-disk-space.action" = "Otwórz ustawienia"; + +/* Alert message */ +"error.insufficient-disk-space.body" = "Wygląda na to, że na urządzeniu zabrakło miejsca. Zwolnij miejsce, aby kontynuować."; + +/* Alert title */ +"error.insufficient-disk-space.title" = "Za mało miejsca"; + +/* Button title that is shutting down the app */ +"error.preemptive-crash.action" = "Zamknij aplikację"; + +/* Alert message */ +"error.preemptive-crash.body" = "Wygląda na to, że wystąpił problem z aplikacją i musi zostać zamknięta. Aby kontynuować, otwórz ją ponownie."; + +/* Alert title */ +"error.preemptive-crash.title" = "Wykryto błąd aplikacji"; + +/* Generic error message on a dialog for when the cause is not known. */ +"error.unknown.try.again" = "An unknown error has occurred"; + /* No comment provided by engineer. */ "favorite" = "Ulubione"; @@ -991,6 +1282,18 @@ /* Home tab title */ "homeTab.title" = "Strona główna"; +/* OK title for invite screen alert dismissal button */ +"invite.alert.ok.button" = "OK"; + +/* Continue button on an invite dialog */ +"invite.dialog.continue.button" = "Continue"; + +/* Get Started button on an invite dialog */ +"invite.dialog.get.started.button" = "Get Started"; + +/* Message to show after user enters an unrecognized invite code */ +"invite.dialog.unrecognized.code.message" = "We didn’t recognize this Invite Code."; + /* No comment provided by engineer. */ "keyCommandAddBookmark" = "Dodaj zakładkę"; @@ -1057,15 +1360,15 @@ /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "Aplikacja DuckDuckGo dla komputerów Mac zapewnia szybkość, której potrzebujesz, i niezbędne funkcje przeglądania. Ponadto jest wyposażona w najlepsze w swojej klasie narzędzia do ochrony prywatności."; -/* Body text for the macOS waitlist notification */ -"mac-waitlist.available.notification.body" = "Otwórz zaproszenie"; - /* Title for the macOS waitlist notification */ "mac-waitlist.available.notification.title" = "Przeglądarka DuckDuckGo dla komputerów Mac jest gotowa!"; /* Title for the copy action */ "mac-waitlist.copy" = "Kopiuj"; +/* Description text above the Share Link button */ +"mac-waitlist.join-waitlist-screen.on-your-computer-go-to" = "Na komputerze z systemem Windows przejdź do:"; + /* Description text above the Share Link button */ "mac-waitlist.join-waitlist-screen.on-your-mac-go-to" = "Na komputerze Mac przejdź do:"; @@ -1078,6 +1381,9 @@ /* Disclaimer for the Join Waitlist screen */ "mac-waitlist.join-waitlist-screen.windows" = "Już wkrótce!"; +/* Title for the macOS waitlist button redirecting to Windows waitlist */ +"mac-waitlist.join-waitlist-screen.windows-waitlist" = "Szukasz wersji dla systemu Windows?"; + /* Title for the settings subtitle */ "mac-waitlist.settings.browse-privately" = "Przeglądaj prywatnie za pomocą aplikacji dla komputerów Mac"; @@ -1088,7 +1394,7 @@ "mac-waitlist.share-sheet.title" = "DuckDuckGo na komputery Mac"; /* Title for the Mac Waitlist feature */ -"mac-waitlist.title" = "Aplikacja komputerowa DuckDuckGo"; +"mac-waitlist.title" = "Aplikacja DuckDuckGo dla komputerów Mac"; /* No comment provided by engineer. */ "menu.button.hint" = "Dostępne opcje"; @@ -1105,9 +1411,81 @@ /* Edit button */ "navigation.title.edit" = "Edytuj"; +/* String indicating NetP is connected when viewed from the settings screen */ +"netP.cell.connected" = "Connected"; + +/* String indicating NetP is disconnected when viewed from the settings screen */ +"netP.cell.disconnected" = "Not connected"; + +/* Title for the Network Protection feature */ +"netP.title" = "Network Protection"; + +/* Message for the network protection invite dialog */ +"network.protection.invite.dialog.message" = "Enter your invite code to get started."; + +/* Title for the network protection invite screen */ +"network.protection.invite.dialog.title" = "You're invited to try Network Protection"; + +/* Prompt for the network protection invite code text field */ +"network.protection.invite.field.prompt" = "Invite Code"; + +/* Message explaining that netP is invite only */ +"network.protection.invite.only.message" = "DuckDuckGo Network Protection is currently invite-only."; + +/* Message for the network protection invite success view */ +"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; + +/* Title for the network protection invite success view */ +"network.protection.invite.success.title" = "Success! You’re in."; + +/* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ +"network.protection.status.connected.format" = "Connected - %@"; + +/* The label for the NetP VPN when connecting */ +"network.protection.status.connecting" = "Connecting..."; + +/* The label for the NetP VPN when disconnected */ +"network.protection.status.disconnected" = "Not connected"; + +/* The label for the NetP VPN when disconnecting */ +"network.protection.status.disconnecting" = "Disconnecting..."; + +/* Message label text for the netP status view */ +"network.protection.status.header.message" = "DuckDuckGo's VPN secures all of your device's Internet traffic anytime, anywhere."; + +/* Header title label text for the status view when netP is disconnected */ +"network.protection.status.header.title.off" = "Network Protection is Off"; + +/* Header title label text for the status view when netP is connected */ +"network.protection.status.header.title.on" = "Network Protection is On"; + +/* The status view 'Share Feedback' button which is shown inline on the status view after the \(netPInviteOnlyMessage) text */ +"network.protection.status.menu.share.feedback" = "Share Feedback"; + +/* Connection details label shown in NetworkProtection's status view. */ +"network.protection.status.view.connection.details" = "Connection Details"; + +/* Generic connection failed error message shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.message" = "Please try again later."; + +/* Generic connection failed error title shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; + +/* IP Address label shown in NetworkProtection's status view. */ +"network.protection.status.view.ip.address" = "IP Address"; + +/* Location label shown in NetworkProtection's status view. */ +"network.protection.status.view.location" = "Location"; + +/* Title label text for the status view when netP is disconnected */ +"network.protection.status.view.title" = "Network Protection"; + /* Do not translate - stringsdict entry */ "number.of.tabs" = "number.of.tabs"; +/* No comment provided by engineer. */ +"Ok" = "OK"; + /* Text displayed on notification appearing in the address bar when the browser dismissed the cookie popup automatically rejecting it */ "omnibar.notification.cookies-managed" = "Zarządzane pliki cookie"; @@ -1147,6 +1525,18 @@ /* ’Link’ is link on a website */ "open.externally.failed" = "Nie można znaleźć aplikacji wymaganej do otwarcia tego linku"; +/* No comment provided by engineer. */ +"Please enter which app on your device is broken." = "Podaj, która aplikacja znajdująca się na Twoim urządzeniu uległa uszkodzeniu."; + +/* Activate button */ +"pm.activate" = "Reactivate"; + +/* Cancel button */ +"pm.cancel" = "Cancel"; + +/* Deactivate button */ +"pm.deactivate" = "Deactivate"; + /* No comment provided by engineer. */ "preserveLogins.domain.list.footer" = "W witrynach internetowych są wykorzystywane pliki cookie. Gdy użyjesz w witrynie funkcji zabezpieczenia, pliki cookie nie zostaną usunięte i pozostaniesz zalogowany(-a) nawet po użyciu przycisku Zabezpiecz. Nadal blokujemy narzędzia śledzące innych firm znalezione w zabezpieczonych witrynach."; @@ -1216,8 +1606,8 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Ulubione"; -/* No comment provided by engineer. */ -"settings.about.text" = "DuckDuckGo ustanawia nowy standard zaufania w sieci.\n\nPrzeglądarka DuckDuckGo Privacy Browser zapewnia wszelkie niezbędne rozwiązania w zakresie prywatności, które gwarantują ochronę podczas wyszukiwania i przeglądania treści w sieci, między innymi blokowanie skryptów śledzących, bardziej inteligentne szyfrowanie oraz prywatne wyszukiwanie DuckDuckGo.\n\nW końcu w Internecie nie wszystko powinno być widoczne, a uzyskanie niezbędnej prywatności powinno być tak proste jak zasunięcie rolety."; +/* about page */ +"settings.about.text" = "DuckDuckGo to założona w 2008 roku niezależna firma zajmująca się prywatnością w Internecie dla osób, które mają dosyć śledzenia online i poszukują łatwego w obsłudze rozwiązania. Jesteśmy dowodem na to, że możesz uzyskać prawdziwą ochronę prywatności online bez kompromisów.\n\nPrzeglądarka DuckDuckGo jest wyposażona w funkcje, których potrzebujesz, takie jak zakładki, karty, hasła i inne, a także [kilkanaście zaawansowanych mechanizmów ochrony prywatności](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/), które nie są domyślnie oferowane w większości popularnych przeglądarek. Ten wyjątkowo zaawansowany zestaw mechanizmów ochrony prywatności pomaga chronić działania w Internecie, od wyszukiwania po przeglądanie, obsługę wiadomości e-mail i nie tylko.\n\nDo korzystania z mechanizmów ochrony prywatności nie jest wymagana znajomość szczegółów technicznych ani skomplikowane ustawienia. Aby uzyskać ochronę prywatności, wystarczy ustawić na wszystkich urządzeniach domyślną przeglądarkę DuckDuckGo.\n\nJeśli interesują Cię szczegóły techniczne, więcej informacji o działaniu mechanizmów ochrony prywatności DuckDuckGo można znaleźć na [stronach pomocy](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/)."; /* Report a Broken Site screen confirmation button */ "siteFeedback.buttonText" = "Prześlij raport"; @@ -1237,6 +1627,9 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Która witryna nie działa poprawnie?"; +/* No comment provided by engineer. */ +"sync.remove-device.message" = "„%@” nie będzie już mieć dostępu do zsynchronizowanych danych."; + /* Accessibility label on remove button */ "tab.close.home" = "Zamknij kartę główną"; @@ -1309,6 +1702,57 @@ /* Voice-search footer note with on-device privacy warning */ "voiceSearch.footer.note" = "Dźwięk jest przetwarzany na urządzeniu. Dane nie są przechowywane ani udostępniane żadnym podmiotom, w tym DuckDuckGo."; +/* Title for the button to enable push notifications in system settings */ +"waitlist.allow-notifications" = "Zezwalaj na powiadomienia"; + +/* Body text for the waitlist notification */ +"waitlist.available.notification.body" = "Otwórz zaproszenie"; + +/* Title for the copy action */ +"waitlist.copy" = "Kopiuj"; + +/* Label text for the invite code */ +"waitlist.invite-code" = "Kod zaproszenia"; + +/* Step title on the invite screen */ +"waitlist.invite-screen.step.title" = "Krok %d"; + +/* Title for the invite code screen */ +"waitlist.invite-screen.youre-invited" = "Masz zaproszenie!"; + +/* Title for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.join" = "Dołącz do prywatnej listy oczekujących"; + +/* Temporary status text for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.joining" = "Dołączanie do listy oczekujących..."; + +/* Title for the Share Link button */ +"waitlist.join-waitlist-screen.share-link" = "Udostępnij link"; + +/* Notification text for the waitlist */ +"waitlist.joined.no-notification.get-notification" = "POWIADOM MNIE"; + +/* Title for the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.get-notification-confirmation-title" = "Chcesz otrzymać powiadomienie, gdy przyjdzie na to pora?"; + +/* Cancel button in the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.no-thanks" = "Nie, dziękuję"; + +/* Text used for the Notifications Disabled state */ +"waitlist.notification.disabled" = "Możemy powiadomić Cię, gdy nadejdzie Twoja kolej, ale powiadomienia są obecnie wyłączone dla DuckDuckGo."; + +/* Privacy disclaimer for the Waitlist feature */ +"waitlist.privacy-disclaimer" = "Nie musisz udostępniać żadnych danych osobowych, aby dołączyć do listy oczekujących. Miejsce w kolejce zostanie zarezerwowane zgodnie z sygnaturą czasową przechowywaną wyłącznie na Twoim urządzeniu, aby umożliwić nam powiadomienie, gdy nadejdzie Twoja kolej."; + +/* Title for the queue screen */ +"waitlist.queue-screen.on-the-list" = "Jesteś na liście!"; + +/* Title for the settings subtitle */ +"waitlist.settings.download-available" = "Możliwość pobrania"; + +/* Title for the share sheet entry */ +"waitlist.share-sheet.title" = "Masz zaproszenie!"; + /* Confirmation message */ "web.url.remove.favorite.done" = "Usunięto ulubione"; @@ -1333,3 +1777,51 @@ /* Alert title explaining the message is shown by a website */ "webJSAlert.website-message.format" = "Wiadomość od %@:"; +/* No comment provided by engineer. */ +"Welcome to the Duck Side!" = "Witaj po Kaczej Stronie Mocy!"; + +/* Title for the Windows waitlist notification */ +"windows-waitlist.available.notification.title" = "Wypróbuj DuckDuckGo dla systemu Windows."; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-1.description" = "Wejdź pod ten adres na urządzeniu z systemem Windows, aby pobrać:"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-2.description" = "Uruchom plik instalacyjny DuckDuckGo znajdujący się w folderze z pobranymi plikami, wybierz opcję „Instaluj”, a następnie wprowadź swój kod zaproszenia."; + +/* Subtitle for the Windows Waitlist Invite screen */ +"windows-waitlist.invite-screen.subtitle" = "Chcesz zacząć korzystać z DuckDuckGo na urządzeniu z systemem Windows?"; + +/* Title for the Windows waitlist button redirecting to Mac waitlist */ +"windows-waitlist.join-waitlist-screen.mac-waitlist" = "Szukasz wersji na komputer Mac?"; + +/* Title for the Join Windows Waitlist screen */ +"windows-waitlist.join-waitlist-screen.try-duckduckgo-for-windows" = "Uzyskaj możliwość wcześniejszego wypróbowania DuckDuckGo dla systemu Windows!"; + +/* Message for the alert to confirm enabling notifications */ +"windows-waitlist.joined.no-notification.get-notification-confirmation-message" = "Wyślemy Ci powiadomienie, gdy Twoja kopia DuckDuckGo dla komputerów z systemem Windows będzie gotowa do pobrania. "; + +/* Label text for the Joined Waitlist state with notifications declined */ +"windows-waitlist.joined.notifications-declined" = "Tutaj pojawi się Twoje zaproszenie do wypróbowania DuckDuckGo dla komputerów z systemem Windows. Zajrzyj tu wkrótce, możemy też wysłać Ci powiadomienie, gdy nadejdzie Twoja kolej."; + +/* Label text for the Joined Waitlist state with notifications enabled */ +"windows-waitlist.joined.notifications-enabled" = "Wyślemy Ci powiadomienie, gdy Twoja kopia DuckDuckGo dla komputerów z systemem Windows będzie gotowa do pobrania."; + +/* Title for the settings subtitle */ +"windows-waitlist.settings.browse-privately" = "Przeglądaj prywatnie za pomocą naszej aplikacji dla systemu Windows"; + +/* Message used when sharing to iMessage. Parameter is an eight digit invite code. */ +"windows-waitlist.share-sheet.invite-code-message" = "Masz zaproszenie!\n\nChcesz zacząć korzystać z DuckDuckGo na urządzeniu z systemem Windows?\n\nKrok 1\nOdwiedź ten adres na swoim urządzeniu z systemem Windows, aby pobrać aplikację: \nhttps://duckduckgo.com/windows\n\nKrok 2\nUruchom plik instalacyjny DuckDuckGo znajdujący się w folderze z pobranymi plikami, wybierz opcję „Instaluj”, a następnie wprowadź swój kod zaproszenia.\n\nKod zaproszenia: %@"; + +/* Message used when sharing to iMessage */ +"windows-waitlist.share-sheet.message" = "Chcesz rozpocząć prywatne przeglądanie w systemie Windows?\n\nOdwiedź ten adres na komputerze, aby pobrać:\nhttps://duckduckgo.com/windows"; + +/* Summary text for the Windows browser waitlist */ +"windows-waitlist.summary" = "DuckDuckGo dla systemu Windows ma wszystko, czego potrzebujesz, aby przeglądać strony internetowe, zachowując większą prywatność – prywatne wyszukiwanie, blokowanie śledzących, wymuszone szyfrowanie i blokowanie wyskakujących okienek z ciasteczkami, a w planach mamy jeszcze więcej najlepszych w swojej klasie zabezpieczeń."; + +/* Title for the Windows Waitlist feature */ +"windows-waitlist.title" = "Aplikacja DuckDuckGo dla systemu Windows"; + +/* Title for the Windows browser download link page */ +"windows-waitlist.waitlist-download-screen.try-duckduckgo-for-windows" = "Pobierz DuckDuckGo dla systemu Windows."; + diff --git a/DuckDuckGo/pl.lproj/Settings.strings b/DuckDuckGo/pl.lproj/Settings.strings index 4761086158..0ce1fa9ac0 100644 --- a/DuckDuckGo/pl.lproj/Settings.strings +++ b/DuckDuckGo/pl.lproj/Settings.strings @@ -37,6 +37,9 @@ /* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ "9Ko-0g-T3h.text" = "Rozmiar tekstu"; +/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ +"9kt-6R-XiZ.text" = "Tytuł"; + /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Uruchomienie aplikacji"; @@ -73,6 +76,12 @@ /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Globalna Kontrola Prywatności (GPC)"; +/* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ +"CxT-QK-iVn.title" = "Dodaj"; + +/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ +"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; + /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Zamknięcie aplikacji, brak aktywności przez 15 minut"; @@ -151,6 +160,9 @@ /* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ "HLr-R8-xxF.text" = "Podglądy po dłuższym przyciśnięciu"; +/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ +"hoT-Nu-KXP.text" = "Przeglądaj prywatnie za pomocą naszej aplikacji dla systemu Windows"; + /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Ochrona prywatności włączona dla wszystkich witryn"; @@ -163,6 +175,12 @@ /* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ "m23-t6-9cb.text" = "Podziel się opinią"; +/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ +"mLn-1x-Fl5.title" = "Pozycja paska adresu"; + +/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ +"mqV-pf-NZ1.text" = "Pozycja paska adresu"; + /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Pozwól DuckDuckGo zarządzać wyskakującymi okienkami ze zgodą na używanie plików cookie"; @@ -175,15 +193,27 @@ /* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ "oM7-1o-9oY.text" = "O DuckDuckGo"; +/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ +"opn-JO-idF.text" = "Do góry"; + /* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ "OxE-MQ-uJk.headerTitle" = "Więcej możliwości DuckDuckGo"; +/* Class = "UILabel"; text = "Sync"; ObjectID = "oXN-ez-gct"; */ +"oXN-ez-gct.text" = "Synchronizacja"; + /* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ "P0F-ts-ekd.text" = "Przeglądaj prywatnie za pomocą aplikacji dla komputerów Mac "; +/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ +"qah-gb-udB.text" = "Network Protection"; + /* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ "qeN-SV-zy7.text" = "Etykieta"; +/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ +"RQ8-H1-Ez1.text" = "Aplikacja DuckDuckGo dla systemu Windows"; + /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Rozmiar tekstu"; @@ -232,8 +262,8 @@ /* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ "yvj-LL-MiR.text" = "Dodaj aplikację do Docka"; -/* Class = "UILabel"; text = "DuckDuckGo Desktop App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "Aplikacja komputerowa DuckDuckGo"; +/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ +"Yz9-17-qnn.text" = "Aplikacja DuckDuckGo na komputery Mac"; /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Nowa karta"; diff --git a/DuckDuckGo/pt.lproj/Feedback.strings b/DuckDuckGo/pt.lproj/Feedback.strings index 8fefc4cf1a..ec88efdd05 100644 --- a/DuckDuckGo/pt.lproj/Feedback.strings +++ b/DuckDuckGo/pt.lproj/Feedback.strings @@ -28,9 +28,6 @@ /* Class = "UIBarButtonItem"; title = "Back"; ObjectID = "JF8-Ow-zXL"; */ "JF8-Ow-zXL.title" = "Retroceder"; -/* Class = "UIBarButtonItem"; title = "Submit"; ObjectID = "jr6-Al-4fr"; */ -"jr6-Al-4fr.title" = "Submeter"; - /* Class = "UIButton"; normalTitle = "Submit"; ObjectID = "lsB-Kq-U8p"; */ "lsB-Kq-U8p.normalTitle" = "Submeter"; @@ -58,9 +55,6 @@ /* Class = "UINavigationItem"; title = "Report Broken Site"; ObjectID = "WjT-HM-yim"; */ "WjT-HM-yim.title" = "Denunciar site danificado"; -/* Class = "UILabel"; text = "Broken Site Reason"; ObjectID = "Wx3-Vb-ItS"; */ -"Wx3-Vb-ItS.text" = "Motivo do site avariado"; - /* Class = "UINavigationItem"; title = "Share Feedback"; ObjectID = "Xb0-Zu-opM"; */ "Xb0-Zu-opM.title" = "Partilhar comentários"; diff --git a/DuckDuckGo/pt.lproj/Localizable.strings b/DuckDuckGo/pt.lproj/Localizable.strings index a246eb5775..929e1ca91a 100644 --- a/DuckDuckGo/pt.lproj/Localizable.strings +++ b/DuckDuckGo/pt.lproj/Localizable.strings @@ -1,6 +1,9 @@ /* No comment provided by engineer. */ "%@" = "%@"; +/* No comment provided by engineer. */ +"%@ [%@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)" = "%1$@ [%2$@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)"; + /* Buton label for Edit action */ "action.generic.edit" = "Editar"; @@ -103,6 +106,12 @@ /* Share action */ "action.title.share" = "Partilhar"; +/* Settings label for bottom position for the address bar */ +"address.bar.bottom" = "Parte inferior"; + +/* Settings label for top position for the address bar */ +"address.bar.top" = "Topo"; + /* No comment provided by engineer. */ "addWidget.button" = "Adicionar widget"; @@ -142,6 +151,225 @@ /* Shown on authentication screen */ "app.authentication.unlock" = "Desbloqueie o DuckDuckGo."; +/* First part of about page content (note the trailing space) */ +"appTP.about.content1" = "You’ve probably heard about companies like Google and Facebook tracking you behind the scenes on third-party websites. But did you know they also track your personal information through apps on your device?\n\nIn 2022, DuckDuckGo found that "; + +/* Second part of about page content (note the trailing space) */ +"appTP.about.content2" = "over 85% of free iOS apps tested contained hidden trackers from other companies."; + +/* Third part of about page content (note the leading space) */ +"appTP.about.content3" = " Of the 395 apps tested, 60% sent data to Google. This happens even while you’re not using your device.\n\nTrackers in apps may have access to a lot more information than their website tracker cousins, such as your location down to which floor of a building you're on, how often you play games while at work, and when and how long you sleep each day. Even if you haven’t given apps explicit permission to collect data, they can still take it without your knowledge.\n\nTracking networks like Facebook and Google use these little pieces of information to build a digital profile about you. With it, tracking networks can manipulate what you see online and allow advertisers to bid on access to you based on your data.\n\nTrackers in apps is a BIG problem for privacy. But DuckDuckGo has a solution that can help.\n\nWhen enabled in the DuckDuckGo Privacy Browser app, App Tracking Protection blocks many trackers in other apps, not just the trackers we find on websites when you browse. These dual layers of protection reduce what companies know about you overall, so you can use your apps with more peace of mind, knowing you’re more protected."; + +/* Navigation Title for AppTP about page */ +"appTP.about.navTitle" = "About App Trackers"; + +/* Title for AppTP about page */ +"appTP.about.title" = "What Are App Trackers?"; + +/* Title for 'Report an Issue' button in the activity view. */ +"appTP.activityView.reportIssue" = "Report Issue"; + +/* Text label for switch that turns blocking on or off for a tracker */ +"appTP.blockTrackerText" = "Block this Tracker"; + +/* Detail string describing what AppTP is */ +"appTP.cell.detail" = "Bloquear rastreadores de aplicações no teu dispositivo"; + +/* String indicating AppTP is disabled when viewed from the settings screen */ +"appTP.cell.disabled" = "Desativado"; + +/* String indicating AppTP is enabled when viewed from the settings screen */ +"appTP.cell.enabled" = "Ativado"; + +/* Info string informing the user what App Tracking Protection does. */ +"appTP.empty.disabled.info" = "Reativa a App Tracking Protection para que possamos bloquear rastreadores irritantes noutras aplicações."; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.heading" = "We’re blocking hidden trackers"; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.info" = "Come back soon to see a list of all the app trackers we’ve blocked."; + +/* First answer for AppTP FAQ page */ +"appTP.faq.answer1" = "App Tracking Protection blocks app trackers from other companies, like when Facebook tries to track you in a banking app. Companies may still track you in apps they own."; + +/* Second answer for AppTP FAQ page */ +"appTP.faq.answer2" = "Yes! App Tracking Protection works across all apps on your device to block the most common hidden trackers we find trying to collect your personal info."; + +/* Third answer for AppTP FAQ page */ +"appTP.faq.answer3" = "We currently only block the most common trackers that we find on iOS. This helps us to comprehensively test App Tracking Protection and lower frequency of app breakage, while blocking up to 70% of all tracking requests."; + +/* Fourth answer for AppTP FAQ page */ +"appTP.faq.answer4" = "You’ll be asked to set up a virtual private network (VPN) connection, but you don't need to install a VPN app for App Tracking Protection to work.\n\nThis permission, which works only on your device, allows App Tracking Protection to monitor network traffic so that it can block known trackers."; + +/* Fifth answer for AppTP FAQ page */ +"appTP.faq.answer5" = "You can use App Tracking Protection at the same time as using an IKEv2 protocol VPN app on an iOS device. You won’t be able to use App Tracking Protection on an iOS device if you’re using a VPN app that uses a different type of protocol, like WireGuard or OpenVPN type VPNs."; + +/* Sixth answer for AppTP FAQ page */ +"appTP.faq.answer6" = "A VPN sends your data from the device to its own server, where it secures and anonymizes your data from prying eyes. However, this allows the VPN company to see your network traffic.\n\nApp Tracking Protection is different. Instead of sending your data to a VPN server, App Tracking Protection works only on your device, sitting between your apps and the servers they talk to.\n\nWhenever App Tracking Protection recognizes a known tracker, it blocks the tracker from sending personal information (such as your IP address, activity, and device details) off your device. All other traffic reaches its destination, so your apps work normally."; + +/* Seventh answer for AppTP FAQ page */ +"appTP.faq.answer7" = "App Tracking Protection works only on your device and doesn’t send your data off your device to DuckDuckGo. We don’t collect or store any data from your apps."; + +/* First question for AppTP FAQ page */ +"appTP.faq.question1" = "How does App Tracking Protection work?"; + +/* Second question for AppTP FAQ page */ +"appTP.faq.question2" = "Does App Tracking Protection block trackers in all apps on my device?"; + +/* Third question for AppTP FAQ page */ +"appTP.faq.question3" = "Does App Tracking Protection block all app trackers?"; + +/* Fourth question for AppTP FAQ page */ +"appTP.faq.question4" = "Why does App Tracking Protection use a VPN connection?"; + +/* Fifth question for AppTP FAQ page */ +"appTP.faq.question5" = "Will App Tracking Protection work if I also use a VPN app?"; + +/* Sixth question for AppTP FAQ page */ +"appTP.faq.question6" = "How is App Tracking Protection different from a VPN?"; + +/* Seventh question for AppTP FAQ page */ +"appTP.faq.question7" = "Is my data private?"; + +/* Title for AppTP FAQ page */ +"appTP.faq.title" = "App Tracking Protection FAQ"; + +/* Do not translate. StringsDict entry -- Count part of string 'App Tracking Protection blocked x tracking attempts today' */ +"appTP.home.blockedCount" = "appTP.home.blockedCount"; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the trailing space) */ +"appTP.home.blockedPrefix" = "A App Tracking Protection bloqueou "; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the leading space) */ +"appTP.home.blockedSuffix" = " nas tuas aplicações hoje."; + +/* Prefix of string 'App Tracking Protection disabled. Tap to re-enable.' (note the trailing space) */ +"appTP.home.disabledPrefix" = "App Tracking Protection disabled. "; + +/* Suffix of string 'App Tracking Protection disabled. Tap to re-enable.' */ +"appTP.home.disabledSuffix" = "Tap to continue blocking tracking attempts across your apps."; + +/* Text indicating the tracking event occured 'just now'. Example: Last attempt 'just now' */ +"appTP.justNow" = "Agora mesmo"; + +/* View to manage trackers for AppTP. Allows the user to turn trackers on or off. */ +"appTP.manageTrackers" = "Gerir rastreadores"; + +/* Button title for AppTP onboarding */ +"appTP.onboarding.continueButton" = "Continue"; + +/* Button title for AppTP onboarding to enable AppTP */ +"appTP.onboarding.enableeButton" = "Enable App Tracking Protection"; + +/* Button title for AppTP onboarding to learn more about AppTP */ +"appTP.onboarding.learnMoreButton" = "Learn More"; + +/* First part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info1" = "Over 85% of free iOS apps"; + +/* Second part of info on the first AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page1Info2" = " we’ve tested allow other companies to track your personal information, even when you’re sleeping."; + +/* Third part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info3" = "See who we catch trying to track you in your apps and take back control."; + +/* First part of info on the second AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page2Info1" = "App Tracking Protection "; + +/* Second part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info2" = "detects and blocks app trackers from other companies,"; + +/* Third part of info on the second AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page2Info3" = " like when Google attempts to track you in a health app."; + +/* Fourth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info4" = "It’s free,"; + +/* Fifth part of info on the second AppTP onboarding page (note the leading and trailing space) */ +"appTP.onboarding.page2Info5" = " and you can enjoy your apps as you normally would. Working in the background, it helps "; + +/* Sixth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info6" = "protect you night and day."; + +/* First part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info1" = "App Tracking Protection is not a VPN."; + +/* Second part of info on the third AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page3Info2" = " However, your device will recognize it as one. This is because it uses a local VPN connection to work."; + +/* Third part of info on the third AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page3Info3" = "App Tracking Protection is different. "; + +/* Fourth part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info4" = "It never routes app data through an external server."; + +/* Title for first AppTP onboarding page */ +"appTP.onboarding.title1" = "One easy step for better app privacy!"; + +/* Title for second AppTP onboarding page */ +"appTP.onboarding.title2" = "How does it work?"; + +/* Title for third AppTP onboarding page */ +"appTP.onboarding.title3" = "Who sees your data?"; + +/* Breakage report app name label */ +"appTP.report.appLabel" = "Which app is having issues?"; + +/* Breakage report app name placeholder */ +"appTP.report.appPlaceholder" = "App name"; + +/* Breakage report category label */ +"appTP.report.categoryLabel" = "O que está a acontecer?"; + +/* Breakage report comment label */ +"appTP.report.commentLabel" = "Comments"; + +/* Breakage report comment placeholder */ +"appTP.report.commentPlaceholder" = "Add additional details"; + +/* Breakage report footer explaining what is collected in the breakage report */ +"appTP.report.footer" = "In addition to the details entered into this form, your app issue report will contain:\n• A list of trackers blocked in the last 10 min\n• Whether App Tracking Protection is enabled\n• Aggregate DuckDuckGo app diagnostics"; + +/* Breakage report submit button */ +"appTP.report.submit" = "Submeter"; + +/* Breakage report form title */ +"appTP.report.title" = "Report Issue"; + +/* Breakage report succcess message */ +"appTP.report.toast" = "Obrigado! Comentário enviado"; + +/* Cancel button for 'Report an Issue' alert. */ +"appTP.reportAlert.cancel" = "Agora não"; + +/* Confirm button for 'Report an Issue' alert. */ +"appTP.reportAlert.confirm" = "Comunicar problema"; + +/* Message for 'Report an Issue' alert. */ +"appTP.reportAlert.message" = "Let us know if you disabled App Tracking Protection for this specific tracker because it caused app issues. Your feedback helps us improve!"; + +/* Title for 'Report an Issue' alert. */ +"appTP.reportAlert.title" = "Report Issue?"; + +/* Toast notification diplayed after restoring the blocklist to default settings */ +"appTP.restoreDefaultsToast" = "Default settings restored"; + +/* Button to restore the blocklist to its default state. */ +"appTP.restoreDefualts" = "Restaurar valores predefinidos"; + +/* Title for the App Tracking Protection feature */ +"appTP.title" = "Proteção contra o rastreamento por aplicações"; + +/* Text indicating when the tracker was last allowed. Example: Last attempt allowed (timeString) */ +"appTP.trackerAllowedTimestamp" = "Última tentativa permitida %@"; + +/* Text indicating when the tracker was last blocked. Example: Last attempt blocked (timeString) */ +"appTP.trackerBlockedTimestamp" = "Última tentativa bloqueada %@"; + +/* Do not translate. StringsDict entry -- Subtitle for tracking attempts in App Tracking Protection Activity View. Example: (count) tracking attempts */ +"appTP.trackingattempts" = "appTP.trackingattempts"; + /* Authentication Alert Sign In Button */ "auth.alert.login.button" = "Iniciar sessão"; @@ -301,6 +529,18 @@ /* Toast message when a login item without a title is deleted */ "autofill.logins.list.login-deleted-message-no-title" = "Início de sessão eliminado"; +/* Title for a button that allows a user to reset their list of never saved sites */ +"autofill.logins.list.never.saved" = "Repor sites excluídos"; + +/* Cancel button for resetting list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.cancel" = "Cancelar"; + +/* Confirm button to reset list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.confirm" = "Repor sites excluídos"; + +/* Alert title */ +"autofill.logins.list.never.saved.reset.action.title" = "Se repuseres os sites excluídos, ser-te-á pedido que guardes os teus dados de início de sessão da próxima vez que iniciares sessão num destes sites."; + /* Placeholder for search field on autofill login listing */ "autofill.logins.list.search-placeholder" = "Pesquisar inícios de sessão"; @@ -335,7 +575,7 @@ "autofill.logins.prompt.password.button.title" = "Palavra-passe para % @"; /* Title for autofill login prompt */ -"autofill.logins.prompt.title" = "Utilizar um início de sessão guardado?"; +"autofill.logins.prompt.title" = "Usar uma palavra-passe guardada?"; /* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ "autofill.logins.search.no-results.subtitle" = "para \"%@\""; @@ -385,27 +625,24 @@ /* Title for the alert dialog telling the user an updated username is no longer a private email address */ "autofill.removed.duck.address.title" = "O nome de utilizador privado do Duck Address foi removido"; +/* CTA displayed on modal asking if the user never wants to be prompted to save a login for this website agin */ +"autofill.save-login.never-prompt.CTA" = "Nunca pedir para este site"; + /* Message displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.message" = "Os dados de início de sessão são armazenados de forma segura no teu dispositivo no menu Inícios de sessão."; +"autofill.save-login.new-user.message" = "As palavras-passe são armazenadas de forma segura no teu dispositivo no menu Inícios de sessão."; /* Title displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.title" = "Queres que o DuckDuckGo guarde o teu início de sessão?"; +"autofill.save-login.new-user.title" = "Queres que o DuckDuckGo guarde a tua palavra-passe?"; /* Cancel CTA displayed on modal asking for the user to save the login */ "autofill.save-login.not-now.CTA" = "Não guardes"; -/* Confirm CTA displayed on modal asking for the user to save the login */ -"autofill.save-login.save.CTA" = "Guardar início de sessão"; - /* Title displayed on modal asking for the user to save the login */ "autofill.save-login.title" = "Guardar início de sessão?"; /* Confirm CTA displayed on modal asking for the user to save the password */ "autofill.save-password.save.CTA" = "Guardar palavra-passe"; -/* Title displayed on modal asking for the user to save the password */ -"autofill.save-password.title" = "Guardar palavra-passe?"; - /* Accessibility title for a Show Password button displaying actial password instead of ***** */ "autofill.show-password" = "Mostrar palavra-passe"; @@ -413,7 +650,7 @@ "autofill.signin.to.manage" = "%@ para gerires os teus Duck Addresses neste dispositivo."; /* Message displayed on modal asking for the user to update the password */ -"autofill.update-password.message" = "O DuckDuckGo vai atualizar este início de sessão armazenado no teu dispositivo."; +"autofill.update-password.message" = "O DuckDuckGo vai atualizar esta palavra-passe armazenada no teu dispositivo."; /* Confirm CTA displayed on modal asking for the user to update the password */ "autofill.update-password.save.CTA" = "Atualizar palavra-passe"; @@ -442,6 +679,15 @@ /* Placeholder in the add bookmark form */ "bookmark.address.placeholder" = "www.exemplo.com"; +/* Delete bookmark alert message */ +"bookmark.delete.alert.message" = "Vai eliminar o seu marcador para \"%@\""; + +/* Delete bookmark alert title */ +"bookmark.delete.alert.title" = "Delete?"; + +/* The message shown after a bookmark has been deleted */ +"bookmark.deleted.toast" = "Marcador eliminado"; + /* Delete bookmark folder alert delete button */ "bookmark.deleteFolderAlert.deleteButton" = "Eliminar"; @@ -538,9 +784,21 @@ /* Broken Site Category */ "brokensite.category.videos" = "O vídeo não foi reproduzido"; +/* Broken Site Category Placeholder */ +"brokensite.categoryPlaceholder" = "Escolhe o teu problema na lista…"; + +/* Broken Site Category Section Title */ +"brokensite.categoryTitle" = "SELECIONA UMA CATEGORIA"; + +/* Broken Site Comment Placeholder */ +"brokensite.commentPlaceholder" = "Indica mais detalhes para nos ajudares a resolver este problema"; + /* Broken Site Section Title */ "brokensite.sectionTitle" = "DESCREVER O QUE ACONTECEU"; +/* No comment provided by engineer. */ +"bucket: %@" = "grupo: %@"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Mês passado"; @@ -772,6 +1030,39 @@ /* Empty search placeholder on bookmarks search */ "empty.search" = "Nenhuma correspondência encontrada"; +/* No comment provided by engineer. */ +"Error" = "Erro"; + +/* Button title to Sign In */ +"error.email-protection-sign-in.action" = "Iniciar sessão"; + +/* Alert message */ +"error.email-protection-sign-in.body" = "Lamentamos. Inicia sessão novamente para reativar as funcionalidades de Email Protection neste navegador."; + +/* Alert title */ +"error.email-protection-sign-in.title" = "Erro de Email Protection"; + +/* Button title to open device settings */ +"error.insufficient-disk-space.action" = "Abre Definições"; + +/* Alert message */ +"error.insufficient-disk-space.body" = "O teu dispositivo não tem espaço de armazenamento. Liberta espaço para continuar."; + +/* Alert title */ +"error.insufficient-disk-space.title" = "Armazenamento insuficiente"; + +/* Button title that is shutting down the app */ +"error.preemptive-crash.action" = "Fechar a aplicação"; + +/* Alert message */ +"error.preemptive-crash.body" = "A aplicação tem um problema e será encerrada. Reabre a aplicação para continuar."; + +/* Alert title */ +"error.preemptive-crash.title" = "Detetado problema na aplicação"; + +/* Generic error message on a dialog for when the cause is not known. */ +"error.unknown.try.again" = "An unknown error has occurred"; + /* No comment provided by engineer. */ "favorite" = "Favorito"; @@ -991,6 +1282,18 @@ /* Home tab title */ "homeTab.title" = "Página inicial"; +/* OK title for invite screen alert dismissal button */ +"invite.alert.ok.button" = "OK"; + +/* Continue button on an invite dialog */ +"invite.dialog.continue.button" = "Continue"; + +/* Get Started button on an invite dialog */ +"invite.dialog.get.started.button" = "Get Started"; + +/* Message to show after user enters an unrecognized invite code */ +"invite.dialog.unrecognized.code.message" = "We didn’t recognize this Invite Code."; + /* No comment provided by engineer. */ "keyCommandAddBookmark" = "Adicionar marcador"; @@ -1057,15 +1360,15 @@ /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "O DuckDuckGo para Mac tem a velocidade de que necessita, as funcionalidades de navegação que espera e os nossos Privacy Essentials líderes no setor."; -/* Body text for the macOS waitlist notification */ -"mac-waitlist.available.notification.body" = "Abra o seu convite"; - /* Title for the macOS waitlist notification */ "mac-waitlist.available.notification.title" = "O DuckDuckGo para Mac está pronto!"; /* Title for the copy action */ "mac-waitlist.copy" = "Copiar"; +/* Description text above the Share Link button */ +"mac-waitlist.join-waitlist-screen.on-your-computer-go-to" = "No teu computador Windows, acede a:"; + /* Description text above the Share Link button */ "mac-waitlist.join-waitlist-screen.on-your-mac-go-to" = "No seu Mac, aceda a:"; @@ -1078,6 +1381,9 @@ /* Disclaimer for the Join Waitlist screen */ "mac-waitlist.join-waitlist-screen.windows" = "Windows em breve!"; +/* Title for the macOS waitlist button redirecting to Windows waitlist */ +"mac-waitlist.join-waitlist-screen.windows-waitlist" = "Procura a versão para Windows?"; + /* Title for the settings subtitle */ "mac-waitlist.settings.browse-privately" = "Navegue em privado com a nossa aplicação para Mac"; @@ -1088,7 +1394,7 @@ "mac-waitlist.share-sheet.title" = "DuckDuckGo para Mac"; /* Title for the Mac Waitlist feature */ -"mac-waitlist.title" = "Aplicação para desktop DuckDuckGo"; +"mac-waitlist.title" = "Aplicação DuckDuckGo para Mac"; /* No comment provided by engineer. */ "menu.button.hint" = "Menu de navegação"; @@ -1105,9 +1411,81 @@ /* Edit button */ "navigation.title.edit" = "Editar"; +/* String indicating NetP is connected when viewed from the settings screen */ +"netP.cell.connected" = "Connected"; + +/* String indicating NetP is disconnected when viewed from the settings screen */ +"netP.cell.disconnected" = "Not connected"; + +/* Title for the Network Protection feature */ +"netP.title" = "Network Protection"; + +/* Message for the network protection invite dialog */ +"network.protection.invite.dialog.message" = "Enter your invite code to get started."; + +/* Title for the network protection invite screen */ +"network.protection.invite.dialog.title" = "You're invited to try Network Protection"; + +/* Prompt for the network protection invite code text field */ +"network.protection.invite.field.prompt" = "Invite Code"; + +/* Message explaining that netP is invite only */ +"network.protection.invite.only.message" = "DuckDuckGo Network Protection is currently invite-only."; + +/* Message for the network protection invite success view */ +"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; + +/* Title for the network protection invite success view */ +"network.protection.invite.success.title" = "Success! You’re in."; + +/* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ +"network.protection.status.connected.format" = "Connected - %@"; + +/* The label for the NetP VPN when connecting */ +"network.protection.status.connecting" = "Connecting..."; + +/* The label for the NetP VPN when disconnected */ +"network.protection.status.disconnected" = "Not connected"; + +/* The label for the NetP VPN when disconnecting */ +"network.protection.status.disconnecting" = "Disconnecting..."; + +/* Message label text for the netP status view */ +"network.protection.status.header.message" = "DuckDuckGo's VPN secures all of your device's Internet traffic anytime, anywhere."; + +/* Header title label text for the status view when netP is disconnected */ +"network.protection.status.header.title.off" = "Network Protection is Off"; + +/* Header title label text for the status view when netP is connected */ +"network.protection.status.header.title.on" = "Network Protection is On"; + +/* The status view 'Share Feedback' button which is shown inline on the status view after the \(netPInviteOnlyMessage) text */ +"network.protection.status.menu.share.feedback" = "Share Feedback"; + +/* Connection details label shown in NetworkProtection's status view. */ +"network.protection.status.view.connection.details" = "Connection Details"; + +/* Generic connection failed error message shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.message" = "Please try again later."; + +/* Generic connection failed error title shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; + +/* IP Address label shown in NetworkProtection's status view. */ +"network.protection.status.view.ip.address" = "IP Address"; + +/* Location label shown in NetworkProtection's status view. */ +"network.protection.status.view.location" = "Location"; + +/* Title label text for the status view when netP is disconnected */ +"network.protection.status.view.title" = "Network Protection"; + /* Do not translate - stringsdict entry */ "number.of.tabs" = "number.of.tabs"; +/* No comment provided by engineer. */ +"Ok" = "OK"; + /* Text displayed on notification appearing in the address bar when the browser dismissed the cookie popup automatically rejecting it */ "omnibar.notification.cookies-managed" = "Cookies geridos"; @@ -1147,6 +1525,18 @@ /* ’Link’ is link on a website */ "open.externally.failed" = "Não encontramos a aplicação necessária para abrir esse link"; +/* No comment provided by engineer. */ +"Please enter which app on your device is broken." = "Indica que aplicação no teu dispositivo está com problemas."; + +/* Activate button */ +"pm.activate" = "Reactivate"; + +/* Cancel button */ +"pm.cancel" = "Cancel"; + +/* Deactivate button */ +"pm.deactivate" = "Deactivate"; + /* No comment provided by engineer. */ "preserveLogins.domain.list.footer" = "Os websites confiam nos cookies para o manter ligado. Quando se usa uma barreira de segurança num site, os cookies não são apagados e permanecerá ligado, mesmo após usar o botão de segurança. Ainda bloqueamos rastreadores de terceiros encontrados em sites com barreiras de segurança."; @@ -1216,8 +1606,8 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Favoritos"; -/* No comment provided by engineer. */ -"settings.about.text" = "No DuckDuckGo, estamos a estabelecer o novo padrão de confiança online.\n\nO Navegador de Privacidade DuckDuckGo fornece todos os elementos essenciais de privacidade que precisa para se proteger enquanto pesquisa e navega na web, incluindo o bloqueio de rastreamento, a criptografia mais inteligente e a pesquisa privada DuckDuckGo.\n\nAfinal de contas, a internet não deve ser tão assustadora e obter a privacidade que merece online deve ser tão simples quanto fechar as cortinas."; +/* about page */ +"settings.about.text" = "A DuckDuckGo é a empresa independente de privacidade na Internet fundada em 2008 para quem está cansado de ser rastreado online e quer uma solução simples. Somos a prova de que podes proteger a tua privacidade online sem compromissos.\n\nO navegador DuckDuckGo vem com as funcionalidades que esperas num navegador de referência, como marcadores, separadores, palavras-passe, entre outras, além de mais de [uma dúzia de proteções de privacidade poderosas](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/) que os navegadores mais populares não oferecem por predefinição. Este conjunto especialmente abrangente de proteções de privacidade ajuda a proteger as tuas atividades online, desde a pesquisa à navegação, e-mails, entre outras.\n\nPara utilizares as nossas proteções de privacidade, não tens de saber nada sobre os detalhes técnicos nem lidar com definições complicadas. Tudo o que tens de fazer é mudar o teu navegador para o DuckDuckGo em todos os teus dispositivos e desfrutar de privacidade como predefinição.\n\nMas *se* quiseres espreitar os bastidores, podes consultar mais informações sobre como funcionam as proteções de privacidade do DuckDuckGo nas nossas [páginas de ajuda](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/)."; /* Report a Broken Site screen confirmation button */ "siteFeedback.buttonText" = "Submeter relatório"; @@ -1237,6 +1627,9 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Que site está com falhas?"; +/* No comment provided by engineer. */ +"sync.remove-device.message" = "\"%@\" deixará de poder aceder aos teus dados sincronizados."; + /* Accessibility label on remove button */ "tab.close.home" = "Fechar o separador inicial"; @@ -1309,6 +1702,57 @@ /* Voice-search footer note with on-device privacy warning */ "voiceSearch.footer.note" = "O áudio é processado no dispositivo. Não foi armazenado ou partilhado com ninguém, incluindo o DuckDuckGo."; +/* Title for the button to enable push notifications in system settings */ +"waitlist.allow-notifications" = "Permitir notificações"; + +/* Body text for the waitlist notification */ +"waitlist.available.notification.body" = "Abra o seu convite"; + +/* Title for the copy action */ +"waitlist.copy" = "Copiar"; + +/* Label text for the invite code */ +"waitlist.invite-code" = "Código de Convite"; + +/* Step title on the invite screen */ +"waitlist.invite-screen.step.title" = "Passo %d"; + +/* Title for the invite code screen */ +"waitlist.invite-screen.youre-invited" = "Está convidado!"; + +/* Title for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.join" = "Adira à Lista de Espera Privada"; + +/* Temporary status text for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.joining" = "A aderir à Lista de Espera..."; + +/* Title for the Share Link button */ +"waitlist.join-waitlist-screen.share-link" = "Partilhar ligação"; + +/* Notification text for the waitlist */ +"waitlist.joined.no-notification.get-notification" = "NOTIFICAR-ME"; + +/* Title for the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.get-notification-confirmation-title" = "Receber uma notificação quando for a tua vez?"; + +/* Cancel button in the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.no-thanks" = "Não, obrigado"; + +/* Text used for the Notifications Disabled state */ +"waitlist.notification.disabled" = "Podemos notificá-lo quando for a sua vez, mas as notificações, de momento, estão desativadas para o DuckDuckGo."; + +/* Privacy disclaimer for the Waitlist feature */ +"waitlist.privacy-disclaimer" = "Não precisará de partilhar nenhuma informação pessoal para aderir à lista de espera. Irá proteger o seu lugar na fila com um carimbo de data/hora que existe exclusivamente em seu dispositivo, para que possamos notificá-lo quando for a sua vez."; + +/* Title for the queue screen */ +"waitlist.queue-screen.on-the-list" = "Está na lista!"; + +/* Title for the settings subtitle */ +"waitlist.settings.download-available" = "Transferência disponível"; + +/* Title for the share sheet entry */ +"waitlist.share-sheet.title" = "Está convidado!"; + /* Confirmation message */ "web.url.remove.favorite.done" = "Favorito removido"; @@ -1333,3 +1777,51 @@ /* Alert title explaining the message is shown by a website */ "webJSAlert.website-message.format" = "Uma mensagem de %@:"; +/* No comment provided by engineer. */ +"Welcome to the Duck Side!" = "Bem-vindo ao Duck Side!"; + +/* Title for the Windows waitlist notification */ +"windows-waitlist.available.notification.title" = "Experimenta o DuckDuckGo para Windows!"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-1.description" = "Visita este URL no teu dispositivo Windows para descarregar:"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-2.description" = "Abre o instalador do DuckDuckGo em Transferências, seleciona Instalar e introduz o teu código de convite."; + +/* Subtitle for the Windows Waitlist Invite screen */ +"windows-waitlist.invite-screen.subtitle" = "A postos para usar o DuckDuckGo no Windows?"; + +/* Title for the Windows waitlist button redirecting to Mac waitlist */ +"windows-waitlist.join-waitlist-screen.mac-waitlist" = "Procura a versão para Mac?"; + +/* Title for the Join Windows Waitlist screen */ +"windows-waitlist.join-waitlist-screen.try-duckduckgo-for-windows" = "Obtenha acesso antecipado para experimentar o DuckDuckGo para Windows!"; + +/* Message for the alert to confirm enabling notifications */ +"windows-waitlist.joined.no-notification.get-notification-confirmation-message" = "Vamos enviar-te uma notificação quando a tua cópia do DuckDuckGo para Windows estiver pronta para descarregar. "; + +/* Label text for the Joined Waitlist state with notifications declined */ +"windows-waitlist.joined.notifications-declined" = "O seu convite para experimentar o DuckDuckGo para Windows ficará disponível aqui. Volte em breve, ou poderemos enviar uma notificação quando for a sua vez."; + +/* Label text for the Joined Waitlist state with notifications enabled */ +"windows-waitlist.joined.notifications-enabled" = "Vamos enviar-te uma notificação quando a tua cópia do DuckDuckGo para Windows estiver pronta para descarregar."; + +/* Title for the settings subtitle */ +"windows-waitlist.settings.browse-privately" = "Navegue em privado com a nossa aplicação para Windows"; + +/* Message used when sharing to iMessage. Parameter is an eight digit invite code. */ +"windows-waitlist.share-sheet.invite-code-message" = "Recebeste um convite!\n\nA postos para usar o DuckDuckGo no Windows?Passo 1\nVisita este URL no teu dispositivo Windows para descarregar:\nhttps://duckduckgo.com/windows\n\nPasso 2\nAbre o instalador do DuckDuckGo em Transferências, seleciona Instalar e introduz o teu código de convite.\n\nCódigo de convite: %@"; + +/* Message used when sharing to iMessage */ +"windows-waitlist.share-sheet.message" = "A postos para começares a navegar em privado no Windows?\n\nVisita este URL no teu computador para transferir:\nhttps://duckduckgo.com/windows"; + +/* Summary text for the Windows browser waitlist */ +"windows-waitlist.summary" = "O DuckDuckGo tem tudo o que precisa para navegar com mais privacidade — pesquisa privada, bloqueio de rastreadores, encriptação forçada e bloqueio de pop-ups de cookies — e há mais proteções líderes de mercado a caminho."; + +/* Title for the Windows Waitlist feature */ +"windows-waitlist.title" = "Aplicação DuckDuckGo para Windows"; + +/* Title for the Windows browser download link page */ +"windows-waitlist.waitlist-download-screen.try-duckduckgo-for-windows" = "Obtém o DuckDuckGo para Windows!"; + diff --git a/DuckDuckGo/pt.lproj/Settings.strings b/DuckDuckGo/pt.lproj/Settings.strings index 47d97a737e..e7f04d47c8 100644 --- a/DuckDuckGo/pt.lproj/Settings.strings +++ b/DuckDuckGo/pt.lproj/Settings.strings @@ -37,6 +37,9 @@ /* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ "9Ko-0g-T3h.text" = "Tamanho do texto"; +/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ +"9kt-6R-XiZ.text" = "Título"; + /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Abertura das aplicações"; @@ -73,6 +76,12 @@ /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Controlo Global de Privacidade (CGP)"; +/* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ +"CxT-QK-iVn.title" = "Adicionar"; + +/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ +"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; + /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Saída da aplicação, inativa durante 15 minutos"; @@ -151,6 +160,9 @@ /* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ "HLr-R8-xxF.text" = "Pré-visualizações ao premir prolongadamente"; +/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ +"hoT-Nu-KXP.text" = "Navegue em privado com a nossa aplicação para Windows"; + /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Proteção de Privacidade ativa para todos os sites"; @@ -163,6 +175,12 @@ /* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ "m23-t6-9cb.text" = "Partilhar comentários"; +/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ +"mLn-1x-Fl5.title" = "Posição da barra de endereços"; + +/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ +"mqV-pf-NZ1.text" = "Posição da barra de endereços"; + /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Permitir que o DuckDuckGo faça a gestão dos pop-ups de consentimento de cookies"; @@ -175,15 +193,27 @@ /* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ "oM7-1o-9oY.text" = "Sobre o DuckDuckGo"; +/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ +"opn-JO-idF.text" = "Topo"; + /* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ "OxE-MQ-uJk.headerTitle" = "Mais da DuckDuckGo"; +/* Class = "UILabel"; text = "Sync"; ObjectID = "oXN-ez-gct"; */ +"oXN-ez-gct.text" = "Sincronizar"; + /* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ "P0F-ts-ekd.text" = "Navegue em privado com a nossa aplicação para Mac "; +/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ +"qah-gb-udB.text" = "Network Protection"; + /* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ "qeN-SV-zy7.text" = "Rótulo"; +/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ +"RQ8-H1-Ez1.text" = "Aplicação DuckDuckGo para Windows"; + /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Tamanho do texto"; @@ -232,8 +262,8 @@ /* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ "yvj-LL-MiR.text" = "Adicionar aplicação à sua dock"; -/* Class = "UILabel"; text = "DuckDuckGo Desktop App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "Aplicação para desktop DuckDuckGo"; +/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ +"Yz9-17-qnn.text" = "Aplicação DuckDuckGo para Mac"; /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Novo separador"; diff --git a/DuckDuckGo/ro.lproj/Feedback.strings b/DuckDuckGo/ro.lproj/Feedback.strings index 99764dfeae..c2715d7fcf 100644 --- a/DuckDuckGo/ro.lproj/Feedback.strings +++ b/DuckDuckGo/ro.lproj/Feedback.strings @@ -28,9 +28,6 @@ /* Class = "UIBarButtonItem"; title = "Back"; ObjectID = "JF8-Ow-zXL"; */ "JF8-Ow-zXL.title" = "Înapoi"; -/* Class = "UIBarButtonItem"; title = "Submit"; ObjectID = "jr6-Al-4fr"; */ -"jr6-Al-4fr.title" = "Trimite"; - /* Class = "UIButton"; normalTitle = "Submit"; ObjectID = "lsB-Kq-U8p"; */ "lsB-Kq-U8p.normalTitle" = "Trimite"; @@ -58,9 +55,6 @@ /* Class = "UINavigationItem"; title = "Report Broken Site"; ObjectID = "WjT-HM-yim"; */ "WjT-HM-yim.title" = "Raportați site-ul defect"; -/* Class = "UILabel"; text = "Broken Site Reason"; ObjectID = "Wx3-Vb-ItS"; */ -"Wx3-Vb-ItS.text" = "Motivul pentru care site-ul nu funcționează"; - /* Class = "UINavigationItem"; title = "Share Feedback"; ObjectID = "Xb0-Zu-opM"; */ "Xb0-Zu-opM.title" = "Partajează feedback"; diff --git a/DuckDuckGo/ro.lproj/Localizable.strings b/DuckDuckGo/ro.lproj/Localizable.strings index a9ae2508be..0064a3f2b3 100644 --- a/DuckDuckGo/ro.lproj/Localizable.strings +++ b/DuckDuckGo/ro.lproj/Localizable.strings @@ -1,6 +1,9 @@ /* No comment provided by engineer. */ "%@" = "%@"; +/* No comment provided by engineer. */ +"%@ [%@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)" = "%1$@ [%2$@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)"; + /* Buton label for Edit action */ "action.generic.edit" = "Editați"; @@ -103,6 +106,12 @@ /* Share action */ "action.title.share" = "Partajare"; +/* Settings label for bottom position for the address bar */ +"address.bar.bottom" = "Partea de jos"; + +/* Settings label for top position for the address bar */ +"address.bar.top" = "Sus"; + /* No comment provided by engineer. */ "addWidget.button" = "Adăugare widget"; @@ -142,6 +151,225 @@ /* Shown on authentication screen */ "app.authentication.unlock" = "Deblochează DuckDuckGo."; +/* First part of about page content (note the trailing space) */ +"appTP.about.content1" = "You’ve probably heard about companies like Google and Facebook tracking you behind the scenes on third-party websites. But did you know they also track your personal information through apps on your device?\n\nIn 2022, DuckDuckGo found that "; + +/* Second part of about page content (note the trailing space) */ +"appTP.about.content2" = "over 85% of free iOS apps tested contained hidden trackers from other companies."; + +/* Third part of about page content (note the leading space) */ +"appTP.about.content3" = " Of the 395 apps tested, 60% sent data to Google. This happens even while you’re not using your device.\n\nTrackers in apps may have access to a lot more information than their website tracker cousins, such as your location down to which floor of a building you're on, how often you play games while at work, and when and how long you sleep each day. Even if you haven’t given apps explicit permission to collect data, they can still take it without your knowledge.\n\nTracking networks like Facebook and Google use these little pieces of information to build a digital profile about you. With it, tracking networks can manipulate what you see online and allow advertisers to bid on access to you based on your data.\n\nTrackers in apps is a BIG problem for privacy. But DuckDuckGo has a solution that can help.\n\nWhen enabled in the DuckDuckGo Privacy Browser app, App Tracking Protection blocks many trackers in other apps, not just the trackers we find on websites when you browse. These dual layers of protection reduce what companies know about you overall, so you can use your apps with more peace of mind, knowing you’re more protected."; + +/* Navigation Title for AppTP about page */ +"appTP.about.navTitle" = "About App Trackers"; + +/* Title for AppTP about page */ +"appTP.about.title" = "What Are App Trackers?"; + +/* Title for 'Report an Issue' button in the activity view. */ +"appTP.activityView.reportIssue" = "Report Issue"; + +/* Text label for switch that turns blocking on or off for a tracker */ +"appTP.blockTrackerText" = "Block this Tracker"; + +/* Detail string describing what AppTP is */ +"appTP.cell.detail" = "Blochează tehnologiile de urmărire pe dispozitivul tău"; + +/* String indicating AppTP is disabled when viewed from the settings screen */ +"appTP.cell.disabled" = "Dezactivat"; + +/* String indicating AppTP is enabled when viewed from the settings screen */ +"appTP.cell.enabled" = "Activat"; + +/* Info string informing the user what App Tracking Protection does. */ +"appTP.empty.disabled.info" = "Activează App Tracking Protection pentru a putea bloca tehnologiile de urmărire deranjante din alte aplicații."; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.heading" = "We’re blocking hidden trackers"; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.info" = "Come back soon to see a list of all the app trackers we’ve blocked."; + +/* First answer for AppTP FAQ page */ +"appTP.faq.answer1" = "App Tracking Protection blocks app trackers from other companies, like when Facebook tries to track you in a banking app. Companies may still track you in apps they own."; + +/* Second answer for AppTP FAQ page */ +"appTP.faq.answer2" = "Yes! App Tracking Protection works across all apps on your device to block the most common hidden trackers we find trying to collect your personal info."; + +/* Third answer for AppTP FAQ page */ +"appTP.faq.answer3" = "We currently only block the most common trackers that we find on iOS. This helps us to comprehensively test App Tracking Protection and lower frequency of app breakage, while blocking up to 70% of all tracking requests."; + +/* Fourth answer for AppTP FAQ page */ +"appTP.faq.answer4" = "You’ll be asked to set up a virtual private network (VPN) connection, but you don't need to install a VPN app for App Tracking Protection to work.\n\nThis permission, which works only on your device, allows App Tracking Protection to monitor network traffic so that it can block known trackers."; + +/* Fifth answer for AppTP FAQ page */ +"appTP.faq.answer5" = "You can use App Tracking Protection at the same time as using an IKEv2 protocol VPN app on an iOS device. You won’t be able to use App Tracking Protection on an iOS device if you’re using a VPN app that uses a different type of protocol, like WireGuard or OpenVPN type VPNs."; + +/* Sixth answer for AppTP FAQ page */ +"appTP.faq.answer6" = "A VPN sends your data from the device to its own server, where it secures and anonymizes your data from prying eyes. However, this allows the VPN company to see your network traffic.\n\nApp Tracking Protection is different. Instead of sending your data to a VPN server, App Tracking Protection works only on your device, sitting between your apps and the servers they talk to.\n\nWhenever App Tracking Protection recognizes a known tracker, it blocks the tracker from sending personal information (such as your IP address, activity, and device details) off your device. All other traffic reaches its destination, so your apps work normally."; + +/* Seventh answer for AppTP FAQ page */ +"appTP.faq.answer7" = "App Tracking Protection works only on your device and doesn’t send your data off your device to DuckDuckGo. We don’t collect or store any data from your apps."; + +/* First question for AppTP FAQ page */ +"appTP.faq.question1" = "How does App Tracking Protection work?"; + +/* Second question for AppTP FAQ page */ +"appTP.faq.question2" = "Does App Tracking Protection block trackers in all apps on my device?"; + +/* Third question for AppTP FAQ page */ +"appTP.faq.question3" = "Does App Tracking Protection block all app trackers?"; + +/* Fourth question for AppTP FAQ page */ +"appTP.faq.question4" = "Why does App Tracking Protection use a VPN connection?"; + +/* Fifth question for AppTP FAQ page */ +"appTP.faq.question5" = "Will App Tracking Protection work if I also use a VPN app?"; + +/* Sixth question for AppTP FAQ page */ +"appTP.faq.question6" = "How is App Tracking Protection different from a VPN?"; + +/* Seventh question for AppTP FAQ page */ +"appTP.faq.question7" = "Is my data private?"; + +/* Title for AppTP FAQ page */ +"appTP.faq.title" = "App Tracking Protection FAQ"; + +/* Do not translate. StringsDict entry -- Count part of string 'App Tracking Protection blocked x tracking attempts today' */ +"appTP.home.blockedCount" = "appTP.home.blockedCount"; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the trailing space) */ +"appTP.home.blockedPrefix" = "App Tracking Protection a blocat "; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the leading space) */ +"appTP.home.blockedSuffix" = " în aplicațiile tale astăzi."; + +/* Prefix of string 'App Tracking Protection disabled. Tap to re-enable.' (note the trailing space) */ +"appTP.home.disabledPrefix" = "App Tracking Protection disabled. "; + +/* Suffix of string 'App Tracking Protection disabled. Tap to re-enable.' */ +"appTP.home.disabledSuffix" = "Tap to continue blocking tracking attempts across your apps."; + +/* Text indicating the tracking event occured 'just now'. Example: Last attempt 'just now' */ +"appTP.justNow" = "Chiar acum"; + +/* View to manage trackers for AppTP. Allows the user to turn trackers on or off. */ +"appTP.manageTrackers" = "Gestionează tehnologiile de urmărire"; + +/* Button title for AppTP onboarding */ +"appTP.onboarding.continueButton" = "Continue"; + +/* Button title for AppTP onboarding to enable AppTP */ +"appTP.onboarding.enableeButton" = "Enable App Tracking Protection"; + +/* Button title for AppTP onboarding to learn more about AppTP */ +"appTP.onboarding.learnMoreButton" = "Learn More"; + +/* First part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info1" = "Over 85% of free iOS apps"; + +/* Second part of info on the first AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page1Info2" = " we’ve tested allow other companies to track your personal information, even when you’re sleeping."; + +/* Third part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info3" = "See who we catch trying to track you in your apps and take back control."; + +/* First part of info on the second AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page2Info1" = "App Tracking Protection "; + +/* Second part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info2" = "detects and blocks app trackers from other companies,"; + +/* Third part of info on the second AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page2Info3" = " like when Google attempts to track you in a health app."; + +/* Fourth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info4" = "It’s free,"; + +/* Fifth part of info on the second AppTP onboarding page (note the leading and trailing space) */ +"appTP.onboarding.page2Info5" = " and you can enjoy your apps as you normally would. Working in the background, it helps "; + +/* Sixth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info6" = "protect you night and day."; + +/* First part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info1" = "App Tracking Protection is not a VPN."; + +/* Second part of info on the third AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page3Info2" = " However, your device will recognize it as one. This is because it uses a local VPN connection to work."; + +/* Third part of info on the third AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page3Info3" = "App Tracking Protection is different. "; + +/* Fourth part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info4" = "It never routes app data through an external server."; + +/* Title for first AppTP onboarding page */ +"appTP.onboarding.title1" = "One easy step for better app privacy!"; + +/* Title for second AppTP onboarding page */ +"appTP.onboarding.title2" = "How does it work?"; + +/* Title for third AppTP onboarding page */ +"appTP.onboarding.title3" = "Who sees your data?"; + +/* Breakage report app name label */ +"appTP.report.appLabel" = "Which app is having issues?"; + +/* Breakage report app name placeholder */ +"appTP.report.appPlaceholder" = "App name"; + +/* Breakage report category label */ +"appTP.report.categoryLabel" = "Ce se întâmplă?"; + +/* Breakage report comment label */ +"appTP.report.commentLabel" = "Comments"; + +/* Breakage report comment placeholder */ +"appTP.report.commentPlaceholder" = "Add additional details"; + +/* Breakage report footer explaining what is collected in the breakage report */ +"appTP.report.footer" = "In addition to the details entered into this form, your app issue report will contain:\n• A list of trackers blocked in the last 10 min\n• Whether App Tracking Protection is enabled\n• Aggregate DuckDuckGo app diagnostics"; + +/* Breakage report submit button */ +"appTP.report.submit" = "Trimite"; + +/* Breakage report form title */ +"appTP.report.title" = "Report Issue"; + +/* Breakage report succcess message */ +"appTP.report.toast" = "Mulțumesc! Feedback trimis."; + +/* Cancel button for 'Report an Issue' alert. */ +"appTP.reportAlert.cancel" = "Nu acum"; + +/* Confirm button for 'Report an Issue' alert. */ +"appTP.reportAlert.confirm" = "Raportează problema"; + +/* Message for 'Report an Issue' alert. */ +"appTP.reportAlert.message" = "Let us know if you disabled App Tracking Protection for this specific tracker because it caused app issues. Your feedback helps us improve!"; + +/* Title for 'Report an Issue' alert. */ +"appTP.reportAlert.title" = "Report Issue?"; + +/* Toast notification diplayed after restoring the blocklist to default settings */ +"appTP.restoreDefaultsToast" = "Default settings restored"; + +/* Button to restore the blocklist to its default state. */ +"appTP.restoreDefualts" = "Restabilește valorile implicite"; + +/* Title for the App Tracking Protection feature */ +"appTP.title" = "Protecția la urmărirea aplicațiilor"; + +/* Text indicating when the tracker was last allowed. Example: Last attempt allowed (timeString) */ +"appTP.trackerAllowedTimestamp" = "Ultima încercare a fost permisă la %@"; + +/* Text indicating when the tracker was last blocked. Example: Last attempt blocked (timeString) */ +"appTP.trackerBlockedTimestamp" = "Ultima încercare a fost blocată la %@"; + +/* Do not translate. StringsDict entry -- Subtitle for tracking attempts in App Tracking Protection Activity View. Example: (count) tracking attempts */ +"appTP.trackingattempts" = "appTP.trackingattempts"; + /* Authentication Alert Sign In Button */ "auth.alert.login.button" = "Autentificare"; @@ -301,6 +529,18 @@ /* Toast message when a login item without a title is deleted */ "autofill.logins.list.login-deleted-message-no-title" = "Date de conectare șterse"; +/* Title for a button that allows a user to reset their list of never saved sites */ +"autofill.logins.list.never.saved" = "Resetează site-urile excluse"; + +/* Cancel button for resetting list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.cancel" = "Renunță"; + +/* Confirm button to reset list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.confirm" = "Resetează site-urile excluse"; + +/* Alert title */ +"autofill.logins.list.never.saved.reset.action.title" = "Dacă resetezi site-urile excluse, ți se va cere să îți salvezi datele de conectare data viitoare când te conectezi la oricare dintre aceste site-uri."; + /* Placeholder for search field on autofill login listing */ "autofill.logins.list.search-placeholder" = "Date de autentificare pentru căutare"; @@ -335,7 +575,7 @@ "autofill.logins.prompt.password.button.title" = "Parola pentru %@"; /* Title for autofill login prompt */ -"autofill.logins.prompt.title" = "Folosești datele de conectare salvate?"; +"autofill.logins.prompt.title" = "Folosești o parolă salvată?"; /* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ "autofill.logins.search.no-results.subtitle" = "pentru „%@”"; @@ -385,27 +625,24 @@ /* Title for the alert dialog telling the user an updated username is no longer a private email address */ "autofill.removed.duck.address.title" = "Numele de utilizator pentru Duck Address privată a fost eliminat"; +/* CTA displayed on modal asking if the user never wants to be prompted to save a login for this website agin */ +"autofill.save-login.never-prompt.CTA" = "Nu solicita niciodată pentru acest site"; + /* Message displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.message" = "Conexiunile sunt stocate în siguranță pe dispozitivul dvs. în meniul Conectări."; +"autofill.save-login.new-user.message" = "Parolele sunt stocate în siguranță pe dispozitivul dvs. în meniul Autentificări."; /* Title displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.title" = "Dorești ca DuckDuckGo să îți salveze datele de autentificare?"; +"autofill.save-login.new-user.title" = "Dorești ca DuckDuckGo să-ți salveze parola?"; /* Cancel CTA displayed on modal asking for the user to save the login */ "autofill.save-login.not-now.CTA" = "Nu salva"; -/* Confirm CTA displayed on modal asking for the user to save the login */ -"autofill.save-login.save.CTA" = "Salvează autentificarea"; - /* Title displayed on modal asking for the user to save the login */ "autofill.save-login.title" = "Salvezi autentificarea?"; /* Confirm CTA displayed on modal asking for the user to save the password */ "autofill.save-password.save.CTA" = "Salvează parola"; -/* Title displayed on modal asking for the user to save the password */ -"autofill.save-password.title" = "Salvezi parola?"; - /* Accessibility title for a Show Password button displaying actial password instead of ***** */ "autofill.show-password" = "Arată parola"; @@ -413,7 +650,7 @@ "autofill.signin.to.manage" = "%@ pentru a-ți gestiona adresele Duck Address pe acest dispozitiv."; /* Message displayed on modal asking for the user to update the password */ -"autofill.update-password.message" = "DuckDuckGo va actualiza aceste date de conectare stocate pe dispozitivul tău."; +"autofill.update-password.message" = "DuckDuckGo va actualiza această parolă stocată pe dispozitivul tău."; /* Confirm CTA displayed on modal asking for the user to update the password */ "autofill.update-password.save.CTA" = "Actualizează parola"; @@ -442,6 +679,15 @@ /* Placeholder in the add bookmark form */ "bookmark.address.placeholder" = "www.example.com"; +/* Delete bookmark alert message */ +"bookmark.delete.alert.message" = "Aceasta va șterge marcajul tău pentru „%@”"; + +/* Delete bookmark alert title */ +"bookmark.delete.alert.title" = "Delete?"; + +/* The message shown after a bookmark has been deleted */ +"bookmark.deleted.toast" = "Marcaj șters"; + /* Delete bookmark folder alert delete button */ "bookmark.deleteFolderAlert.deleteButton" = "Ștergere"; @@ -538,9 +784,21 @@ /* Broken Site Category */ "brokensite.category.videos" = "Videoclipul nu s-a redat"; +/* Broken Site Category Placeholder */ +"brokensite.categoryPlaceholder" = "Alegeți problema ta din listă..."; + +/* Broken Site Category Section Title */ +"brokensite.categoryTitle" = "SELECTEAZĂ O CATEGORIE"; + +/* Broken Site Comment Placeholder */ +"brokensite.commentPlaceholder" = "Comunicarea mai multor detalii ne poate ajuta să rezolvăm această problemă"; + /* Broken Site Section Title */ "brokensite.sectionTitle" = "DESCRIE CE S-A INTAMPLAT"; +/* No comment provided by engineer. */ +"bucket: %@" = "bucket: %@"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Ultima lună"; @@ -772,6 +1030,39 @@ /* Empty search placeholder on bookmarks search */ "empty.search" = "Nu s-au găsit potriviri"; +/* No comment provided by engineer. */ +"Error" = "Eroare"; + +/* Button title to Sign In */ +"error.email-protection-sign-in.action" = "Autentificare"; + +/* Alert message */ +"error.email-protection-sign-in.body" = "Ne pare rău, te rugăm să te conectezi din nou pentru a reactiva caracteristicile Email Protection în acest browser."; + +/* Alert title */ +"error.email-protection-sign-in.title" = "Eroare Email Protection"; + +/* Button title to open device settings */ +"error.insufficient-disk-space.action" = "Deschide Setări"; + +/* Alert message */ +"error.insufficient-disk-space.body" = "Se pare că dispozitivul tău a rămas fără spațiu de stocare. Eliberează spațiu pentru a continua."; + +/* Alert title */ +"error.insufficient-disk-space.title" = "Nu există suficient spațiu de stocare"; + +/* Button title that is shutting down the app */ +"error.preemptive-crash.action" = "Închide aplicația"; + +/* Alert message */ +"error.preemptive-crash.body" = "Se pare că există o problemă cu aplicația și aceasta trebuie să se închidă. Redeschide pentru a continua."; + +/* Alert title */ +"error.preemptive-crash.title" = "A fost detectată o problemă cu aplicația"; + +/* Generic error message on a dialog for when the cause is not known. */ +"error.unknown.try.again" = "An unknown error has occurred"; + /* No comment provided by engineer. */ "favorite" = "Preferință"; @@ -991,6 +1282,18 @@ /* Home tab title */ "homeTab.title" = "Pagina de pornire"; +/* OK title for invite screen alert dismissal button */ +"invite.alert.ok.button" = "OK"; + +/* Continue button on an invite dialog */ +"invite.dialog.continue.button" = "Continue"; + +/* Get Started button on an invite dialog */ +"invite.dialog.get.started.button" = "Get Started"; + +/* Message to show after user enters an unrecognized invite code */ +"invite.dialog.unrecognized.code.message" = "We didn’t recognize this Invite Code."; + /* No comment provided by engineer. */ "keyCommandAddBookmark" = "Adăugare semn de carte"; @@ -1057,15 +1360,15 @@ /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "DuckDuckGo pentru Mac are viteza de care ai nevoie, funcționalitățile de răsfoire pe care ți le dorești și este prevăzută cu cele mai bune caracteristici de bază în privința confidențialității din domeniu."; -/* Body text for the macOS waitlist notification */ -"mac-waitlist.available.notification.body" = "Deschide invitația ta"; - /* Title for the macOS waitlist notification */ "mac-waitlist.available.notification.title" = "DuckDuckGo pentru Mac este pregătită!"; /* Title for the copy action */ "mac-waitlist.copy" = "Copiere"; +/* Description text above the Share Link button */ +"mac-waitlist.join-waitlist-screen.on-your-computer-go-to" = "Pe computerul tău Windows, accesează:"; + /* Description text above the Share Link button */ "mac-waitlist.join-waitlist-screen.on-your-mac-go-to" = "Pe dispozitivul tău Mac, accesează:"; @@ -1078,6 +1381,9 @@ /* Disclaimer for the Join Waitlist screen */ "mac-waitlist.join-waitlist-screen.windows" = "Va fi disponibil în curând și pentru Windows!"; +/* Title for the macOS waitlist button redirecting to Windows waitlist */ +"mac-waitlist.join-waitlist-screen.windows-waitlist" = "Cauți versiunea pentru Windows?"; + /* Title for the settings subtitle */ "mac-waitlist.settings.browse-privately" = "Răsfoiește în mod privat, cu aplicația noastră pentru Mac"; @@ -1088,7 +1394,7 @@ "mac-waitlist.share-sheet.title" = "DuckDuckGo pentru Mac"; /* Title for the Mac Waitlist feature */ -"mac-waitlist.title" = "Aplicația DuckDuckGo pentru desktop"; +"mac-waitlist.title" = "Aplicația DuckDuckGo pentru Mac"; /* No comment provided by engineer. */ "menu.button.hint" = "Meniul de navigare"; @@ -1105,9 +1411,81 @@ /* Edit button */ "navigation.title.edit" = "Editați"; +/* String indicating NetP is connected when viewed from the settings screen */ +"netP.cell.connected" = "Connected"; + +/* String indicating NetP is disconnected when viewed from the settings screen */ +"netP.cell.disconnected" = "Not connected"; + +/* Title for the Network Protection feature */ +"netP.title" = "Network Protection"; + +/* Message for the network protection invite dialog */ +"network.protection.invite.dialog.message" = "Enter your invite code to get started."; + +/* Title for the network protection invite screen */ +"network.protection.invite.dialog.title" = "You're invited to try Network Protection"; + +/* Prompt for the network protection invite code text field */ +"network.protection.invite.field.prompt" = "Invite Code"; + +/* Message explaining that netP is invite only */ +"network.protection.invite.only.message" = "DuckDuckGo Network Protection is currently invite-only."; + +/* Message for the network protection invite success view */ +"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; + +/* Title for the network protection invite success view */ +"network.protection.invite.success.title" = "Success! You’re in."; + +/* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ +"network.protection.status.connected.format" = "Connected - %@"; + +/* The label for the NetP VPN when connecting */ +"network.protection.status.connecting" = "Connecting..."; + +/* The label for the NetP VPN when disconnected */ +"network.protection.status.disconnected" = "Not connected"; + +/* The label for the NetP VPN when disconnecting */ +"network.protection.status.disconnecting" = "Disconnecting..."; + +/* Message label text for the netP status view */ +"network.protection.status.header.message" = "DuckDuckGo's VPN secures all of your device's Internet traffic anytime, anywhere."; + +/* Header title label text for the status view when netP is disconnected */ +"network.protection.status.header.title.off" = "Network Protection is Off"; + +/* Header title label text for the status view when netP is connected */ +"network.protection.status.header.title.on" = "Network Protection is On"; + +/* The status view 'Share Feedback' button which is shown inline on the status view after the \(netPInviteOnlyMessage) text */ +"network.protection.status.menu.share.feedback" = "Share Feedback"; + +/* Connection details label shown in NetworkProtection's status view. */ +"network.protection.status.view.connection.details" = "Connection Details"; + +/* Generic connection failed error message shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.message" = "Please try again later."; + +/* Generic connection failed error title shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; + +/* IP Address label shown in NetworkProtection's status view. */ +"network.protection.status.view.ip.address" = "IP Address"; + +/* Location label shown in NetworkProtection's status view. */ +"network.protection.status.view.location" = "Location"; + +/* Title label text for the status view when netP is disconnected */ +"network.protection.status.view.title" = "Network Protection"; + /* Do not translate - stringsdict entry */ "number.of.tabs" = "number.of.tabs"; +/* No comment provided by engineer. */ +"Ok" = "OK"; + /* Text displayed on notification appearing in the address bar when the browser dismissed the cookie popup automatically rejecting it */ "omnibar.notification.cookies-managed" = "Modulele cookie au fost gestionate"; @@ -1147,6 +1525,18 @@ /* ’Link’ is link on a website */ "open.externally.failed" = "Aplicația necesară pentru a deschide acel link nu poate fi găsită"; +/* No comment provided by engineer. */ +"Please enter which app on your device is broken." = "Menționează ce aplicație de pe dispozitivul tău este nefuncțională."; + +/* Activate button */ +"pm.activate" = "Reactivate"; + +/* Cancel button */ +"pm.cancel" = "Cancel"; + +/* Deactivate button */ +"pm.deactivate" = "Deactivate"; + /* No comment provided by engineer. */ "preserveLogins.domain.list.footer" = "Site-urile web se bazează pe cookie-uri pentru a te menține conectat. Atunci când ștergi activitatea și istoricul unui site, cookie-urile nu vor fi șterse și vei rămâne conectat, chiar și după utilizarea butonului Ștergere activitate și istoric. Blocăm totuși instrumentele de urmărire terțe găsite pe site-urile web cu ștergerea activității și istoricului din browser la ieșire."; @@ -1216,8 +1606,8 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Preferințe"; -/* No comment provided by engineer. */ -"settings.about.text" = "Aici, la DuckDuckGo, stabilim noul standard de încredere online.\n\nDuckDuckGo Privacy Browser oferă toate elementele esențiale de confidențialitate de care aveți nevoie pentru a vă proteja în timp ce căutați și navigați pe web, inclusiv blocarea cookie-urilor de urmărire, criptare mai inteligentă și căutare privată DuckDuckGo.\n\nLa urma urmei, internetul nu ar trebui să fie perceput atât de ciudat, iar obținerea confidențialității pe care o meritați online ar trebui să fie la fel de simplă ca a trage perdeaua."; +/* about page */ +"settings.about.text" = "DuckDuckGo este o companie independentă înființată în 2008, care oferă confidențialitate pe internet tuturor celor care s-au săturat să fie urmăriți online și își doresc o soluție simplă. Suntem dovada că protecția confidențialității online fără compromisuri este reală.\n\nBrowserul DuckDuckGo oferă funcțiile pe care le aștepți de la browserul predilect, cum ar fi marcajele, filele, parolele și multe altele, plus peste [o duzină de instrumente puternice de protecție a confidențialității](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/) care nu sunt oferite implicit de majoritatea browserelor populare. Acest set unic și cuprinzător de măsuri de protecție a confidențialității te ajută să îți protejezi activitățile online, de la căutări la navigare, e-mailuri și multe altele.\n\nMăsurile noastre robuste de protecție a confidențialității funcționează fără a necesita cunoștințe tehnice din partea ta și fără a fi nevoie să faci setări complicate. Tot ce trebuie să faci este să folosești browserul DuckDuckGo pe toate dispozitivele și vei beneficia implicit de confidențialitate.\n\nDar dacă *chiar dorești* să consulți detaliile mai tehnice, poți găsi mai multe informații despre modul în care funcționează instrumentele DuckDuckGo de protecție a confidențialității în [paginile noastre de asistență](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/)."; /* Report a Broken Site screen confirmation button */ "siteFeedback.buttonText" = "Trimite raportul"; @@ -1237,6 +1627,9 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Ce site este defect?"; +/* No comment provided by engineer. */ +"sync.remove-device.message" = "„%@” nu va mai putea accesa datele tale sincronizate."; + /* Accessibility label on remove button */ "tab.close.home" = "Închide fila de pornire"; @@ -1309,6 +1702,57 @@ /* Voice-search footer note with on-device privacy warning */ "voiceSearch.footer.note" = "Componenta audio este prelucrată pe dispozitiv. Nu este stocată sau distribuită nimănui, nici DuckDuckGo."; +/* Title for the button to enable push notifications in system settings */ +"waitlist.allow-notifications" = "Permite notificările"; + +/* Body text for the waitlist notification */ +"waitlist.available.notification.body" = "Deschide invitația ta"; + +/* Title for the copy action */ +"waitlist.copy" = "Copiere"; + +/* Label text for the invite code */ +"waitlist.invite-code" = "Codul de invitație"; + +/* Step title on the invite screen */ +"waitlist.invite-screen.step.title" = "Pasul %d"; + +/* Title for the invite code screen */ +"waitlist.invite-screen.youre-invited" = "Ești invitat(ă)!"; + +/* Title for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.join" = "Alătură-te listei de așteptare private"; + +/* Temporary status text for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.joining" = "Te alături listei de așteptare..."; + +/* Title for the Share Link button */ +"waitlist.join-waitlist-screen.share-link" = "Trimite linkul"; + +/* Notification text for the waitlist */ +"waitlist.joined.no-notification.get-notification" = "DORESC SĂ PRIMESC NOTIFICĂRI"; + +/* Title for the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.get-notification-confirmation-title" = "Primești o notificare când este rândul tău?"; + +/* Cancel button in the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.no-thanks" = "Nu, mulțumesc"; + +/* Text used for the Notifications Disabled state */ +"waitlist.notification.disabled" = "Te putem înștiința când este rândul tău, însă notificările sunt momentan dezactivate pentru DuckDuckGo."; + +/* Privacy disclaimer for the Waitlist feature */ +"waitlist.privacy-disclaimer" = "Nu va trebui să comunici informații personale, pentru a te înscrie pe lista de așteptare. Îți vei asigura locul la coadă, folosind o marcă de timp care există numai pe dispozitivul tău, astfel încât să te putem înștiința când ți-a sosit rândul."; + +/* Title for the queue screen */ +"waitlist.queue-screen.on-the-list" = "Ești pe listă!"; + +/* Title for the settings subtitle */ +"waitlist.settings.download-available" = "Descărcare disponibilă"; + +/* Title for the share sheet entry */ +"waitlist.share-sheet.title" = "Ești invitat(ă)!"; + /* Confirmation message */ "web.url.remove.favorite.done" = "Favorit eliminat"; @@ -1333,3 +1777,51 @@ /* Alert title explaining the message is shown by a website */ "webJSAlert.website-message.format" = "Un mesaj de la %@:"; +/* No comment provided by engineer. */ +"Welcome to the Duck Side!" = "Welcome to the Duck Side!"; + +/* Title for the Windows waitlist notification */ +"windows-waitlist.available.notification.title" = "Încearcă DuckDuckGo pentru Windows!"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-1.description" = "Accesează această adresă URL pe dispozitivul tău Windows pentru a descărca:"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-2.description" = "Deschide programul de instalare DuckDuckGo în Descărcări, selectează Instalează, apoi completează codul de invitație."; + +/* Subtitle for the Windows Waitlist Invite screen */ +"windows-waitlist.invite-screen.subtitle" = "Ești gata să folosești DuckDuckGo pe Windows?"; + +/* Title for the Windows waitlist button redirecting to Mac waitlist */ +"windows-waitlist.join-waitlist-screen.mac-waitlist" = "Cauți versiunea pentru Mac?"; + +/* Title for the Join Windows Waitlist screen */ +"windows-waitlist.join-waitlist-screen.try-duckduckgo-for-windows" = "Beneficiază de acces timpuriu pentru a încerca DuckDuckGo pentru Windows!"; + +/* Message for the alert to confirm enabling notifications */ +"windows-waitlist.joined.no-notification.get-notification-confirmation-message" = "Îți vom trimite o notificare atunci când copia ta DuckDuckGo pentru Windows este gata pentru descărcare. "; + +/* Label text for the Joined Waitlist state with notifications declined */ +"windows-waitlist.joined.notifications-declined" = "Invitația ta de a încerca DuckDuckGo pentru Windows va sosi aici. Revino în curând sau îți putem trimite o notificare când este rândul tău."; + +/* Label text for the Joined Waitlist state with notifications enabled */ +"windows-waitlist.joined.notifications-enabled" = "Îți vom trimite o notificare atunci când copia ta DuckDuckGo pentru Windows este gata pentru descărcare."; + +/* Title for the settings subtitle */ +"windows-waitlist.settings.browse-privately" = "Navighează în mod privat cu aplicația noastră pentru Windows"; + +/* Message used when sharing to iMessage. Parameter is an eight digit invite code. */ +"windows-waitlist.share-sheet.invite-code-message" = "Ești invitat!\n\nEști gata să folosești DuckDuckGo pe Windows?\n\nPasul 1\nAccesează această adresă URL pe dispozitivul tău Windows pentru a descărca:\nhttps://duckduckgo.com/windows\n\nPasul 2\nDeschide programul de instalare DuckDuckGo în Descărcări, selectează Instalează, apoi completează codul de invitație.\n\nCod de invitație: %@"; + +/* Message used when sharing to iMessage */ +"windows-waitlist.share-sheet.message" = "Ești gata să începi să navighezi în mod privat folosind Windows?\n\nVizitează această adresă URL pe computerul tău Windows pentru a descărca:\nhttps://duckduckgo.com/windows"; + +/* Summary text for the Windows browser waitlist */ +"windows-waitlist.summary" = "DuckDuckGo pentru Windows are tot ceea ce îți trebuie pentru a naviga într-un mod mai confidențial – căutare privată, blocarea tehnologiilor de urmărire, criptare forțată și blocarea ferestrelor pop-up cookie, plus alte elemente de protecție de cel mai înalt nivel, în curs de implementare."; + +/* Title for the Windows Waitlist feature */ +"windows-waitlist.title" = "Aplicația DuckDuckGo pentru Windows"; + +/* Title for the Windows browser download link page */ +"windows-waitlist.waitlist-download-screen.try-duckduckgo-for-windows" = "Descarcă DuckDuckGo pentru Windows!"; + diff --git a/DuckDuckGo/ro.lproj/Settings.strings b/DuckDuckGo/ro.lproj/Settings.strings index 2c505910ce..c2b63de768 100644 --- a/DuckDuckGo/ro.lproj/Settings.strings +++ b/DuckDuckGo/ro.lproj/Settings.strings @@ -37,6 +37,9 @@ /* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ "9Ko-0g-T3h.text" = "Dimensiune text"; +/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ +"9kt-6R-XiZ.text" = "Titlu"; + /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Lansare aplicație"; @@ -73,6 +76,12 @@ /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Global Privacy Control (GPC)"; +/* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ +"CxT-QK-iVn.title" = "Adăugați"; + +/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ +"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; + /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Ieșire din aplicație, inactiv timp de 15 minute"; @@ -151,6 +160,9 @@ /* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ "HLr-R8-xxF.text" = "Previzualizări prin apăsare lungă"; +/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ +"hoT-Nu-KXP.text" = "Navighează în mod privat cu aplicația noastră pentru Windows"; + /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Protecția confidențialității este activată pentru toate site-urile"; @@ -163,6 +175,12 @@ /* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ "m23-t6-9cb.text" = "Partajează feedback"; +/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ +"mLn-1x-Fl5.title" = "Poziția barei de adrese"; + +/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ +"mqV-pf-NZ1.text" = "Poziția barei de adrese"; + /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Lasă DuckDuckGo să gestioneze ferestrele pop-up de solicitare a consimțământului cu privire la modulele cookie"; @@ -175,15 +193,27 @@ /* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ "oM7-1o-9oY.text" = "Despre DuckDuckGo"; +/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ +"opn-JO-idF.text" = "Sus"; + /* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ "OxE-MQ-uJk.headerTitle" = "Mai multe de la DuckDuckGo"; +/* Class = "UILabel"; text = "Sync"; ObjectID = "oXN-ez-gct"; */ +"oXN-ez-gct.text" = "Sincronizare"; + /* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ "P0F-ts-ekd.text" = "Răsfoiește în mod privat, cu aplicația noastră pentru Mac "; +/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ +"qah-gb-udB.text" = "Network Protection"; + /* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ "qeN-SV-zy7.text" = "Label"; +/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ +"RQ8-H1-Ez1.text" = "Aplicația DuckDuckGo pentru Windows"; + /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Dimensiune text"; @@ -232,8 +262,8 @@ /* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ "yvj-LL-MiR.text" = "Adaugă aplicația la secțiunea ta fixă"; -/* Class = "UILabel"; text = "DuckDuckGo Desktop App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "Aplicația DuckDuckGo pentru desktop"; +/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ +"Yz9-17-qnn.text" = "Aplicația DuckDuckGo pentru Mac"; /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Filă nouă"; diff --git a/DuckDuckGo/ru.lproj/Feedback.strings b/DuckDuckGo/ru.lproj/Feedback.strings index a54ac40d24..b34300f93c 100644 --- a/DuckDuckGo/ru.lproj/Feedback.strings +++ b/DuckDuckGo/ru.lproj/Feedback.strings @@ -28,9 +28,6 @@ /* Class = "UIBarButtonItem"; title = "Back"; ObjectID = "JF8-Ow-zXL"; */ "JF8-Ow-zXL.title" = "Назад"; -/* Class = "UIBarButtonItem"; title = "Submit"; ObjectID = "jr6-Al-4fr"; */ -"jr6-Al-4fr.title" = "Отправить"; - /* Class = "UIButton"; normalTitle = "Submit"; ObjectID = "lsB-Kq-U8p"; */ "lsB-Kq-U8p.normalTitle" = "Отправить"; @@ -58,9 +55,6 @@ /* Class = "UINavigationItem"; title = "Report Broken Site"; ObjectID = "WjT-HM-yim"; */ "WjT-HM-yim.title" = "Сообщить о неработающем сайте"; -/* Class = "UILabel"; text = "Broken Site Reason"; ObjectID = "Wx3-Vb-ItS"; */ -"Wx3-Vb-ItS.text" = "Что не так с сайтом"; - /* Class = "UINavigationItem"; title = "Share Feedback"; ObjectID = "Xb0-Zu-opM"; */ "Xb0-Zu-opM.title" = "Оставьте нам отзыв"; diff --git a/DuckDuckGo/ru.lproj/Localizable.strings b/DuckDuckGo/ru.lproj/Localizable.strings index f1353ea90b..4b79bf5001 100644 --- a/DuckDuckGo/ru.lproj/Localizable.strings +++ b/DuckDuckGo/ru.lproj/Localizable.strings @@ -1,6 +1,9 @@ /* No comment provided by engineer. */ "%@" = "%@"; +/* No comment provided by engineer. */ +"%@ [%@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)" = "%1$@ [%2$@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)"; + /* Buton label for Edit action */ "action.generic.edit" = "Редактировать"; @@ -103,6 +106,12 @@ /* Share action */ "action.title.share" = "Поделиться"; +/* Settings label for bottom position for the address bar */ +"address.bar.bottom" = "Внизу"; + +/* Settings label for top position for the address bar */ +"address.bar.top" = "Вверх"; + /* No comment provided by engineer. */ "addWidget.button" = "Добавить виджет"; @@ -142,6 +151,225 @@ /* Shown on authentication screen */ "app.authentication.unlock" = "Разблокируйте DuckDuckGo."; +/* First part of about page content (note the trailing space) */ +"appTP.about.content1" = "You’ve probably heard about companies like Google and Facebook tracking you behind the scenes on third-party websites. But did you know they also track your personal information through apps on your device?\n\nIn 2022, DuckDuckGo found that "; + +/* Second part of about page content (note the trailing space) */ +"appTP.about.content2" = "over 85% of free iOS apps tested contained hidden trackers from other companies."; + +/* Third part of about page content (note the leading space) */ +"appTP.about.content3" = " Of the 395 apps tested, 60% sent data to Google. This happens even while you’re not using your device.\n\nTrackers in apps may have access to a lot more information than their website tracker cousins, such as your location down to which floor of a building you're on, how often you play games while at work, and when and how long you sleep each day. Even if you haven’t given apps explicit permission to collect data, they can still take it without your knowledge.\n\nTracking networks like Facebook and Google use these little pieces of information to build a digital profile about you. With it, tracking networks can manipulate what you see online and allow advertisers to bid on access to you based on your data.\n\nTrackers in apps is a BIG problem for privacy. But DuckDuckGo has a solution that can help.\n\nWhen enabled in the DuckDuckGo Privacy Browser app, App Tracking Protection blocks many trackers in other apps, not just the trackers we find on websites when you browse. These dual layers of protection reduce what companies know about you overall, so you can use your apps with more peace of mind, knowing you’re more protected."; + +/* Navigation Title for AppTP about page */ +"appTP.about.navTitle" = "About App Trackers"; + +/* Title for AppTP about page */ +"appTP.about.title" = "What Are App Trackers?"; + +/* Title for 'Report an Issue' button in the activity view. */ +"appTP.activityView.reportIssue" = "Report Issue"; + +/* Text label for switch that turns blocking on or off for a tracker */ +"appTP.blockTrackerText" = "Block this Tracker"; + +/* Detail string describing what AppTP is */ +"appTP.cell.detail" = "Блокируйте трекеры приложений на своем устройстве"; + +/* String indicating AppTP is disabled when viewed from the settings screen */ +"appTP.cell.disabled" = "Отключено"; + +/* String indicating AppTP is enabled when viewed from the settings screen */ +"appTP.cell.enabled" = "Включено"; + +/* Info string informing the user what App Tracking Protection does. */ +"appTP.empty.disabled.info" = "Включите функцию защиты от слежки в приложениях, чтобы блокировать надоедливые трекеры в других программах."; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.heading" = "We’re blocking hidden trackers"; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.info" = "Come back soon to see a list of all the app trackers we’ve blocked."; + +/* First answer for AppTP FAQ page */ +"appTP.faq.answer1" = "App Tracking Protection blocks app trackers from other companies, like when Facebook tries to track you in a banking app. Companies may still track you in apps they own."; + +/* Second answer for AppTP FAQ page */ +"appTP.faq.answer2" = "Yes! App Tracking Protection works across all apps on your device to block the most common hidden trackers we find trying to collect your personal info."; + +/* Third answer for AppTP FAQ page */ +"appTP.faq.answer3" = "We currently only block the most common trackers that we find on iOS. This helps us to comprehensively test App Tracking Protection and lower frequency of app breakage, while blocking up to 70% of all tracking requests."; + +/* Fourth answer for AppTP FAQ page */ +"appTP.faq.answer4" = "You’ll be asked to set up a virtual private network (VPN) connection, but you don't need to install a VPN app for App Tracking Protection to work.\n\nThis permission, which works only on your device, allows App Tracking Protection to monitor network traffic so that it can block known trackers."; + +/* Fifth answer for AppTP FAQ page */ +"appTP.faq.answer5" = "You can use App Tracking Protection at the same time as using an IKEv2 protocol VPN app on an iOS device. You won’t be able to use App Tracking Protection on an iOS device if you’re using a VPN app that uses a different type of protocol, like WireGuard or OpenVPN type VPNs."; + +/* Sixth answer for AppTP FAQ page */ +"appTP.faq.answer6" = "A VPN sends your data from the device to its own server, where it secures and anonymizes your data from prying eyes. However, this allows the VPN company to see your network traffic.\n\nApp Tracking Protection is different. Instead of sending your data to a VPN server, App Tracking Protection works only on your device, sitting between your apps and the servers they talk to.\n\nWhenever App Tracking Protection recognizes a known tracker, it blocks the tracker from sending personal information (such as your IP address, activity, and device details) off your device. All other traffic reaches its destination, so your apps work normally."; + +/* Seventh answer for AppTP FAQ page */ +"appTP.faq.answer7" = "App Tracking Protection works only on your device and doesn’t send your data off your device to DuckDuckGo. We don’t collect or store any data from your apps."; + +/* First question for AppTP FAQ page */ +"appTP.faq.question1" = "How does App Tracking Protection work?"; + +/* Second question for AppTP FAQ page */ +"appTP.faq.question2" = "Does App Tracking Protection block trackers in all apps on my device?"; + +/* Third question for AppTP FAQ page */ +"appTP.faq.question3" = "Does App Tracking Protection block all app trackers?"; + +/* Fourth question for AppTP FAQ page */ +"appTP.faq.question4" = "Why does App Tracking Protection use a VPN connection?"; + +/* Fifth question for AppTP FAQ page */ +"appTP.faq.question5" = "Will App Tracking Protection work if I also use a VPN app?"; + +/* Sixth question for AppTP FAQ page */ +"appTP.faq.question6" = "How is App Tracking Protection different from a VPN?"; + +/* Seventh question for AppTP FAQ page */ +"appTP.faq.question7" = "Is my data private?"; + +/* Title for AppTP FAQ page */ +"appTP.faq.title" = "App Tracking Protection FAQ"; + +/* Do not translate. StringsDict entry -- Count part of string 'App Tracking Protection blocked x tracking attempts today' */ +"appTP.home.blockedCount" = "appTP.home.blockedCount"; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the trailing space) */ +"appTP.home.blockedPrefix" = "Сегодня функция защиты от слежки в приложениях заблокировала "; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the leading space) */ +"appTP.home.blockedSuffix" = " в ваших приложениях."; + +/* Prefix of string 'App Tracking Protection disabled. Tap to re-enable.' (note the trailing space) */ +"appTP.home.disabledPrefix" = "App Tracking Protection disabled. "; + +/* Suffix of string 'App Tracking Protection disabled. Tap to re-enable.' */ +"appTP.home.disabledSuffix" = "Tap to continue blocking tracking attempts across your apps."; + +/* Text indicating the tracking event occured 'just now'. Example: Last attempt 'just now' */ +"appTP.justNow" = "Только что"; + +/* View to manage trackers for AppTP. Allows the user to turn trackers on or off. */ +"appTP.manageTrackers" = "Управление трекерами"; + +/* Button title for AppTP onboarding */ +"appTP.onboarding.continueButton" = "Continue"; + +/* Button title for AppTP onboarding to enable AppTP */ +"appTP.onboarding.enableeButton" = "Enable App Tracking Protection"; + +/* Button title for AppTP onboarding to learn more about AppTP */ +"appTP.onboarding.learnMoreButton" = "Learn More"; + +/* First part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info1" = "Over 85% of free iOS apps"; + +/* Second part of info on the first AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page1Info2" = " we’ve tested allow other companies to track your personal information, even when you’re sleeping."; + +/* Third part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info3" = "See who we catch trying to track you in your apps and take back control."; + +/* First part of info on the second AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page2Info1" = "App Tracking Protection "; + +/* Second part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info2" = "detects and blocks app trackers from other companies,"; + +/* Third part of info on the second AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page2Info3" = " like when Google attempts to track you in a health app."; + +/* Fourth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info4" = "It’s free,"; + +/* Fifth part of info on the second AppTP onboarding page (note the leading and trailing space) */ +"appTP.onboarding.page2Info5" = " and you can enjoy your apps as you normally would. Working in the background, it helps "; + +/* Sixth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info6" = "protect you night and day."; + +/* First part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info1" = "App Tracking Protection is not a VPN."; + +/* Second part of info on the third AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page3Info2" = " However, your device will recognize it as one. This is because it uses a local VPN connection to work."; + +/* Third part of info on the third AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page3Info3" = "App Tracking Protection is different. "; + +/* Fourth part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info4" = "It never routes app data through an external server."; + +/* Title for first AppTP onboarding page */ +"appTP.onboarding.title1" = "One easy step for better app privacy!"; + +/* Title for second AppTP onboarding page */ +"appTP.onboarding.title2" = "How does it work?"; + +/* Title for third AppTP onboarding page */ +"appTP.onboarding.title3" = "Who sees your data?"; + +/* Breakage report app name label */ +"appTP.report.appLabel" = "Which app is having issues?"; + +/* Breakage report app name placeholder */ +"appTP.report.appPlaceholder" = "App name"; + +/* Breakage report category label */ +"appTP.report.categoryLabel" = "Что не так?"; + +/* Breakage report comment label */ +"appTP.report.commentLabel" = "Comments"; + +/* Breakage report comment placeholder */ +"appTP.report.commentPlaceholder" = "Add additional details"; + +/* Breakage report footer explaining what is collected in the breakage report */ +"appTP.report.footer" = "In addition to the details entered into this form, your app issue report will contain:\n• A list of trackers blocked in the last 10 min\n• Whether App Tracking Protection is enabled\n• Aggregate DuckDuckGo app diagnostics"; + +/* Breakage report submit button */ +"appTP.report.submit" = "Отправить"; + +/* Breakage report form title */ +"appTP.report.title" = "Report Issue"; + +/* Breakage report succcess message */ +"appTP.report.toast" = "Спасибо! Ваш отзыв отправлен."; + +/* Cancel button for 'Report an Issue' alert. */ +"appTP.reportAlert.cancel" = "Не сейчас"; + +/* Confirm button for 'Report an Issue' alert. */ +"appTP.reportAlert.confirm" = "Сообщить о проблеме"; + +/* Message for 'Report an Issue' alert. */ +"appTP.reportAlert.message" = "Let us know if you disabled App Tracking Protection for this specific tracker because it caused app issues. Your feedback helps us improve!"; + +/* Title for 'Report an Issue' alert. */ +"appTP.reportAlert.title" = "Report Issue?"; + +/* Toast notification diplayed after restoring the blocklist to default settings */ +"appTP.restoreDefaultsToast" = "Default settings restored"; + +/* Button to restore the blocklist to its default state. */ +"appTP.restoreDefualts" = "Вернуть настройки по умолчанию"; + +/* Title for the App Tracking Protection feature */ +"appTP.title" = "Блокировка отслеживания приложениями"; + +/* Text indicating when the tracker was last allowed. Example: Last attempt allowed (timeString) */ +"appTP.trackerAllowedTimestamp" = "Последний пропуск: %@"; + +/* Text indicating when the tracker was last blocked. Example: Last attempt blocked (timeString) */ +"appTP.trackerBlockedTimestamp" = "Последняя блокировка: %@"; + +/* Do not translate. StringsDict entry -- Subtitle for tracking attempts in App Tracking Protection Activity View. Example: (count) tracking attempts */ +"appTP.trackingattempts" = "appTP.trackingattempts"; + /* Authentication Alert Sign In Button */ "auth.alert.login.button" = "Войти"; @@ -301,6 +529,18 @@ /* Toast message when a login item without a title is deleted */ "autofill.logins.list.login-deleted-message-no-title" = "Логин удален"; +/* Title for a button that allows a user to reset their list of never saved sites */ +"autofill.logins.list.never.saved" = "Сбросить список исключений"; + +/* Cancel button for resetting list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.cancel" = "Отменить"; + +/* Confirm button to reset list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.confirm" = "Сбросить список исключений"; + +/* Alert title */ +"autofill.logins.list.never.saved.reset.action.title" = "В случае сброса списка исключений, при следующем входе на любой из этих сайтов вам предложат сохранить свои учетные данные."; + /* Placeholder for search field on autofill login listing */ "autofill.logins.list.search-placeholder" = "Поиск логинов"; @@ -335,7 +575,7 @@ "autofill.logins.prompt.password.button.title" = "Пароль %@"; /* Title for autofill login prompt */ -"autofill.logins.prompt.title" = "Использовать сохраненный логин?"; +"autofill.logins.prompt.title" = "Использовать сохраненный пароль?"; /* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ "autofill.logins.search.no-results.subtitle" = "по запросу «%@»"; @@ -385,27 +625,24 @@ /* Title for the alert dialog telling the user an updated username is no longer a private email address */ "autofill.removed.duck.address.title" = "Приватное имя пользователя Duck Address удалено"; +/* CTA displayed on modal asking if the user never wants to be prompted to save a login for this website agin */ +"autofill.save-login.never-prompt.CTA" = "Больше не спрашивать на этом сайте"; + /* Message displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.message" = "Учетные данные надежно хранятся на вашем устройстве в меню «Логины»."; +"autofill.save-login.new-user.message" = "Пароли надежно хранятся на вашем устройстве в меню «Логины»."; /* Title displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.title" = "Хотите, чтобы DuckDuckGo сохранил логин?"; +"autofill.save-login.new-user.title" = "Хотите, чтобы DuckDuckGo сохранил ваш пароль?"; /* Cancel CTA displayed on modal asking for the user to save the login */ "autofill.save-login.not-now.CTA" = "Не сохранять"; -/* Confirm CTA displayed on modal asking for the user to save the login */ -"autofill.save-login.save.CTA" = "Сохранить логин"; - /* Title displayed on modal asking for the user to save the login */ "autofill.save-login.title" = "Сохранить логин?"; /* Confirm CTA displayed on modal asking for the user to save the password */ "autofill.save-password.save.CTA" = "Сохранить пароль"; -/* Title displayed on modal asking for the user to save the password */ -"autofill.save-password.title" = "Сохранить пароль?"; - /* Accessibility title for a Show Password button displaying actial password instead of ***** */ "autofill.show-password" = "Показать пароль"; @@ -413,7 +650,7 @@ "autofill.signin.to.manage" = "%@ для управления адресами Duck Address с этого устройства."; /* Message displayed on modal asking for the user to update the password */ -"autofill.update-password.message" = "DuckDuckGo обновит логин, сохраненный на вашем устройстве."; +"autofill.update-password.message" = "DuckDuckGo обновит пароль, сохраненный на вашем устройстве."; /* Confirm CTA displayed on modal asking for the user to update the password */ "autofill.update-password.save.CTA" = "Обновить пароль"; @@ -442,6 +679,15 @@ /* Placeholder in the add bookmark form */ "bookmark.address.placeholder" = "www.example.com"; +/* Delete bookmark alert message */ +"bookmark.delete.alert.message" = "Это приведет к удалению закладки для «%@»"; + +/* Delete bookmark alert title */ +"bookmark.delete.alert.title" = "Delete?"; + +/* The message shown after a bookmark has been deleted */ +"bookmark.deleted.toast" = "Закладка удалена"; + /* Delete bookmark folder alert delete button */ "bookmark.deleteFolderAlert.deleteButton" = "Удалить"; @@ -538,9 +784,21 @@ /* Broken Site Category */ "brokensite.category.videos" = "Не воспроизводится видео"; +/* Broken Site Category Placeholder */ +"brokensite.categoryPlaceholder" = "Выберите проблему из списка..."; + +/* Broken Site Category Section Title */ +"brokensite.categoryTitle" = "ВЫБЕРИТЕ КАТЕГОРИЮ"; + +/* Broken Site Comment Placeholder */ +"brokensite.commentPlaceholder" = "Более подробная информация поможет нам быстрее найти решение"; + /* Broken Site Section Title */ "brokensite.sectionTitle" = "ОПИШИТЕ, ЧТО СЛУЧИЛОСЬ"; +/* No comment provided by engineer. */ +"bucket: %@" = "контейнер: %@"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "За последний месяц"; @@ -772,6 +1030,39 @@ /* Empty search placeholder on bookmarks search */ "empty.search" = "Ничего не найдено"; +/* No comment provided by engineer. */ +"Error" = "Ошибка"; + +/* Button title to Sign In */ +"error.email-protection-sign-in.action" = "Войти"; + +/* Alert message */ +"error.email-protection-sign-in.body" = "Чтобы включить функцию Email Protection, пожалуйста, выполните вход заново."; + +/* Alert title */ +"error.email-protection-sign-in.title" = "Ошибка функции Email Protection"; + +/* Button title to open device settings */ +"error.insufficient-disk-space.action" = "Открыть Настройки"; + +/* Alert message */ +"error.insufficient-disk-space.body" = "Кажется, на вашем устройстве закончилась внутренняя память. Освободите место, прежде чем продолжить."; + +/* Alert title */ +"error.insufficient-disk-space.title" = "Недостаточно места"; + +/* Button title that is shutting down the app */ +"error.preemptive-crash.action" = "Закрыть приложение"; + +/* Alert message */ +"error.preemptive-crash.body" = "В приложении возникла ошибка, и его нужно закрыть. Для продолжения работы откройте его снова."; + +/* Alert title */ +"error.preemptive-crash.title" = "Ошибка в приложении"; + +/* Generic error message on a dialog for when the cause is not known. */ +"error.unknown.try.again" = "An unknown error has occurred"; + /* No comment provided by engineer. */ "favorite" = "Избранное"; @@ -991,6 +1282,18 @@ /* Home tab title */ "homeTab.title" = "Домашняя"; +/* OK title for invite screen alert dismissal button */ +"invite.alert.ok.button" = "OK"; + +/* Continue button on an invite dialog */ +"invite.dialog.continue.button" = "Continue"; + +/* Get Started button on an invite dialog */ +"invite.dialog.get.started.button" = "Get Started"; + +/* Message to show after user enters an unrecognized invite code */ +"invite.dialog.unrecognized.code.message" = "We didn’t recognize this Invite Code."; + /* No comment provided by engineer. */ "keyCommandAddBookmark" = "Добавить закладку"; @@ -1057,15 +1360,15 @@ /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "Приложение DuckDuckGo для Mac — это высокая скорость, функциональность и беспрецедентная конфиденциальность."; -/* Body text for the macOS waitlist notification */ -"mac-waitlist.available.notification.body" = "Откройте приглашение"; - /* Title for the macOS waitlist notification */ "mac-waitlist.available.notification.title" = "Приложение DuckDuckGo для Mac готово!"; /* Title for the copy action */ "mac-waitlist.copy" = "Копировать"; +/* Description text above the Share Link button */ +"mac-waitlist.join-waitlist-screen.on-your-computer-go-to" = "Перейдите на компьютере Windows по ссылке:"; + /* Description text above the Share Link button */ "mac-waitlist.join-waitlist-screen.on-your-mac-go-to" = "Перейдите на компьютере Mac по ссылке:"; @@ -1078,6 +1381,9 @@ /* Disclaimer for the Join Waitlist screen */ "mac-waitlist.join-waitlist-screen.windows" = "Скоро — для Windows!"; +/* Title for the macOS waitlist button redirecting to Windows waitlist */ +"mac-waitlist.join-waitlist-screen.windows-waitlist" = "Ищете версию для Windows?"; + /* Title for the settings subtitle */ "mac-waitlist.settings.browse-privately" = "Полная конфиденциальность в Интернете, благодаря нашему приложению для Mac"; @@ -1088,7 +1394,7 @@ "mac-waitlist.share-sheet.title" = "DuckDuckGo для Mac"; /* Title for the Mac Waitlist feature */ -"mac-waitlist.title" = "Настольное приложение DuckDuckGo"; +"mac-waitlist.title" = "Приложение DuckDuckGo для Mac"; /* No comment provided by engineer. */ "menu.button.hint" = "Меню браузера"; @@ -1105,9 +1411,81 @@ /* Edit button */ "navigation.title.edit" = "Редактировать"; +/* String indicating NetP is connected when viewed from the settings screen */ +"netP.cell.connected" = "Connected"; + +/* String indicating NetP is disconnected when viewed from the settings screen */ +"netP.cell.disconnected" = "Not connected"; + +/* Title for the Network Protection feature */ +"netP.title" = "Network Protection"; + +/* Message for the network protection invite dialog */ +"network.protection.invite.dialog.message" = "Enter your invite code to get started."; + +/* Title for the network protection invite screen */ +"network.protection.invite.dialog.title" = "You're invited to try Network Protection"; + +/* Prompt for the network protection invite code text field */ +"network.protection.invite.field.prompt" = "Invite Code"; + +/* Message explaining that netP is invite only */ +"network.protection.invite.only.message" = "DuckDuckGo Network Protection is currently invite-only."; + +/* Message for the network protection invite success view */ +"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; + +/* Title for the network protection invite success view */ +"network.protection.invite.success.title" = "Success! You’re in."; + +/* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ +"network.protection.status.connected.format" = "Connected - %@"; + +/* The label for the NetP VPN when connecting */ +"network.protection.status.connecting" = "Connecting..."; + +/* The label for the NetP VPN when disconnected */ +"network.protection.status.disconnected" = "Not connected"; + +/* The label for the NetP VPN when disconnecting */ +"network.protection.status.disconnecting" = "Disconnecting..."; + +/* Message label text for the netP status view */ +"network.protection.status.header.message" = "DuckDuckGo's VPN secures all of your device's Internet traffic anytime, anywhere."; + +/* Header title label text for the status view when netP is disconnected */ +"network.protection.status.header.title.off" = "Network Protection is Off"; + +/* Header title label text for the status view when netP is connected */ +"network.protection.status.header.title.on" = "Network Protection is On"; + +/* The status view 'Share Feedback' button which is shown inline on the status view after the \(netPInviteOnlyMessage) text */ +"network.protection.status.menu.share.feedback" = "Share Feedback"; + +/* Connection details label shown in NetworkProtection's status view. */ +"network.protection.status.view.connection.details" = "Connection Details"; + +/* Generic connection failed error message shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.message" = "Please try again later."; + +/* Generic connection failed error title shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; + +/* IP Address label shown in NetworkProtection's status view. */ +"network.protection.status.view.ip.address" = "IP Address"; + +/* Location label shown in NetworkProtection's status view. */ +"network.protection.status.view.location" = "Location"; + +/* Title label text for the status view when netP is disconnected */ +"network.protection.status.view.title" = "Network Protection"; + /* Do not translate - stringsdict entry */ "number.of.tabs" = "number.of.tabs"; +/* No comment provided by engineer. */ +"Ok" = "Хорошо"; + /* Text displayed on notification appearing in the address bar when the browser dismissed the cookie popup automatically rejecting it */ "omnibar.notification.cookies-managed" = "Kуки-файлы выбраны"; @@ -1147,6 +1525,18 @@ /* ’Link’ is link on a website */ "open.externally.failed" = "Приложение для открытия этой ссылки не найдено"; +/* No comment provided by engineer. */ +"Please enter which app on your device is broken." = "Укажите, какое приложение на вашем устройстве работает со сбоями."; + +/* Activate button */ +"pm.activate" = "Reactivate"; + +/* Cancel button */ +"pm.cancel" = "Cancel"; + +/* Deactivate button */ +"pm.deactivate" = "Deactivate"; + /* No comment provided by engineer. */ "preserveLogins.domain.list.footer" = "Чтобы вам не приходилось каждый раз входить в свою учетную запись, сайты используют куки-файлы. Если сделать сайт огнеупорным, мы не будем стирать его куки-файлы, даже если вы воспользуетесь кнопкой «Огонь». Но мы по-прежнему будем блокировать сторонние трекеры, в том числе на огнеупорных сайтах."; @@ -1216,8 +1606,8 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Избранное"; -/* No comment provided by engineer. */ -"settings.about.text" = "DuckDuckGo задает новый стандарт доверия в сети.\n\nНаш браузер оснащен всеми функциями защиты личных данных в интернете, включая блокировку трекеров, надежное шифрование и конфиденциальный поиск DuckDuckGo.\n\nИнтернет — не место для постоянной слежки, а скрыться от посторонних глаз должно быть не сложнее, чем закрыть жалюзи на окнах."; +/* about page */ +"settings.about.text" = "DuckDuckGo — независимая компания, которая c 2008 года предоставляет услуги защиты личных данных в интернете. Мы предлагаем простое решение для тех, кому надоело отслеживание онлайн. Своим примером DuckDuckGo показывает, что защита конфиденциальности в Сети не требует компромиссов.\n\nВ универсальном браузере DuckDuckGo вы найдете все те же привычные функции — вкладки, закладки, пароли, — а также более [десятка мощных средств защиты личной информации](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/), которых нет в стандартных пакетах других популярных веб-обозревателей. Наш всесторонний набор инструментов скроет от посторонних глаз всю вашу онлайн-активность, включая поиск, просмотр сайтов и чтение почты.\n\nСервисы DuckDuckGo не требуют особых технических познаний или работы со сложными настройками. Достаточно сменить свой браузер на DuckDuckGo на всех устройствах, и конфиденциальность онлайн станет вашим спутником по умолчанию.\n\nЕсли же вас интересует наша «кухня», вы можете почитать о том, как устроены инструменты защиты DuckDuckGo, на [страницах нашего справочного центра](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/)."; /* Report a Broken Site screen confirmation button */ "siteFeedback.buttonText" = "Отправить жалобу"; @@ -1237,6 +1627,9 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "На каком сайте возникает проблема?"; +/* No comment provided by engineer. */ +"sync.remove-device.message" = "«%@» утратит доступ к синхронизированным данным."; + /* Accessibility label on remove button */ "tab.close.home" = "Закрыть домашнюю вкладку"; @@ -1309,6 +1702,57 @@ /* Voice-search footer note with on-device privacy warning */ "voiceSearch.footer.note" = "Аудио обрабатывается непосредственно на устройстве, не хранится и не передается никому, даже DuckDuckGo."; +/* Title for the button to enable push notifications in system settings */ +"waitlist.allow-notifications" = "Разрешить уведомления"; + +/* Body text for the waitlist notification */ +"waitlist.available.notification.body" = "Откройте приглашение"; + +/* Title for the copy action */ +"waitlist.copy" = "Копировать"; + +/* Label text for the invite code */ +"waitlist.invite-code" = "Код приглашения"; + +/* Step title on the invite screen */ +"waitlist.invite-screen.step.title" = "Шаг %d"; + +/* Title for the invite code screen */ +"waitlist.invite-screen.youre-invited" = "Вас пригласили!"; + +/* Title for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.join" = "Приглашаем встать в очередь"; + +/* Temporary status text for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.joining" = "Добавление в список ожидания..."; + +/* Title for the Share Link button */ +"waitlist.join-waitlist-screen.share-link" = "Поделиться ссылкой"; + +/* Notification text for the waitlist */ +"waitlist.joined.no-notification.get-notification" = "ОПОВЕСТИТЬ"; + +/* Title for the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.get-notification-confirmation-title" = "Сообщить, когда придет ваша очередь?"; + +/* Cancel button in the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.no-thanks" = "Нет, спасибо"; + +/* Text used for the Notifications Disabled state */ +"waitlist.notification.disabled" = "Мы можем уведомить вас, когда подойдет ваша очередь, но для этого нужно включить уведомления от DuckDuckGo."; + +/* Privacy disclaimer for the Waitlist feature */ +"waitlist.privacy-disclaimer" = "Чтобы записаться в список ожидания, не нужно делиться личной информацией. Временная метка, хранящаяся только на вашем устройстве, позволит нам определить, когда подойдет ваша очередь, о чем мы сообщим вам дополнительно."; + +/* Title for the queue screen */ +"waitlist.queue-screen.on-the-list" = "Вы в списке!"; + +/* Title for the settings subtitle */ +"waitlist.settings.download-available" = "Готово к загрузке"; + +/* Title for the share sheet entry */ +"waitlist.share-sheet.title" = "Вас пригласили!"; + /* Confirmation message */ "web.url.remove.favorite.done" = "Страница удалена из избранного"; @@ -1333,3 +1777,51 @@ /* Alert title explaining the message is shown by a website */ "webJSAlert.website-message.format" = "Сообщение от %@:"; +/* No comment provided by engineer. */ +"Welcome to the Duck Side!" = "Добро пожаловать на Утиную сторону!"; + +/* Title for the Windows waitlist notification */ +"windows-waitlist.available.notification.title" = "DuckDuckGo для Windows!"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-1.description" = "Чтобы загрузить DuckDuckGo для Windows, откройте эту ссылку:"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-2.description" = "Откройте установщик DuckDuckGo в «Загрузках», выберите «Установить» и введите пригласительный код."; + +/* Subtitle for the Windows Waitlist Invite screen */ +"windows-waitlist.invite-screen.subtitle" = "Хотите использовать DuckDuckGo на Windows?"; + +/* Title for the Windows waitlist button redirecting to Mac waitlist */ +"windows-waitlist.join-waitlist-screen.mac-waitlist" = "Ищете версию для Mac?"; + +/* Title for the Join Windows Waitlist screen */ +"windows-waitlist.join-waitlist-screen.try-duckduckgo-for-windows" = "Ранний доступ к DuckDuckGo для Windows"; + +/* Message for the alert to confirm enabling notifications */ +"windows-waitlist.joined.no-notification.get-notification-confirmation-message" = "Когда ваш экземпляр будет готов к загрузке, мы отправим вам уведомление. "; + +/* Label text for the Joined Waitlist state with notifications declined */ +"windows-waitlist.joined.notifications-declined" = "Приглашение протестировать DuckDuckGo для Windows отобразится здесь. Зайдите сюда позже. Мы также можем отправить вам уведомление, когда придет ваша очередь."; + +/* Label text for the Joined Waitlist state with notifications enabled */ +"windows-waitlist.joined.notifications-enabled" = "Когда ваша копия будет готова к загрузке, мы отправим вам уведомление."; + +/* Title for the settings subtitle */ +"windows-waitlist.settings.browse-privately" = "Полная конфиденциальность в Интернете, благодаря нашему приложению для Windows"; + +/* Message used when sharing to iMessage. Parameter is an eight digit invite code. */ +"windows-waitlist.share-sheet.invite-code-message" = "Вас пригласили!\n\nХотите использовать DuckDuckGo на Windows?\n\nШаг 1\nЗагрузите файл, посетив эту страницу на устройстве Windows:\nhttps://duckduckgo.com/windows\n\nШаг 2\nОткройте установщик DuckDuckGo в «Загрузках», выберите «Установить» и введите пригласительный код.\n\nКод приглашения: %@"; + +/* Message used when sharing to iMessage */ +"windows-waitlist.share-sheet.message" = "Конфиденциальность в интернете на Windows? Легко!\n\nЗагрузите наше приложение по ссылке:\nhttps://duckduckgo.com/windows"; + +/* Summary text for the Windows browser waitlist */ +"windows-waitlist.summary" = "В DuckDuckGo для Windows есть все необходимое для защиты данных в Сети: приватный поиск, принудительное шифрование, блокировка трекеров и всплывающих куки, а также перспектива разработок передовых средств обеспечения безопасности."; + +/* Title for the Windows Waitlist feature */ +"windows-waitlist.title" = "Приложение DuckDuckGo для Windows"; + +/* Title for the Windows browser download link page */ +"windows-waitlist.waitlist-download-screen.try-duckduckgo-for-windows" = "DuckDuckGo для Windows!"; + diff --git a/DuckDuckGo/ru.lproj/Settings.strings b/DuckDuckGo/ru.lproj/Settings.strings index 174c044a8e..7f84888c62 100644 --- a/DuckDuckGo/ru.lproj/Settings.strings +++ b/DuckDuckGo/ru.lproj/Settings.strings @@ -37,6 +37,9 @@ /* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ "9Ko-0g-T3h.text" = "Размер текста"; +/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ +"9kt-6R-XiZ.text" = "Название"; + /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "При запуске приложения"; @@ -73,6 +76,12 @@ /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Глобальный контроль конфиденциальности (GPC)"; +/* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ +"CxT-QK-iVn.title" = "Добавить"; + +/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ +"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; + /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "После выхода из приложения, через 15 минут бездействия"; @@ -151,6 +160,9 @@ /* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ "HLr-R8-xxF.text" = "Предпросмотр долгим нажатием"; +/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ +"hoT-Nu-KXP.text" = "Полная конфиденциальность в Интернете, благодаря нашему приложению для Windows"; + /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Защита конфиденциальности включена на всех сайтах"; @@ -163,6 +175,12 @@ /* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ "m23-t6-9cb.text" = "Оставьте нам отзыв"; +/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ +"mLn-1x-Fl5.title" = "Положение адресной строки"; + +/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ +"mqV-pf-NZ1.text" = "Положение адресной строки"; + /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Разрешить DuckDuckGo управлять окнами выбора куки-файлов"; @@ -175,15 +193,27 @@ /* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ "oM7-1o-9oY.text" = "Несколько слов о DuckDuckGo"; +/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ +"opn-JO-idF.text" = "Вверх"; + /* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ "OxE-MQ-uJk.headerTitle" = "DuckDuckGo также предлагает..."; +/* Class = "UILabel"; text = "Sync"; ObjectID = "oXN-ez-gct"; */ +"oXN-ez-gct.text" = "Синхронизация"; + /* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ "P0F-ts-ekd.text" = "Интернет без трекеров с нашим приложением для Mac "; +/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ +"qah-gb-udB.text" = "Network Protection"; + /* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ "qeN-SV-zy7.text" = "Метка"; +/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ +"RQ8-H1-Ez1.text" = "Приложение DuckDuckGo для Windows"; + /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Размер текста"; @@ -232,8 +262,8 @@ /* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ "yvj-LL-MiR.text" = "Добавьте приложение на док-панель"; -/* Class = "UILabel"; text = "DuckDuckGo Desktop App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "Настольное приложение DuckDuckGo"; +/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ +"Yz9-17-qnn.text" = "Приложение DuckDuckGo для Mac"; /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Новая вкладка"; diff --git a/DuckDuckGo/sk.lproj/Feedback.strings b/DuckDuckGo/sk.lproj/Feedback.strings index 8c24249f76..41fb371247 100644 --- a/DuckDuckGo/sk.lproj/Feedback.strings +++ b/DuckDuckGo/sk.lproj/Feedback.strings @@ -28,9 +28,6 @@ /* Class = "UIBarButtonItem"; title = "Back"; ObjectID = "JF8-Ow-zXL"; */ "JF8-Ow-zXL.title" = "Späť"; -/* Class = "UIBarButtonItem"; title = "Submit"; ObjectID = "jr6-Al-4fr"; */ -"jr6-Al-4fr.title" = "Potvrdiť"; - /* Class = "UIButton"; normalTitle = "Submit"; ObjectID = "lsB-Kq-U8p"; */ "lsB-Kq-U8p.normalTitle" = "Potvrdiť"; @@ -58,9 +55,6 @@ /* Class = "UINavigationItem"; title = "Report Broken Site"; ObjectID = "WjT-HM-yim"; */ "WjT-HM-yim.title" = "Nahlásiť nefunkčnú stránku"; -/* Class = "UILabel"; text = "Broken Site Reason"; ObjectID = "Wx3-Vb-ItS"; */ -"Wx3-Vb-ItS.text" = "Dôvod prerušenia prevádzky webovej stránky"; - /* Class = "UINavigationItem"; title = "Share Feedback"; ObjectID = "Xb0-Zu-opM"; */ "Xb0-Zu-opM.title" = "Zdieľať spätnú väzbu"; diff --git a/DuckDuckGo/sk.lproj/Localizable.strings b/DuckDuckGo/sk.lproj/Localizable.strings index 7704624637..2cc71be60f 100644 --- a/DuckDuckGo/sk.lproj/Localizable.strings +++ b/DuckDuckGo/sk.lproj/Localizable.strings @@ -1,6 +1,9 @@ /* No comment provided by engineer. */ "%@" = "%@"; +/* No comment provided by engineer. */ +"%@ [%@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)" = "%1$@ [%2$@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)"; + /* Buton label for Edit action */ "action.generic.edit" = "Upraviť"; @@ -103,6 +106,12 @@ /* Share action */ "action.title.share" = "Zdieľať"; +/* Settings label for bottom position for the address bar */ +"address.bar.bottom" = "Spodná časť"; + +/* Settings label for top position for the address bar */ +"address.bar.top" = "Hore"; + /* No comment provided by engineer. */ "addWidget.button" = "Pridať miniaplikáciu"; @@ -142,6 +151,225 @@ /* Shown on authentication screen */ "app.authentication.unlock" = "Odomknite DuckDuckGo."; +/* First part of about page content (note the trailing space) */ +"appTP.about.content1" = "You’ve probably heard about companies like Google and Facebook tracking you behind the scenes on third-party websites. But did you know they also track your personal information through apps on your device?\n\nIn 2022, DuckDuckGo found that "; + +/* Second part of about page content (note the trailing space) */ +"appTP.about.content2" = "over 85% of free iOS apps tested contained hidden trackers from other companies."; + +/* Third part of about page content (note the leading space) */ +"appTP.about.content3" = " Of the 395 apps tested, 60% sent data to Google. This happens even while you’re not using your device.\n\nTrackers in apps may have access to a lot more information than their website tracker cousins, such as your location down to which floor of a building you're on, how often you play games while at work, and when and how long you sleep each day. Even if you haven’t given apps explicit permission to collect data, they can still take it without your knowledge.\n\nTracking networks like Facebook and Google use these little pieces of information to build a digital profile about you. With it, tracking networks can manipulate what you see online and allow advertisers to bid on access to you based on your data.\n\nTrackers in apps is a BIG problem for privacy. But DuckDuckGo has a solution that can help.\n\nWhen enabled in the DuckDuckGo Privacy Browser app, App Tracking Protection blocks many trackers in other apps, not just the trackers we find on websites when you browse. These dual layers of protection reduce what companies know about you overall, so you can use your apps with more peace of mind, knowing you’re more protected."; + +/* Navigation Title for AppTP about page */ +"appTP.about.navTitle" = "About App Trackers"; + +/* Title for AppTP about page */ +"appTP.about.title" = "What Are App Trackers?"; + +/* Title for 'Report an Issue' button in the activity view. */ +"appTP.activityView.reportIssue" = "Report Issue"; + +/* Text label for switch that turns blocking on or off for a tracker */ +"appTP.blockTrackerText" = "Block this Tracker"; + +/* Detail string describing what AppTP is */ +"appTP.cell.detail" = "Blokovanie sledovacích nástrojov aplikácií vo vašom zariadení"; + +/* String indicating AppTP is disabled when viewed from the settings screen */ +"appTP.cell.disabled" = "Zakázané"; + +/* String indicating AppTP is enabled when viewed from the settings screen */ +"appTP.cell.enabled" = "Zapnuté"; + +/* Info string informing the user what App Tracking Protection does. */ +"appTP.empty.disabled.info" = "Povoľte ochranu pred sledovaním aplikácií, aby sme mohli blokovať problematické sledovania v iných aplikáciách."; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.heading" = "We’re blocking hidden trackers"; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.info" = "Come back soon to see a list of all the app trackers we’ve blocked."; + +/* First answer for AppTP FAQ page */ +"appTP.faq.answer1" = "App Tracking Protection blocks app trackers from other companies, like when Facebook tries to track you in a banking app. Companies may still track you in apps they own."; + +/* Second answer for AppTP FAQ page */ +"appTP.faq.answer2" = "Yes! App Tracking Protection works across all apps on your device to block the most common hidden trackers we find trying to collect your personal info."; + +/* Third answer for AppTP FAQ page */ +"appTP.faq.answer3" = "We currently only block the most common trackers that we find on iOS. This helps us to comprehensively test App Tracking Protection and lower frequency of app breakage, while blocking up to 70% of all tracking requests."; + +/* Fourth answer for AppTP FAQ page */ +"appTP.faq.answer4" = "You’ll be asked to set up a virtual private network (VPN) connection, but you don't need to install a VPN app for App Tracking Protection to work.\n\nThis permission, which works only on your device, allows App Tracking Protection to monitor network traffic so that it can block known trackers."; + +/* Fifth answer for AppTP FAQ page */ +"appTP.faq.answer5" = "You can use App Tracking Protection at the same time as using an IKEv2 protocol VPN app on an iOS device. You won’t be able to use App Tracking Protection on an iOS device if you’re using a VPN app that uses a different type of protocol, like WireGuard or OpenVPN type VPNs."; + +/* Sixth answer for AppTP FAQ page */ +"appTP.faq.answer6" = "A VPN sends your data from the device to its own server, where it secures and anonymizes your data from prying eyes. However, this allows the VPN company to see your network traffic.\n\nApp Tracking Protection is different. Instead of sending your data to a VPN server, App Tracking Protection works only on your device, sitting between your apps and the servers they talk to.\n\nWhenever App Tracking Protection recognizes a known tracker, it blocks the tracker from sending personal information (such as your IP address, activity, and device details) off your device. All other traffic reaches its destination, so your apps work normally."; + +/* Seventh answer for AppTP FAQ page */ +"appTP.faq.answer7" = "App Tracking Protection works only on your device and doesn’t send your data off your device to DuckDuckGo. We don’t collect or store any data from your apps."; + +/* First question for AppTP FAQ page */ +"appTP.faq.question1" = "How does App Tracking Protection work?"; + +/* Second question for AppTP FAQ page */ +"appTP.faq.question2" = "Does App Tracking Protection block trackers in all apps on my device?"; + +/* Third question for AppTP FAQ page */ +"appTP.faq.question3" = "Does App Tracking Protection block all app trackers?"; + +/* Fourth question for AppTP FAQ page */ +"appTP.faq.question4" = "Why does App Tracking Protection use a VPN connection?"; + +/* Fifth question for AppTP FAQ page */ +"appTP.faq.question5" = "Will App Tracking Protection work if I also use a VPN app?"; + +/* Sixth question for AppTP FAQ page */ +"appTP.faq.question6" = "How is App Tracking Protection different from a VPN?"; + +/* Seventh question for AppTP FAQ page */ +"appTP.faq.question7" = "Is my data private?"; + +/* Title for AppTP FAQ page */ +"appTP.faq.title" = "App Tracking Protection FAQ"; + +/* Do not translate. StringsDict entry -- Count part of string 'App Tracking Protection blocked x tracking attempts today' */ +"appTP.home.blockedCount" = "appTP.home.blockedCount"; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the trailing space) */ +"appTP.home.blockedPrefix" = "Ochrana pred sledovaním aplikácií je zablokovaná "; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the leading space) */ +"appTP.home.blockedSuffix" = " vo svojich aplikáciách ešte dnes."; + +/* Prefix of string 'App Tracking Protection disabled. Tap to re-enable.' (note the trailing space) */ +"appTP.home.disabledPrefix" = "App Tracking Protection disabled. "; + +/* Suffix of string 'App Tracking Protection disabled. Tap to re-enable.' */ +"appTP.home.disabledSuffix" = "Tap to continue blocking tracking attempts across your apps."; + +/* Text indicating the tracking event occured 'just now'. Example: Last attempt 'just now' */ +"appTP.justNow" = "Práve teraz"; + +/* View to manage trackers for AppTP. Allows the user to turn trackers on or off. */ +"appTP.manageTrackers" = "Správa sledovaní"; + +/* Button title for AppTP onboarding */ +"appTP.onboarding.continueButton" = "Continue"; + +/* Button title for AppTP onboarding to enable AppTP */ +"appTP.onboarding.enableeButton" = "Enable App Tracking Protection"; + +/* Button title for AppTP onboarding to learn more about AppTP */ +"appTP.onboarding.learnMoreButton" = "Learn More"; + +/* First part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info1" = "Over 85% of free iOS apps"; + +/* Second part of info on the first AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page1Info2" = " we’ve tested allow other companies to track your personal information, even when you’re sleeping."; + +/* Third part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info3" = "See who we catch trying to track you in your apps and take back control."; + +/* First part of info on the second AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page2Info1" = "App Tracking Protection "; + +/* Second part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info2" = "detects and blocks app trackers from other companies,"; + +/* Third part of info on the second AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page2Info3" = " like when Google attempts to track you in a health app."; + +/* Fourth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info4" = "It’s free,"; + +/* Fifth part of info on the second AppTP onboarding page (note the leading and trailing space) */ +"appTP.onboarding.page2Info5" = " and you can enjoy your apps as you normally would. Working in the background, it helps "; + +/* Sixth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info6" = "protect you night and day."; + +/* First part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info1" = "App Tracking Protection is not a VPN."; + +/* Second part of info on the third AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page3Info2" = " However, your device will recognize it as one. This is because it uses a local VPN connection to work."; + +/* Third part of info on the third AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page3Info3" = "App Tracking Protection is different. "; + +/* Fourth part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info4" = "It never routes app data through an external server."; + +/* Title for first AppTP onboarding page */ +"appTP.onboarding.title1" = "One easy step for better app privacy!"; + +/* Title for second AppTP onboarding page */ +"appTP.onboarding.title2" = "How does it work?"; + +/* Title for third AppTP onboarding page */ +"appTP.onboarding.title3" = "Who sees your data?"; + +/* Breakage report app name label */ +"appTP.report.appLabel" = "Which app is having issues?"; + +/* Breakage report app name placeholder */ +"appTP.report.appPlaceholder" = "App name"; + +/* Breakage report category label */ +"appTP.report.categoryLabel" = "Čo sa deje?"; + +/* Breakage report comment label */ +"appTP.report.commentLabel" = "Comments"; + +/* Breakage report comment placeholder */ +"appTP.report.commentPlaceholder" = "Add additional details"; + +/* Breakage report footer explaining what is collected in the breakage report */ +"appTP.report.footer" = "In addition to the details entered into this form, your app issue report will contain:\n• A list of trackers blocked in the last 10 min\n• Whether App Tracking Protection is enabled\n• Aggregate DuckDuckGo app diagnostics"; + +/* Breakage report submit button */ +"appTP.report.submit" = "Potvrdiť"; + +/* Breakage report form title */ +"appTP.report.title" = "Report Issue"; + +/* Breakage report succcess message */ +"appTP.report.toast" = "Ďakujeme! Spätná väzba bola odoslaná"; + +/* Cancel button for 'Report an Issue' alert. */ +"appTP.reportAlert.cancel" = "Teraz nie"; + +/* Confirm button for 'Report an Issue' alert. */ +"appTP.reportAlert.confirm" = "Nahlásiť problém"; + +/* Message for 'Report an Issue' alert. */ +"appTP.reportAlert.message" = "Let us know if you disabled App Tracking Protection for this specific tracker because it caused app issues. Your feedback helps us improve!"; + +/* Title for 'Report an Issue' alert. */ +"appTP.reportAlert.title" = "Report Issue?"; + +/* Toast notification diplayed after restoring the blocklist to default settings */ +"appTP.restoreDefaultsToast" = "Default settings restored"; + +/* Button to restore the blocklist to its default state. */ +"appTP.restoreDefualts" = "Obnovenie predvolených nastavení"; + +/* Title for the App Tracking Protection feature */ +"appTP.title" = "Ochrana pred sledovaním činnosti v aplikáciách"; + +/* Text indicating when the tracker was last allowed. Example: Last attempt allowed (timeString) */ +"appTP.trackerAllowedTimestamp" = "Posledný povolený pokus %@"; + +/* Text indicating when the tracker was last blocked. Example: Last attempt blocked (timeString) */ +"appTP.trackerBlockedTimestamp" = "Posledný zablokovaný pokus %@"; + +/* Do not translate. StringsDict entry -- Subtitle for tracking attempts in App Tracking Protection Activity View. Example: (count) tracking attempts */ +"appTP.trackingattempts" = "appTP.trackingattempts"; + /* Authentication Alert Sign In Button */ "auth.alert.login.button" = "Prihlásiť sa"; @@ -301,6 +529,18 @@ /* Toast message when a login item without a title is deleted */ "autofill.logins.list.login-deleted-message-no-title" = "Prihlasovacie údaje boli odstránené"; +/* Title for a button that allows a user to reset their list of never saved sites */ +"autofill.logins.list.never.saved" = "Obnovenie vylúčených lokalít"; + +/* Cancel button for resetting list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.cancel" = "Zrušiť"; + +/* Confirm button to reset list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.confirm" = "Obnovenie vylúčených lokalít"; + +/* Alert title */ +"autofill.logins.list.never.saved.reset.action.title" = "Ak obnovíte vylúčené lokality, pri ďalšom prihlásení na niektorú z týchto lokalít dostanete výzvu na uloženie prihlasovacích údajov."; + /* Placeholder for search field on autofill login listing */ "autofill.logins.list.search-placeholder" = "Vyhľadávanie prihlásení"; @@ -335,7 +575,7 @@ "autofill.logins.prompt.password.button.title" = "Heslo pre %@"; /* Title for autofill login prompt */ -"autofill.logins.prompt.title" = "Použiť uložené prihlasovacie údaje?"; +"autofill.logins.prompt.title" = "Použiť uložené heslo?"; /* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ "autofill.logins.search.no-results.subtitle" = "pre „%@”"; @@ -385,27 +625,24 @@ /* Title for the alert dialog telling the user an updated username is no longer a private email address */ "autofill.removed.duck.address.title" = "Používateľské meno Private Duck Address bolo odstránené"; +/* CTA displayed on modal asking if the user never wants to be prompted to save a login for this website agin */ +"autofill.save-login.never-prompt.CTA" = "Nikdy sa nepýtať na túto stránku"; + /* Message displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.message" = "Prihlasovacie údaje sú bezpečne uložené v zariadení v ponuke Prihlásenia."; +"autofill.save-login.new-user.message" = "Heslá sú bezpečne uložené na vašom zariadení v ponuke Prihlásenia."; /* Title displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.title" = "Chcete, aby DuckDuckGo uložil vaše prihlasovacie údaje?"; +"autofill.save-login.new-user.title" = "Chcete, aby DuckDuckGo uložil vaše heslo?"; /* Cancel CTA displayed on modal asking for the user to save the login */ "autofill.save-login.not-now.CTA" = "Neukladať"; -/* Confirm CTA displayed on modal asking for the user to save the login */ -"autofill.save-login.save.CTA" = "Uložiť prihlasovacie údaje"; - /* Title displayed on modal asking for the user to save the login */ "autofill.save-login.title" = "Uložiť prihlasovacie údaje?"; /* Confirm CTA displayed on modal asking for the user to save the password */ "autofill.save-password.save.CTA" = "Uložiť heslo"; -/* Title displayed on modal asking for the user to save the password */ -"autofill.save-password.title" = "Uložiť heslo?"; - /* Accessibility title for a Show Password button displaying actial password instead of ***** */ "autofill.show-password" = "Zobraziť heslo"; @@ -413,7 +650,7 @@ "autofill.signin.to.manage" = "%@ na správu vašich Duck adries na tomto zariadení."; /* Message displayed on modal asking for the user to update the password */ -"autofill.update-password.message" = "DuckDuckGo bude aktualizovať tieto uložené prihlasovacie údaje vo vašom zariadení."; +"autofill.update-password.message" = "DuckDuckGo aktualizuje toto uložené heslo vo vašom zariadení."; /* Confirm CTA displayed on modal asking for the user to update the password */ "autofill.update-password.save.CTA" = "Aktualizovať heslo"; @@ -442,6 +679,15 @@ /* Placeholder in the add bookmark form */ "bookmark.address.placeholder" = "www.example.com"; +/* Delete bookmark alert message */ +"bookmark.delete.alert.message" = "Týmto sa zmaže vaša záložka pre „%@“"; + +/* Delete bookmark alert title */ +"bookmark.delete.alert.title" = "Delete?"; + +/* The message shown after a bookmark has been deleted */ +"bookmark.deleted.toast" = "Záložka bola zmazaná"; + /* Delete bookmark folder alert delete button */ "bookmark.deleteFolderAlert.deleteButton" = "Vymazať"; @@ -538,9 +784,21 @@ /* Broken Site Category */ "brokensite.category.videos" = "Video sa neprehráva"; +/* Broken Site Category Placeholder */ +"brokensite.categoryPlaceholder" = "Vyberte problém zo zoznamu..."; + +/* Broken Site Category Section Title */ +"brokensite.categoryTitle" = "VYBERTE KATEGÓRIU"; + +/* Broken Site Comment Placeholder */ +"brokensite.commentPlaceholder" = "Zdieľanie ďalších podrobností nám môže pomôcť vyriešiť tento problém"; + /* Broken Site Section Title */ "brokensite.sectionTitle" = "POPÍŠTE, ČO SA STALO"; +/* No comment provided by engineer. */ +"bucket: %@" = "vedro: %@"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Minulý mesiac"; @@ -772,6 +1030,39 @@ /* Empty search placeholder on bookmarks search */ "empty.search" = "Nenašli sa žiadne zhody"; +/* No comment provided by engineer. */ +"Error" = "Chyba"; + +/* Button title to Sign In */ +"error.email-protection-sign-in.action" = "Prihlásiť sa"; + +/* Alert message */ +"error.email-protection-sign-in.body" = "Pre opätovné povolenie funkcii ochrany e-mailu v tomto prehliadači sa prihláste ešte raz, prosím."; + +/* Alert title */ +"error.email-protection-sign-in.title" = "Chyba ochrany e-mailu"; + +/* Button title to open device settings */ +"error.insufficient-disk-space.action" = "Otvoriť nastavenia"; + +/* Alert message */ +"error.insufficient-disk-space.body" = "Zdá sa, že vo vašom zariadení sa minul úložný priestor. Ak chcete pokračovať, uvoľnite miesto, prosím."; + +/* Alert title */ +"error.insufficient-disk-space.title" = "Nedostatočný úložný priestor"; + +/* Button title that is shutting down the app */ +"error.preemptive-crash.action" = "Zatvoriť aplikáciu"; + +/* Alert message */ +"error.preemptive-crash.body" = "Zdá sa, že aplikácia má problém a je potrebné ju zavrieť. Na pokračovanie ju opäť otvorte."; + +/* Alert title */ +"error.preemptive-crash.title" = "Zistený problém s aplikáciou"; + +/* Generic error message on a dialog for when the cause is not known. */ +"error.unknown.try.again" = "An unknown error has occurred"; + /* No comment provided by engineer. */ "favorite" = "Obľúbené položky"; @@ -991,6 +1282,18 @@ /* Home tab title */ "homeTab.title" = "Domov"; +/* OK title for invite screen alert dismissal button */ +"invite.alert.ok.button" = "OK"; + +/* Continue button on an invite dialog */ +"invite.dialog.continue.button" = "Continue"; + +/* Get Started button on an invite dialog */ +"invite.dialog.get.started.button" = "Get Started"; + +/* Message to show after user enters an unrecognized invite code */ +"invite.dialog.unrecognized.code.message" = "We didn’t recognize this Invite Code."; + /* No comment provided by engineer. */ "keyCommandAddBookmark" = "Pridať záložku"; @@ -1057,15 +1360,15 @@ /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "DuckDuckGo pre Mac má rýchlosť, ktorú potrebujete, funkcie prehliadania, ktoré očakávate, a obsahuje naše prvotriedne privacy essentials."; -/* Body text for the macOS waitlist notification */ -"mac-waitlist.available.notification.body" = "Otvorte svoju pozvánku"; - /* Title for the macOS waitlist notification */ "mac-waitlist.available.notification.title" = "DuckDuckGo pre Mac je pripravený!"; /* Title for the copy action */ "mac-waitlist.copy" = "Kopírovať"; +/* Description text above the Share Link button */ +"mac-waitlist.join-waitlist-screen.on-your-computer-go-to" = "V počítači so systémom Windows prejdite na:"; + /* Description text above the Share Link button */ "mac-waitlist.join-waitlist-screen.on-your-mac-go-to" = "V počítači Mac prejdite na:"; @@ -1078,6 +1381,9 @@ /* Disclaimer for the Join Waitlist screen */ "mac-waitlist.join-waitlist-screen.windows" = "Verzia pre Windows bude k dispozícii čoskoro!"; +/* Title for the macOS waitlist button redirecting to Windows waitlist */ +"mac-waitlist.join-waitlist-screen.windows-waitlist" = "Hľadáte verziu pre systém Windows?"; + /* Title for the settings subtitle */ "mac-waitlist.settings.browse-privately" = "Prezerajte súkromne stránky s našou aplikáciou pre Mac"; @@ -1088,7 +1394,7 @@ "mac-waitlist.share-sheet.title" = "DuckDuckGo pre Mac"; /* Title for the Mac Waitlist feature */ -"mac-waitlist.title" = "Aplikácia DuckDuckGo pre počítač"; +"mac-waitlist.title" = "Aplikácia DuckDuckGo pre Mac"; /* No comment provided by engineer. */ "menu.button.hint" = "Ponuka prehliadania"; @@ -1105,9 +1411,81 @@ /* Edit button */ "navigation.title.edit" = "Upraviť"; +/* String indicating NetP is connected when viewed from the settings screen */ +"netP.cell.connected" = "Connected"; + +/* String indicating NetP is disconnected when viewed from the settings screen */ +"netP.cell.disconnected" = "Not connected"; + +/* Title for the Network Protection feature */ +"netP.title" = "Network Protection"; + +/* Message for the network protection invite dialog */ +"network.protection.invite.dialog.message" = "Enter your invite code to get started."; + +/* Title for the network protection invite screen */ +"network.protection.invite.dialog.title" = "You're invited to try Network Protection"; + +/* Prompt for the network protection invite code text field */ +"network.protection.invite.field.prompt" = "Invite Code"; + +/* Message explaining that netP is invite only */ +"network.protection.invite.only.message" = "DuckDuckGo Network Protection is currently invite-only."; + +/* Message for the network protection invite success view */ +"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; + +/* Title for the network protection invite success view */ +"network.protection.invite.success.title" = "Success! You’re in."; + +/* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ +"network.protection.status.connected.format" = "Connected - %@"; + +/* The label for the NetP VPN when connecting */ +"network.protection.status.connecting" = "Connecting..."; + +/* The label for the NetP VPN when disconnected */ +"network.protection.status.disconnected" = "Not connected"; + +/* The label for the NetP VPN when disconnecting */ +"network.protection.status.disconnecting" = "Disconnecting..."; + +/* Message label text for the netP status view */ +"network.protection.status.header.message" = "DuckDuckGo's VPN secures all of your device's Internet traffic anytime, anywhere."; + +/* Header title label text for the status view when netP is disconnected */ +"network.protection.status.header.title.off" = "Network Protection is Off"; + +/* Header title label text for the status view when netP is connected */ +"network.protection.status.header.title.on" = "Network Protection is On"; + +/* The status view 'Share Feedback' button which is shown inline on the status view after the \(netPInviteOnlyMessage) text */ +"network.protection.status.menu.share.feedback" = "Share Feedback"; + +/* Connection details label shown in NetworkProtection's status view. */ +"network.protection.status.view.connection.details" = "Connection Details"; + +/* Generic connection failed error message shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.message" = "Please try again later."; + +/* Generic connection failed error title shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; + +/* IP Address label shown in NetworkProtection's status view. */ +"network.protection.status.view.ip.address" = "IP Address"; + +/* Location label shown in NetworkProtection's status view. */ +"network.protection.status.view.location" = "Location"; + +/* Title label text for the status view when netP is disconnected */ +"network.protection.status.view.title" = "Network Protection"; + /* Do not translate - stringsdict entry */ "number.of.tabs" = "number.of.tabs"; +/* No comment provided by engineer. */ +"Ok" = "OK"; + /* Text displayed on notification appearing in the address bar when the browser dismissed the cookie popup automatically rejecting it */ "omnibar.notification.cookies-managed" = "Spravované súbory cookie"; @@ -1147,6 +1525,18 @@ /* ’Link’ is link on a website */ "open.externally.failed" = "Aplikácia potrebná na otvorenie odkazu sa nenašla"; +/* No comment provided by engineer. */ +"Please enter which app on your device is broken." = "Zadajte, ktorá aplikácia vo vašom zariadení je nefunkčná."; + +/* Activate button */ +"pm.activate" = "Reactivate"; + +/* Cancel button */ +"pm.cancel" = "Cancel"; + +/* Deactivate button */ +"pm.deactivate" = "Deactivate"; + /* No comment provided by engineer. */ "preserveLogins.domain.list.footer" = "Webové stránky sa spoliehajú na súbory cookie, aby ste zostali prihlásení. Keď webovú stránku zabezpečíte funkciou ohňovzdornosti, súbory cookie nebudú zmazané a zostanete prihlásení aj po použití tlačidla Oheň. Stále budeme blokovať sledovacie zariadenia tretích strán, ktoré sa nachádzajú na webových stránkach zabezpečených funkciou ohňovzdornosti."; @@ -1216,8 +1606,8 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Obľúbené položky"; -/* No comment provided by engineer. */ -"settings.about.text" = "V službe DuckDuckGo zavádzame nový štandard dôvery online.\n\nSúkromný prehliadač DuckDuckGo poskytuje všetky základné informácie o ochrane súkromia, ktoré potrebujete, aby ste sa ochránili pri vyhľadávaní a prehliadaní webu, vrátane blokovania sledovacích zariadení, inteligentnejšieho šifrovania a súkromného vyhľadávania v prehliadači DuckDuckGo.\n\nPri používaní internetu by sme predsa nemali mať zlý pocit a dopriať si zaslúžené online súkromie by nemalo byť o nič ťažšie ako obyčajné zatiahnutie rolety."; +/* about page */ +"settings.about.text" = "DuckDuckGo je nezávislá spoločnosť na ochranu súkromia na internete, ktorá bola založená v roku 2008 pre všetkých, ktorých už nebaví sledovanie na internete a chcú jednoduché riešenie. Sme dôkazom, že skutočnú online ochranu súkromia môžete získať bez kompromisov.\n\nPrehliadač DuckDuckGo je vybavený funkciami, ktoré od prehliadača očakávate, ako sú napríklad záložky, karty, heslá a ďalšie funkcie. Navyše obsahuje viac ako [tucet účinných ochranných opatrení na ochranu súkromia](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/) sa vo väčšine populárnych prehliadačov štandardne neponúka. Tento jedinečne komplexný súbor ochrany súkromia pomáha chrániť vaše online aktivity od vyhľadávania po prehliadanie, posielanie e-mailov a ďalšie.\n\nNaša ochrana súkromia funguje bez toho, aby ste museli poznať technické detaily alebo riešiť zložité nastavenia. Stačí prepnúť prehliadač na DuckDuckGo vo všetkých zariadeniach a získate predvolené súkromie.\n\nAk však chcete nahliadnuť pod pokrievku, viac informácií o tom, ako funguje ochrana súkromia DuckDuckGo, nájdete na našich [stránkach pomoci](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/)."; /* Report a Broken Site screen confirmation button */ "siteFeedback.buttonText" = "Odoslať správu"; @@ -1237,6 +1627,9 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Ktorá webová stránka je nefunkčná?"; +/* No comment provided by engineer. */ +"sync.remove-device.message" = "\"%@\" už nebude mať prístup k synchronizovaným údajom."; + /* Accessibility label on remove button */ "tab.close.home" = "Zatvorte kartu domovskej stránky"; @@ -1309,6 +1702,57 @@ /* Voice-search footer note with on-device privacy warning */ "voiceSearch.footer.note" = "Zvuk sa spracováva v zariadení. Neukladá sa nikde a s nikým, vrátane DuckDuckGo, sa nezdieľa."; +/* Title for the button to enable push notifications in system settings */ +"waitlist.allow-notifications" = "Povoliť oznámenia"; + +/* Body text for the waitlist notification */ +"waitlist.available.notification.body" = "Otvorte svoju pozvánku"; + +/* Title for the copy action */ +"waitlist.copy" = "Kopírovať"; + +/* Label text for the invite code */ +"waitlist.invite-code" = "Pozývací kód"; + +/* Step title on the invite screen */ +"waitlist.invite-screen.step.title" = "Krok %d"; + +/* Title for the invite code screen */ +"waitlist.invite-screen.youre-invited" = "Ste pozvaný/-á!"; + +/* Title for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.join" = "Pridajte sa do súkromného zoznamu čakateľov"; + +/* Temporary status text for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.joining" = "Pridáva sa na zoznam čakateľov..."; + +/* Title for the Share Link button */ +"waitlist.join-waitlist-screen.share-link" = "Zdieľať odkaz"; + +/* Notification text for the waitlist */ +"waitlist.joined.no-notification.get-notification" = "UPOZORNITE MA"; + +/* Title for the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.get-notification-confirmation-title" = "Chcete dostať upozornenie, keď budete na rade?"; + +/* Cancel button in the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.no-thanks" = "Nie, ďakujem"; + +/* Text used for the Notifications Disabled state */ +"waitlist.notification.disabled" = "Môžeme vám poslať upozornenie, keď na vás príde rad, ale pre DuckDuckGo sú v súčasnosti upozornenia vypnuté."; + +/* Privacy disclaimer for the Waitlist feature */ +"waitlist.privacy-disclaimer" = "Pri pridávaní sa na zoznam čakateľov nemusíte zadávať žiadne osobné údaje. Svoje miesto si zabezpečíte časovou pečiatkou, ktorá existuje výlučne vo vašom zariadení, aby sme vás mohli upozorniť, keď budete na rade."; + +/* Title for the queue screen */ +"waitlist.queue-screen.on-the-list" = "Ste na zozname čakateľov!"; + +/* Title for the settings subtitle */ +"waitlist.settings.download-available" = "K dispozícii na stiahnutie"; + +/* Title for the share sheet entry */ +"waitlist.share-sheet.title" = "Ste pozvaný/-á!"; + /* Confirmation message */ "web.url.remove.favorite.done" = "Obľúbená položka bola odstránená"; @@ -1333,3 +1777,51 @@ /* Alert title explaining the message is shown by a website */ "webJSAlert.website-message.format" = "Správa od %@:"; +/* No comment provided by engineer. */ +"Welcome to the Duck Side!" = "Vitajte na Duck Side!"; + +/* Title for the Windows waitlist notification */ +"windows-waitlist.available.notification.title" = "Vyskúšajte DuckDuckGo pre Windows!"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-1.description" = "Na stiahnutie navštívte túto URL adresu vo svojom zariadení so systémom Windows:"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-2.description" = "Otvorte inštalátor DuckDuckGo v časti Stiahnuté súbory, vyberte možnosť Inštalovať a zadajte kód pozvania."; + +/* Subtitle for the Windows Waitlist Invite screen */ +"windows-waitlist.invite-screen.subtitle" = "Ste pripravení používať DuckDuckGo v systéme Windows?"; + +/* Title for the Windows waitlist button redirecting to Mac waitlist */ +"windows-waitlist.join-waitlist-screen.mac-waitlist" = "Hľadáte verziu pre Mac?"; + +/* Title for the Join Windows Waitlist screen */ +"windows-waitlist.join-waitlist-screen.try-duckduckgo-for-windows" = "Získajte skorý prístup a vyskúšajte DuckDuckGo pre Windows!"; + +/* Message for the alert to confirm enabling notifications */ +"windows-waitlist.joined.no-notification.get-notification-confirmation-message" = "Pošleme vám upozornenie, keď bude váš prehliadač DuckDuckGo pre Windows pripravený na stiahnutie. "; + +/* Label text for the Joined Waitlist state with notifications declined */ +"windows-waitlist.joined.notifications-declined" = "Tu nájdete svoju pozvánku na vyskúšanie DuckDuckGo pre Windows. Čoskoro sa sem vráťte. Môžeme vám tiež poslať upozornenie, keď budete na rade."; + +/* Label text for the Joined Waitlist state with notifications enabled */ +"windows-waitlist.joined.notifications-enabled" = "Pošleme vám upozornenie, keď bude váš prehliadač DuckDuckGo pre Windows pripravený na stiahnutie."; + +/* Title for the settings subtitle */ +"windows-waitlist.settings.browse-privately" = "Prehliadajte súkromne s našou aplikáciou pre Windows"; + +/* Message used when sharing to iMessage. Parameter is an eight digit invite code. */ +"windows-waitlist.share-sheet.invite-code-message" = "Boli ste pozvaný/-á!\n\nSte pripravený/-á používať DuckDuckGo v systéme Windows?1. krok\nPre stiahnutie navštívte túto URL adresu v svojom zariadení so systémom Windows:\nhttps://duckduckgo.com/windows\n\n2. krok\nOtvorte inštalátor DuckDuckGo v časti Stiahnuté súbory, vyberte možnosť Inštalovať a zadajte kód pozvania.\n\nPozývací kód: %@"; + +/* Message used when sharing to iMessage */ +"windows-waitlist.share-sheet.message" = "Ste pripravený/-á začať v systéme Windows prehliadať s ochranou súkromia?\n\nPre stiahnutie navštívte v počítači so systémom Windows túto URL adresu:\nhttps://duckduckgo.com/windows"; + +/* Summary text for the Windows browser waitlist */ +"windows-waitlist.summary" = "DuckDuckGo pre Windows má všetko, čo potrebujete na prehľadávanie s väčšou mierou súkromia — súkromné vyhľadávanie, blokovanie sledovania, vynútené šifrovanie, blokovanie automaticky zobrazených okien so súbormi cookie a ďalšie špičkové prvky ochrany."; + +/* Title for the Windows Waitlist feature */ +"windows-waitlist.title" = "Aplikácia DuckDuckGo pre Windows"; + +/* Title for the Windows browser download link page */ +"windows-waitlist.waitlist-download-screen.try-duckduckgo-for-windows" = "Získajte DuckDuckGo pre Windows!"; + diff --git a/DuckDuckGo/sk.lproj/Settings.strings b/DuckDuckGo/sk.lproj/Settings.strings index 0ed593c226..6864fb3a4f 100644 --- a/DuckDuckGo/sk.lproj/Settings.strings +++ b/DuckDuckGo/sk.lproj/Settings.strings @@ -37,6 +37,9 @@ /* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ "9Ko-0g-T3h.text" = "Veľkosť textu"; +/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ +"9kt-6R-XiZ.text" = "Názov"; + /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Spustenie aplikácie"; @@ -73,6 +76,12 @@ /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Globálna kontrola súkromia (GPC)"; +/* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ +"CxT-QK-iVn.title" = "Pridať"; + +/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ +"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; + /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Ukončiť 15 minút neaktívnu aplikáciu"; @@ -151,6 +160,9 @@ /* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ "HLr-R8-xxF.text" = "Náhľady po dlhodobom stlačení"; +/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ +"hoT-Nu-KXP.text" = "Prehliadajte súkromne s našou aplikáciou pre Windows"; + /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Ochrana súkromia je zapnutá pre všetky webové stránky"; @@ -163,6 +175,12 @@ /* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ "m23-t6-9cb.text" = "Zdieľať spätnú väzbu"; +/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ +"mLn-1x-Fl5.title" = "Poloha riadku s adresou"; + +/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ +"mqV-pf-NZ1.text" = "Poloha riadku s adresou"; + /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Nech DuckDuckGo spravuje kontextové okná týkajúce sa súhlasu so súbormi cookie"; @@ -175,15 +193,27 @@ /* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ "oM7-1o-9oY.text" = "O službe DuckDuckGo"; +/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ +"opn-JO-idF.text" = "Hore"; + /* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ "OxE-MQ-uJk.headerTitle" = "Viac od DuckDuckGo"; +/* Class = "UILabel"; text = "Sync"; ObjectID = "oXN-ez-gct"; */ +"oXN-ez-gct.text" = "Synchronizácia"; + /* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ "P0F-ts-ekd.text" = "Prezerajte si súkromne stránky s našou aplikáciou pre Mac "; +/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ +"qah-gb-udB.text" = "Network Protection"; + /* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ "qeN-SV-zy7.text" = "Označenie"; +/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ +"RQ8-H1-Ez1.text" = "Aplikácia DuckDuckGo pre Windows"; + /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Veľkosť textu"; @@ -232,8 +262,8 @@ /* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ "yvj-LL-MiR.text" = "Pridať aplikáciu do Docku"; -/* Class = "UILabel"; text = "DuckDuckGo Desktop App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "Aplikácia DuckDuckGo pre počítač"; +/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ +"Yz9-17-qnn.text" = "Aplikácia DuckDuckGo pre Mac"; /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Nová karta"; diff --git a/DuckDuckGo/sl.lproj/Feedback.strings b/DuckDuckGo/sl.lproj/Feedback.strings index 56d6659960..a5fbc20190 100644 --- a/DuckDuckGo/sl.lproj/Feedback.strings +++ b/DuckDuckGo/sl.lproj/Feedback.strings @@ -28,9 +28,6 @@ /* Class = "UIBarButtonItem"; title = "Back"; ObjectID = "JF8-Ow-zXL"; */ "JF8-Ow-zXL.title" = "Nazaj"; -/* Class = "UIBarButtonItem"; title = "Submit"; ObjectID = "jr6-Al-4fr"; */ -"jr6-Al-4fr.title" = "Potrdi"; - /* Class = "UIButton"; normalTitle = "Submit"; ObjectID = "lsB-Kq-U8p"; */ "lsB-Kq-U8p.normalTitle" = "Potrdi"; @@ -58,9 +55,6 @@ /* Class = "UINavigationItem"; title = "Report Broken Site"; ObjectID = "WjT-HM-yim"; */ "WjT-HM-yim.title" = "Prijavi nedelujočo stran"; -/* Class = "UILabel"; text = "Broken Site Reason"; ObjectID = "Wx3-Vb-ItS"; */ -"Wx3-Vb-ItS.text" = "Vzrok za nedelujočo stran"; - /* Class = "UINavigationItem"; title = "Share Feedback"; ObjectID = "Xb0-Zu-opM"; */ "Xb0-Zu-opM.title" = "Deli povratne informacije"; diff --git a/DuckDuckGo/sl.lproj/Localizable.strings b/DuckDuckGo/sl.lproj/Localizable.strings index d0c2f9a7fa..47190ef484 100644 --- a/DuckDuckGo/sl.lproj/Localizable.strings +++ b/DuckDuckGo/sl.lproj/Localizable.strings @@ -1,6 +1,9 @@ /* No comment provided by engineer. */ "%@" = "%@"; +/* No comment provided by engineer. */ +"%@ [%@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)" = "%1$@ [%2$@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)"; + /* Buton label for Edit action */ "action.generic.edit" = "Uredi"; @@ -103,6 +106,12 @@ /* Share action */ "action.title.share" = "Deli"; +/* Settings label for bottom position for the address bar */ +"address.bar.bottom" = "Spodaj"; + +/* Settings label for top position for the address bar */ +"address.bar.top" = "Vrh"; + /* No comment provided by engineer. */ "addWidget.button" = "Dodaj gradnik"; @@ -142,6 +151,225 @@ /* Shown on authentication screen */ "app.authentication.unlock" = "Odkleni DuckDuckGo."; +/* First part of about page content (note the trailing space) */ +"appTP.about.content1" = "You’ve probably heard about companies like Google and Facebook tracking you behind the scenes on third-party websites. But did you know they also track your personal information through apps on your device?\n\nIn 2022, DuckDuckGo found that "; + +/* Second part of about page content (note the trailing space) */ +"appTP.about.content2" = "over 85% of free iOS apps tested contained hidden trackers from other companies."; + +/* Third part of about page content (note the leading space) */ +"appTP.about.content3" = " Of the 395 apps tested, 60% sent data to Google. This happens even while you’re not using your device.\n\nTrackers in apps may have access to a lot more information than their website tracker cousins, such as your location down to which floor of a building you're on, how often you play games while at work, and when and how long you sleep each day. Even if you haven’t given apps explicit permission to collect data, they can still take it without your knowledge.\n\nTracking networks like Facebook and Google use these little pieces of information to build a digital profile about you. With it, tracking networks can manipulate what you see online and allow advertisers to bid on access to you based on your data.\n\nTrackers in apps is a BIG problem for privacy. But DuckDuckGo has a solution that can help.\n\nWhen enabled in the DuckDuckGo Privacy Browser app, App Tracking Protection blocks many trackers in other apps, not just the trackers we find on websites when you browse. These dual layers of protection reduce what companies know about you overall, so you can use your apps with more peace of mind, knowing you’re more protected."; + +/* Navigation Title for AppTP about page */ +"appTP.about.navTitle" = "About App Trackers"; + +/* Title for AppTP about page */ +"appTP.about.title" = "What Are App Trackers?"; + +/* Title for 'Report an Issue' button in the activity view. */ +"appTP.activityView.reportIssue" = "Report Issue"; + +/* Text label for switch that turns blocking on or off for a tracker */ +"appTP.blockTrackerText" = "Block this Tracker"; + +/* Detail string describing what AppTP is */ +"appTP.cell.detail" = "Blokiraj sledilnike aplikacij v napravi"; + +/* String indicating AppTP is disabled when viewed from the settings screen */ +"appTP.cell.disabled" = "Onemogočeno"; + +/* String indicating AppTP is enabled when viewed from the settings screen */ +"appTP.cell.enabled" = "Omogočeno"; + +/* Info string informing the user what App Tracking Protection does. */ +"appTP.empty.disabled.info" = "Omogočite zaščito pred sledenjem aplikacij App Tracking Protection, da lahko blokiramo nadležne sledilnike v drugih aplikacijah."; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.heading" = "We’re blocking hidden trackers"; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.info" = "Come back soon to see a list of all the app trackers we’ve blocked."; + +/* First answer for AppTP FAQ page */ +"appTP.faq.answer1" = "App Tracking Protection blocks app trackers from other companies, like when Facebook tries to track you in a banking app. Companies may still track you in apps they own."; + +/* Second answer for AppTP FAQ page */ +"appTP.faq.answer2" = "Yes! App Tracking Protection works across all apps on your device to block the most common hidden trackers we find trying to collect your personal info."; + +/* Third answer for AppTP FAQ page */ +"appTP.faq.answer3" = "We currently only block the most common trackers that we find on iOS. This helps us to comprehensively test App Tracking Protection and lower frequency of app breakage, while blocking up to 70% of all tracking requests."; + +/* Fourth answer for AppTP FAQ page */ +"appTP.faq.answer4" = "You’ll be asked to set up a virtual private network (VPN) connection, but you don't need to install a VPN app for App Tracking Protection to work.\n\nThis permission, which works only on your device, allows App Tracking Protection to monitor network traffic so that it can block known trackers."; + +/* Fifth answer for AppTP FAQ page */ +"appTP.faq.answer5" = "You can use App Tracking Protection at the same time as using an IKEv2 protocol VPN app on an iOS device. You won’t be able to use App Tracking Protection on an iOS device if you’re using a VPN app that uses a different type of protocol, like WireGuard or OpenVPN type VPNs."; + +/* Sixth answer for AppTP FAQ page */ +"appTP.faq.answer6" = "A VPN sends your data from the device to its own server, where it secures and anonymizes your data from prying eyes. However, this allows the VPN company to see your network traffic.\n\nApp Tracking Protection is different. Instead of sending your data to a VPN server, App Tracking Protection works only on your device, sitting between your apps and the servers they talk to.\n\nWhenever App Tracking Protection recognizes a known tracker, it blocks the tracker from sending personal information (such as your IP address, activity, and device details) off your device. All other traffic reaches its destination, so your apps work normally."; + +/* Seventh answer for AppTP FAQ page */ +"appTP.faq.answer7" = "App Tracking Protection works only on your device and doesn’t send your data off your device to DuckDuckGo. We don’t collect or store any data from your apps."; + +/* First question for AppTP FAQ page */ +"appTP.faq.question1" = "How does App Tracking Protection work?"; + +/* Second question for AppTP FAQ page */ +"appTP.faq.question2" = "Does App Tracking Protection block trackers in all apps on my device?"; + +/* Third question for AppTP FAQ page */ +"appTP.faq.question3" = "Does App Tracking Protection block all app trackers?"; + +/* Fourth question for AppTP FAQ page */ +"appTP.faq.question4" = "Why does App Tracking Protection use a VPN connection?"; + +/* Fifth question for AppTP FAQ page */ +"appTP.faq.question5" = "Will App Tracking Protection work if I also use a VPN app?"; + +/* Sixth question for AppTP FAQ page */ +"appTP.faq.question6" = "How is App Tracking Protection different from a VPN?"; + +/* Seventh question for AppTP FAQ page */ +"appTP.faq.question7" = "Is my data private?"; + +/* Title for AppTP FAQ page */ +"appTP.faq.title" = "App Tracking Protection FAQ"; + +/* Do not translate. StringsDict entry -- Count part of string 'App Tracking Protection blocked x tracking attempts today' */ +"appTP.home.blockedCount" = "appTP.home.blockedCount"; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the trailing space) */ +"appTP.home.blockedPrefix" = "Zaščita pred sledenjem aplikacij App Tracking Protection je blokirala "; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the leading space) */ +"appTP.home.blockedSuffix" = " v vaših aplikacijah danes."; + +/* Prefix of string 'App Tracking Protection disabled. Tap to re-enable.' (note the trailing space) */ +"appTP.home.disabledPrefix" = "App Tracking Protection disabled. "; + +/* Suffix of string 'App Tracking Protection disabled. Tap to re-enable.' */ +"appTP.home.disabledSuffix" = "Tap to continue blocking tracking attempts across your apps."; + +/* Text indicating the tracking event occured 'just now'. Example: Last attempt 'just now' */ +"appTP.justNow" = "Ravno zdaj"; + +/* View to manage trackers for AppTP. Allows the user to turn trackers on or off. */ +"appTP.manageTrackers" = "Upravljanje sledilnikov"; + +/* Button title for AppTP onboarding */ +"appTP.onboarding.continueButton" = "Continue"; + +/* Button title for AppTP onboarding to enable AppTP */ +"appTP.onboarding.enableeButton" = "Enable App Tracking Protection"; + +/* Button title for AppTP onboarding to learn more about AppTP */ +"appTP.onboarding.learnMoreButton" = "Learn More"; + +/* First part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info1" = "Over 85% of free iOS apps"; + +/* Second part of info on the first AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page1Info2" = " we’ve tested allow other companies to track your personal information, even when you’re sleeping."; + +/* Third part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info3" = "See who we catch trying to track you in your apps and take back control."; + +/* First part of info on the second AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page2Info1" = "App Tracking Protection "; + +/* Second part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info2" = "detects and blocks app trackers from other companies,"; + +/* Third part of info on the second AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page2Info3" = " like when Google attempts to track you in a health app."; + +/* Fourth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info4" = "It’s free,"; + +/* Fifth part of info on the second AppTP onboarding page (note the leading and trailing space) */ +"appTP.onboarding.page2Info5" = " and you can enjoy your apps as you normally would. Working in the background, it helps "; + +/* Sixth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info6" = "protect you night and day."; + +/* First part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info1" = "App Tracking Protection is not a VPN."; + +/* Second part of info on the third AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page3Info2" = " However, your device will recognize it as one. This is because it uses a local VPN connection to work."; + +/* Third part of info on the third AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page3Info3" = "App Tracking Protection is different. "; + +/* Fourth part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info4" = "It never routes app data through an external server."; + +/* Title for first AppTP onboarding page */ +"appTP.onboarding.title1" = "One easy step for better app privacy!"; + +/* Title for second AppTP onboarding page */ +"appTP.onboarding.title2" = "How does it work?"; + +/* Title for third AppTP onboarding page */ +"appTP.onboarding.title3" = "Who sees your data?"; + +/* Breakage report app name label */ +"appTP.report.appLabel" = "Which app is having issues?"; + +/* Breakage report app name placeholder */ +"appTP.report.appPlaceholder" = "App name"; + +/* Breakage report category label */ +"appTP.report.categoryLabel" = "Kaj se dogaja?"; + +/* Breakage report comment label */ +"appTP.report.commentLabel" = "Comments"; + +/* Breakage report comment placeholder */ +"appTP.report.commentPlaceholder" = "Add additional details"; + +/* Breakage report footer explaining what is collected in the breakage report */ +"appTP.report.footer" = "In addition to the details entered into this form, your app issue report will contain:\n• A list of trackers blocked in the last 10 min\n• Whether App Tracking Protection is enabled\n• Aggregate DuckDuckGo app diagnostics"; + +/* Breakage report submit button */ +"appTP.report.submit" = "Potrdi"; + +/* Breakage report form title */ +"appTP.report.title" = "Report Issue"; + +/* Breakage report succcess message */ +"appTP.report.toast" = "Hvala! Povratne informacije so poslane."; + +/* Cancel button for 'Report an Issue' alert. */ +"appTP.reportAlert.cancel" = "Ne zdaj"; + +/* Confirm button for 'Report an Issue' alert. */ +"appTP.reportAlert.confirm" = "Izdaja poročila"; + +/* Message for 'Report an Issue' alert. */ +"appTP.reportAlert.message" = "Let us know if you disabled App Tracking Protection for this specific tracker because it caused app issues. Your feedback helps us improve!"; + +/* Title for 'Report an Issue' alert. */ +"appTP.reportAlert.title" = "Report Issue?"; + +/* Toast notification diplayed after restoring the blocklist to default settings */ +"appTP.restoreDefaultsToast" = "Default settings restored"; + +/* Button to restore the blocklist to its default state. */ +"appTP.restoreDefualts" = "Obnovi privzete nastavitve"; + +/* Title for the App Tracking Protection feature */ +"appTP.title" = "Zaščita pred sledenjem aplikacij"; + +/* Text indicating when the tracker was last allowed. Example: Last attempt allowed (timeString) */ +"appTP.trackerAllowedTimestamp" = "Zadnji dovoljen poskus: %@"; + +/* Text indicating when the tracker was last blocked. Example: Last attempt blocked (timeString) */ +"appTP.trackerBlockedTimestamp" = "Zadnji blokiran poskus: %@"; + +/* Do not translate. StringsDict entry -- Subtitle for tracking attempts in App Tracking Protection Activity View. Example: (count) tracking attempts */ +"appTP.trackingattempts" = "appTP.trackingattempts"; + /* Authentication Alert Sign In Button */ "auth.alert.login.button" = "Prijavite se"; @@ -301,6 +529,18 @@ /* Toast message when a login item without a title is deleted */ "autofill.logins.list.login-deleted-message-no-title" = "Prijava je izbrisana"; +/* Title for a button that allows a user to reset their list of never saved sites */ +"autofill.logins.list.never.saved" = "Ponastavi izključena spletna mesta"; + +/* Cancel button for resetting list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.cancel" = "Prekliči"; + +/* Confirm button to reset list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.confirm" = "Ponastavi izključena spletna mesta"; + +/* Alert title */ +"autofill.logins.list.never.saved.reset.action.title" = "Če ponastavite izključena spletna mesta, boste ob naslednji prijavi na katero koli od teh spletnih mest pozvani, da shranite svojo prijavo."; + /* Placeholder for search field on autofill login listing */ "autofill.logins.list.search-placeholder" = "Iskanje podatkov za prijavo"; @@ -335,7 +575,7 @@ "autofill.logins.prompt.password.button.title" = "Geslo za %@"; /* Title for autofill login prompt */ -"autofill.logins.prompt.title" = "Želite uporabiti shranjene podatke za prijavo?"; +"autofill.logins.prompt.title" = "Želite uporabiti shranjeno geslo?"; /* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ "autofill.logins.search.no-results.subtitle" = "za »%@«"; @@ -385,27 +625,24 @@ /* Title for the alert dialog telling the user an updated username is no longer a private email address */ "autofill.removed.duck.address.title" = "Uporabniško ime za zasebni naslov Duck Address je bilo odstranjeno"; +/* CTA displayed on modal asking if the user never wants to be prompted to save a login for this website agin */ +"autofill.save-login.never-prompt.CTA" = "Nikoli ne vprašaj za to spletno mesto"; + /* Message displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.message" = "Prijave so varno shranjene v vaši napravi v meniju Prijave."; +"autofill.save-login.new-user.message" = "Gesla so varno shranjena v napravi v meniju Prijave."; /* Title displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.title" = "Ali želite, da DuckDuckGo shrani vašo prijavo?"; +"autofill.save-login.new-user.title" = "Ali želite, da DuckDuckGo shrani vaše geslo?"; /* Cancel CTA displayed on modal asking for the user to save the login */ "autofill.save-login.not-now.CTA" = "Ne shrani"; -/* Confirm CTA displayed on modal asking for the user to save the login */ -"autofill.save-login.save.CTA" = "Shrani prijavo"; - /* Title displayed on modal asking for the user to save the login */ "autofill.save-login.title" = "Želite shraniti prijavo?"; /* Confirm CTA displayed on modal asking for the user to save the password */ "autofill.save-password.save.CTA" = "Shrani geslo"; -/* Title displayed on modal asking for the user to save the password */ -"autofill.save-password.title" = "Želite shraniti geslo?"; - /* Accessibility title for a Show Password button displaying actial password instead of ***** */ "autofill.show-password" = "Prikaži geslo"; @@ -413,7 +650,7 @@ "autofill.signin.to.manage" = "%@ za upravljanje naslovov Duck Address v tej napravi."; /* Message displayed on modal asking for the user to update the password */ -"autofill.update-password.message" = "DuckDuckGo bo posodobil to shranjeno prijavo v vaši napravi."; +"autofill.update-password.message" = "DuckDuckGo bo posodobil to shranjeno geslo v vaši napravi."; /* Confirm CTA displayed on modal asking for the user to update the password */ "autofill.update-password.save.CTA" = "Posodobitev gesla"; @@ -442,6 +679,15 @@ /* Placeholder in the add bookmark form */ "bookmark.address.placeholder" = "www.example.com"; +/* Delete bookmark alert message */ +"bookmark.delete.alert.message" = "S tem boste izbrisali svoj zaznamek za »%@«"; + +/* Delete bookmark alert title */ +"bookmark.delete.alert.title" = "Delete?"; + +/* The message shown after a bookmark has been deleted */ +"bookmark.deleted.toast" = "Zaznamek je bil izbrisan"; + /* Delete bookmark folder alert delete button */ "bookmark.deleteFolderAlert.deleteButton" = "Izbriši"; @@ -538,9 +784,21 @@ /* Broken Site Category */ "brokensite.category.videos" = "Video se ni predvajal"; +/* Broken Site Category Placeholder */ +"brokensite.categoryPlaceholder" = "Svoje vprašanje izberite s seznama ..."; + +/* Broken Site Category Section Title */ +"brokensite.categoryTitle" = "IZBERITE KATEGORIJO"; + +/* Broken Site Comment Placeholder */ +"brokensite.commentPlaceholder" = "Če z nami delite več podrobnosti, nam to lahko pomaga pri reševanju te težave"; + /* Broken Site Section Title */ "brokensite.sectionTitle" = "OPIŠITE, KAJ SE JE ZGODILO"; +/* No comment provided by engineer. */ +"bucket: %@" = "zbiralnik: %@"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Pretekli mesec"; @@ -772,6 +1030,39 @@ /* Empty search placeholder on bookmarks search */ "empty.search" = "Ni zadetkov"; +/* No comment provided by engineer. */ +"Error" = "Napaka"; + +/* Button title to Sign In */ +"error.email-protection-sign-in.action" = "Prijavite se"; + +/* Alert message */ +"error.email-protection-sign-in.body" = "Znova se prijavite, da ponovno omogočite funkcije Email Protection v tem brskalniku."; + +/* Alert title */ +"error.email-protection-sign-in.title" = "Napaka funkcije Email Protection"; + +/* Button title to open device settings */ +"error.insufficient-disk-space.action" = "Odpri nastavitve"; + +/* Alert message */ +"error.insufficient-disk-space.body" = "Videti je, da je vaši napravi zmanjkalo prostora za shranjevanje. Za nadaljevanje sprostite prostor."; + +/* Alert title */ +"error.insufficient-disk-space.title" = "Premalo prostora za shranjevanje"; + +/* Button title that is shutting down the app */ +"error.preemptive-crash.action" = "Zapri aplikacijo"; + +/* Alert message */ +"error.preemptive-crash.body" = "Videti je, da je prišlo do težave z aplikacijo in jo je zato treba zapreti. Če želite nadaljevati, jo odprite znova."; + +/* Alert title */ +"error.preemptive-crash.title" = "Odkrita težava z aplikacijo"; + +/* Generic error message on a dialog for when the cause is not known. */ +"error.unknown.try.again" = "An unknown error has occurred"; + /* No comment provided by engineer. */ "favorite" = "Priljubljeni"; @@ -991,6 +1282,18 @@ /* Home tab title */ "homeTab.title" = "Domov"; +/* OK title for invite screen alert dismissal button */ +"invite.alert.ok.button" = "OK"; + +/* Continue button on an invite dialog */ +"invite.dialog.continue.button" = "Continue"; + +/* Get Started button on an invite dialog */ +"invite.dialog.get.started.button" = "Get Started"; + +/* Message to show after user enters an unrecognized invite code */ +"invite.dialog.unrecognized.code.message" = "We didn’t recognize this Invite Code."; + /* No comment provided by engineer. */ "keyCommandAddBookmark" = "Dodaj zaznamek"; @@ -1057,15 +1360,15 @@ /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "DuckDuckGo za računalnike Mac zagotavlja hitrost, ki jo potrebujete, in funkcije brskanja, ki jih pričakujete, ter je opremljen z našimi ključnimi funkcijami zasebnosti, ki so najboljše v tem razredu."; -/* Body text for the macOS waitlist notification */ -"mac-waitlist.available.notification.body" = "Odprite svoje povabilo"; - /* Title for the macOS waitlist notification */ "mac-waitlist.available.notification.title" = "DuckDuckGo za računalnike Mac je na voljo!"; /* Title for the copy action */ "mac-waitlist.copy" = "Kopiraj"; +/* Description text above the Share Link button */ +"mac-waitlist.join-waitlist-screen.on-your-computer-go-to" = "V računalniku s sistemom Windows pojdite na:"; + /* Description text above the Share Link button */ "mac-waitlist.join-waitlist-screen.on-your-mac-go-to" = "V računalniku Mac odprite:"; @@ -1078,6 +1381,9 @@ /* Disclaimer for the Join Waitlist screen */ "mac-waitlist.join-waitlist-screen.windows" = "Kmalu bo na voljo za Windows!"; +/* Title for the macOS waitlist button redirecting to Windows waitlist */ +"mac-waitlist.join-waitlist-screen.windows-waitlist" = "Iščete različico za računalnike Windows?"; + /* Title for the settings subtitle */ "mac-waitlist.settings.browse-privately" = "Brskajte zasebno z našo aplikacijo za računalnike Mac"; @@ -1088,7 +1394,7 @@ "mac-waitlist.share-sheet.title" = "DuckDuckGo za računalnike Mac"; /* Title for the Mac Waitlist feature */ -"mac-waitlist.title" = "Namizna aplikacija DuckDuckGo"; +"mac-waitlist.title" = "Aplikacija DuckDuckGo za računalnike Mac"; /* No comment provided by engineer. */ "menu.button.hint" = "Brskanje po meniju"; @@ -1105,9 +1411,81 @@ /* Edit button */ "navigation.title.edit" = "Uredi"; +/* String indicating NetP is connected when viewed from the settings screen */ +"netP.cell.connected" = "Connected"; + +/* String indicating NetP is disconnected when viewed from the settings screen */ +"netP.cell.disconnected" = "Not connected"; + +/* Title for the Network Protection feature */ +"netP.title" = "Network Protection"; + +/* Message for the network protection invite dialog */ +"network.protection.invite.dialog.message" = "Enter your invite code to get started."; + +/* Title for the network protection invite screen */ +"network.protection.invite.dialog.title" = "You're invited to try Network Protection"; + +/* Prompt for the network protection invite code text field */ +"network.protection.invite.field.prompt" = "Invite Code"; + +/* Message explaining that netP is invite only */ +"network.protection.invite.only.message" = "DuckDuckGo Network Protection is currently invite-only."; + +/* Message for the network protection invite success view */ +"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; + +/* Title for the network protection invite success view */ +"network.protection.invite.success.title" = "Success! You’re in."; + +/* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ +"network.protection.status.connected.format" = "Connected - %@"; + +/* The label for the NetP VPN when connecting */ +"network.protection.status.connecting" = "Connecting..."; + +/* The label for the NetP VPN when disconnected */ +"network.protection.status.disconnected" = "Not connected"; + +/* The label for the NetP VPN when disconnecting */ +"network.protection.status.disconnecting" = "Disconnecting..."; + +/* Message label text for the netP status view */ +"network.protection.status.header.message" = "DuckDuckGo's VPN secures all of your device's Internet traffic anytime, anywhere."; + +/* Header title label text for the status view when netP is disconnected */ +"network.protection.status.header.title.off" = "Network Protection is Off"; + +/* Header title label text for the status view when netP is connected */ +"network.protection.status.header.title.on" = "Network Protection is On"; + +/* The status view 'Share Feedback' button which is shown inline on the status view after the \(netPInviteOnlyMessage) text */ +"network.protection.status.menu.share.feedback" = "Share Feedback"; + +/* Connection details label shown in NetworkProtection's status view. */ +"network.protection.status.view.connection.details" = "Connection Details"; + +/* Generic connection failed error message shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.message" = "Please try again later."; + +/* Generic connection failed error title shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; + +/* IP Address label shown in NetworkProtection's status view. */ +"network.protection.status.view.ip.address" = "IP Address"; + +/* Location label shown in NetworkProtection's status view. */ +"network.protection.status.view.location" = "Location"; + +/* Title label text for the status view when netP is disconnected */ +"network.protection.status.view.title" = "Network Protection"; + /* Do not translate - stringsdict entry */ "number.of.tabs" = "number.of.tabs"; +/* No comment provided by engineer. */ +"Ok" = "V REDU"; + /* Text displayed on notification appearing in the address bar when the browser dismissed the cookie popup automatically rejecting it */ "omnibar.notification.cookies-managed" = "Upravljani piškotki"; @@ -1147,6 +1525,18 @@ /* ’Link’ is link on a website */ "open.externally.failed" = "Aplikacije, ki je potrebna za odpiranje te povezave, ni mogoče najti"; +/* No comment provided by engineer. */ +"Please enter which app on your device is broken." = "Vnesite, katera aplikacija v vaši napravi ne deluje."; + +/* Activate button */ +"pm.activate" = "Reactivate"; + +/* Cancel button */ +"pm.cancel" = "Cancel"; + +/* Deactivate button */ +"pm.deactivate" = "Deactivate"; + /* No comment provided by engineer. */ "preserveLogins.domain.list.footer" = "Spletne strani se zanašajo na piškotke, da boš vedno prijavljen. Ko spletno mesto požarno zaščitiš, piškotki ne bodo izbrisani in ostal boš prijavljen tudi po uporabi gumba Fire. Še vedno blokiramo zunanje sledilce, ki jih najdemo na požarno izoliranih spletnih mestih."; @@ -1216,8 +1606,8 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Priljubljeni"; -/* No comment provided by engineer. */ -"settings.about.text" = "Pri DuckDuckGo vzpostavljamo nov standard zaupanja na spletu.\n\nZasebni brskalnik DuckDuckGo zagotavlja vse bistvene sestavine zasebnosti, ki jih potrebujete za svojo zaščito, kadar iščete in brskate po spletu, vključno z blokiranjem sledilnikov, pametnejšim šifriranjem in zasebnim iskanjem DuckDuckGo.\n\nDoseganje zasebnosti na spletu, ki si jo zaslužimo, bi moralo biti tako preprosto kot zagrinjanje zaves."; +/* about page */ +"settings.about.text" = "DuckDuckGo je neodvisno podjetje za zasebnost v internetu, ustanovljeno leta 2008, in je namenjeno vsem, ki so naveličani sledenja v spletu in si želijo preproste rešitve. Dokazujemo, da je v spletu mogoče zagotoviti pravo zaščito zasebnosti brez sklepanja kompromisov.\n\nBrskalnik DuckDuckGo ima funkcije, ki jih pričakujete od priljubljenega brskalnika, kot so zaznamki, zavihki, gesla in drugo, ter več kot [ducat učinkovitih zaščit zasebnosti](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/) ki v večini priljubljenih brskalnikov niso na voljo privzeto. Ta edinstveni celovit sklop zaščite zasebnosti pomaga zaščititi vaše spletne dejavnosti, od iskanja do brskanja, pošiljanja e-pošte in še več.\n\nNaša zaščita zasebnosti deluje, ne da bi se vam bilo treba spoznati na tehnične podrobnosti ali se ukvarjati z zapletenimi nastavitvami. Vse, kar morate storiti, je, da brskalnik preklopite na DuckDuckGo v vseh napravah in zasebnost bo privzeta.\n\nČe pa želite pogledati v mehanizem delovanja, lahko več informacij o delovanju zaščite zasebnosti DuckDuckGo najdete na naših [straneh s pomočjo](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/)."; /* Report a Broken Site screen confirmation button */ "siteFeedback.buttonText" = "Pošlji poročilo"; @@ -1237,6 +1627,9 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Katera spletna stran je poškodovana?"; +/* No comment provided by engineer. */ +"sync.remove-device.message" = "Aplikacija »%@« ne bo več mogla dostopati do sinhroniziranih podatkov."; + /* Accessibility label on remove button */ "tab.close.home" = "Zapri zavihek Domov"; @@ -1309,6 +1702,57 @@ /* Voice-search footer note with on-device privacy warning */ "voiceSearch.footer.note" = "Zvok bo obdelan v napravi. Zvok ne bo shranjen in ne bo deljen z drugimi, niti z brskalnikom DuckDuckGo."; +/* Title for the button to enable push notifications in system settings */ +"waitlist.allow-notifications" = "Dovoli obvestila"; + +/* Body text for the waitlist notification */ +"waitlist.available.notification.body" = "Odprite svoje povabilo"; + +/* Title for the copy action */ +"waitlist.copy" = "Kopiraj"; + +/* Label text for the invite code */ +"waitlist.invite-code" = "Koda povabila"; + +/* Step title on the invite screen */ +"waitlist.invite-screen.step.title" = "%d. korak"; + +/* Title for the invite code screen */ +"waitlist.invite-screen.youre-invited" = "Povabljeni ste!"; + +/* Title for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.join" = "Pridruži se zasebnemu čakalnemu seznamu"; + +/* Temporary status text for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.joining" = "Pridružitev čakalni listi ..."; + +/* Title for the Share Link button */ +"waitlist.join-waitlist-screen.share-link" = "Daj povezavo v skupno rabo"; + +/* Notification text for the waitlist */ +"waitlist.joined.no-notification.get-notification" = "OBVESTI ME"; + +/* Title for the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.get-notification-confirmation-title" = "Želite prejeti obvestilo, ko boste na vrsti?"; + +/* Cancel button in the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.no-thanks" = "Ne, hvala"; + +/* Text used for the Notifications Disabled state */ +"waitlist.notification.disabled" = "Lahko vas obvestimo, ko boste na vrsti, toda obvestila za DuckDuckGo so trenutno onemogočena."; + +/* Privacy disclaimer for the Waitlist feature */ +"waitlist.privacy-disclaimer" = "Za pridružitev čakalnemu seznamu vam ne bo treba posredovati nobenih osebnih podatkov. Svoje mesto si boste zagotovili v skladu s časovnim žigom, ki obstaja samo v vaši napravi, tako da vas lahko obvestimo, ko boste na vrsti."; + +/* Title for the queue screen */ +"waitlist.queue-screen.on-the-list" = "Na seznamu ste!"; + +/* Title for the settings subtitle */ +"waitlist.settings.download-available" = "Na voljo za prenos"; + +/* Title for the share sheet entry */ +"waitlist.share-sheet.title" = "Povabljeni ste!"; + /* Confirmation message */ "web.url.remove.favorite.done" = "Priljubljeni odstranjeni"; @@ -1333,3 +1777,51 @@ /* Alert title explaining the message is shown by a website */ "webJSAlert.website-message.format" = "Sporočilo od %@:"; +/* No comment provided by engineer. */ +"Welcome to the Duck Side!" = "Dobrodošli na strani Duck!"; + +/* Title for the Windows waitlist notification */ +"windows-waitlist.available.notification.title" = "Preizkusite DuckDuckGo za Windowse!"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-1.description" = "Za prenos obiščite ta naslov URL v napravi Windows:"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-2.description" = "Odprite namestitveni program DuckDuckGo v mapi Prenosi, izberite Namesti in vnesite kodo povabila."; + +/* Subtitle for the Windows Waitlist Invite screen */ +"windows-waitlist.invite-screen.subtitle" = "Želite začeti uporabljati DuckDuckGo v sistemu Windows?"; + +/* Title for the Windows waitlist button redirecting to Mac waitlist */ +"windows-waitlist.join-waitlist-screen.mac-waitlist" = "Iščete različico za računalnike Mac?"; + +/* Title for the Join Windows Waitlist screen */ +"windows-waitlist.join-waitlist-screen.try-duckduckgo-for-windows" = "Zagotovite si zgodnji dostop in preizkusite DuckDuckGo za sistem Windows!"; + +/* Message for the alert to confirm enabling notifications */ +"windows-waitlist.joined.no-notification.get-notification-confirmation-message" = "Ko bo vaša izdaja DuckDuckGo za računalnike Windows pripravljena za prenos, vam bomo poslali obvestilo. "; + +/* Label text for the Joined Waitlist state with notifications declined */ +"windows-waitlist.joined.notifications-declined" = "Vaše povabilo za preizkus DuckDuckGo za sistem Windows bo prispelo sem. Preverite znova kmalu, lahko pa vam lahko pošljemo obvestilo, ko bo preizkus na voljo."; + +/* Label text for the Joined Waitlist state with notifications enabled */ +"windows-waitlist.joined.notifications-enabled" = "Ko bo vaša izdaja DuckDuckGo za računalnike Windows pripravljena za prenos, vam bomo poslali obvestilo."; + +/* Title for the settings subtitle */ +"windows-waitlist.settings.browse-privately" = "Brskajte zasebno z našo aplikacijo za sistem Windows"; + +/* Message used when sharing to iMessage. Parameter is an eight digit invite code. */ +"windows-waitlist.share-sheet.invite-code-message" = "Povabljeni ste!\n\nŽelite začeti uporabljati DuckDuckGo v sistemu Windows?\n\n1. korak\nZa prenos obiščite ta URL v napravi Windows:\nhttps://duckduckgo.com/windows\n\n2. korak\nOdprite namestitveni program DuckDuckGo Installer, ki je v mapi Prenosi, izberite Namesti in vnesite kodo povabila.\n\nKoda povabila: %@"; + +/* Message used when sharing to iMessage */ +"windows-waitlist.share-sheet.message" = "Ste pripravljeni začeti zasebno brskati v sistemu Windows?\n\nZa prenos v računalniku s sistemom Windows obiščite ta naslov URL:\nhttps://duckduckgo.com/windows"; + +/* Summary text for the Windows browser waitlist */ +"windows-waitlist.summary" = "DuckDuckGo za sistem Windows ima vse, kar potrebujete za zasebnejše brskanje – zasebno iskanje, blokiranje sledenja, prisilno šifriranje in blokiranje pojavnih oken s piškotki ter še več najboljših zaščit v svojem razredu."; + +/* Title for the Windows Waitlist feature */ +"windows-waitlist.title" = "Aplikacija DuckDuckGo za sistem Windows"; + +/* Title for the Windows browser download link page */ +"windows-waitlist.waitlist-download-screen.try-duckduckgo-for-windows" = "Pridobite DuckDuckGo za Windows!"; + diff --git a/DuckDuckGo/sl.lproj/Settings.strings b/DuckDuckGo/sl.lproj/Settings.strings index fcae0075d2..5c032acda2 100644 --- a/DuckDuckGo/sl.lproj/Settings.strings +++ b/DuckDuckGo/sl.lproj/Settings.strings @@ -37,6 +37,9 @@ /* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ "9Ko-0g-T3h.text" = "Velikost besedila"; +/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ +"9kt-6R-XiZ.text" = "Naslov"; + /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Zagon aplikacije"; @@ -73,6 +76,12 @@ /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Globalni nadzor zasebnosti (GPC)"; +/* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ +"CxT-QK-iVn.title" = "Dodaj"; + +/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ +"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; + /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Izhod iz aplikacije, neaktivnost 15 minut"; @@ -151,6 +160,9 @@ /* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ "HLr-R8-xxF.text" = "Pregledi z dolgim pritiskom"; +/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ +"hoT-Nu-KXP.text" = "Brskajte zasebno z našo aplikacijo za sistem Windows"; + /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Zaščita zasebnosti omogočena za vsa spletna mesta"; @@ -163,6 +175,12 @@ /* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ "m23-t6-9cb.text" = "Deli povratne informacije"; +/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ +"mLn-1x-Fl5.title" = "Položaj naslovne vrstice"; + +/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ +"mqV-pf-NZ1.text" = "Položaj naslovne vrstice"; + /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Naj DuckDuckGo upravlja pojavna okna s soglasjem za piškotke"; @@ -175,15 +193,27 @@ /* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ "oM7-1o-9oY.text" = "O DuckDuckGo"; +/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ +"opn-JO-idF.text" = "Vrh"; + /* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ "OxE-MQ-uJk.headerTitle" = "Več od iskalnika DuckDuckGo"; +/* Class = "UILabel"; text = "Sync"; ObjectID = "oXN-ez-gct"; */ +"oXN-ez-gct.text" = "Sinhronizacija"; + /* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ "P0F-ts-ekd.text" = "Brskajte zasebno z našo aplikacijo za računalnike Mac "; +/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ +"qah-gb-udB.text" = "Network Protection"; + /* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ "qeN-SV-zy7.text" = "Oznaka"; +/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ +"RQ8-H1-Ez1.text" = "Aplikacija DuckDuckGo za sistem Windows"; + /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Velikost besedila"; @@ -232,8 +262,8 @@ /* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ "yvj-LL-MiR.text" = "Dodajte aplikacijo na svoj domači zaslon"; -/* Class = "UILabel"; text = "DuckDuckGo Desktop App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "Namizna aplikacija DuckDuckGo"; +/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ +"Yz9-17-qnn.text" = "Aplikacija DuckDuckGo za računalnike Mac"; /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Nov zavihek"; diff --git a/DuckDuckGo/sv.lproj/Feedback.strings b/DuckDuckGo/sv.lproj/Feedback.strings index 3d5436cf98..1c1dce6277 100644 --- a/DuckDuckGo/sv.lproj/Feedback.strings +++ b/DuckDuckGo/sv.lproj/Feedback.strings @@ -28,9 +28,6 @@ /* Class = "UIBarButtonItem"; title = "Back"; ObjectID = "JF8-Ow-zXL"; */ "JF8-Ow-zXL.title" = "Tillbaka"; -/* Class = "UIBarButtonItem"; title = "Submit"; ObjectID = "jr6-Al-4fr"; */ -"jr6-Al-4fr.title" = "Skicka"; - /* Class = "UIButton"; normalTitle = "Submit"; ObjectID = "lsB-Kq-U8p"; */ "lsB-Kq-U8p.normalTitle" = "Skicka"; @@ -58,9 +55,6 @@ /* Class = "UINavigationItem"; title = "Report Broken Site"; ObjectID = "WjT-HM-yim"; */ "WjT-HM-yim.title" = "Rapportera skadad webbplats"; -/* Class = "UILabel"; text = "Broken Site Reason"; ObjectID = "Wx3-Vb-ItS"; */ -"Wx3-Vb-ItS.text" = "Orsak till skadad webbplats"; - /* Class = "UINavigationItem"; title = "Share Feedback"; ObjectID = "Xb0-Zu-opM"; */ "Xb0-Zu-opM.title" = "Berätta vad du tycker"; diff --git a/DuckDuckGo/sv.lproj/Localizable.strings b/DuckDuckGo/sv.lproj/Localizable.strings index 63175f919e..8cd3fdbc47 100644 --- a/DuckDuckGo/sv.lproj/Localizable.strings +++ b/DuckDuckGo/sv.lproj/Localizable.strings @@ -1,6 +1,9 @@ /* No comment provided by engineer. */ "%@" = "%@"; +/* No comment provided by engineer. */ +"%@ [%@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)" = "%1$@ [%2$@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)"; + /* Buton label for Edit action */ "action.generic.edit" = "Redigera"; @@ -103,6 +106,12 @@ /* Share action */ "action.title.share" = "Dela"; +/* Settings label for bottom position for the address bar */ +"address.bar.bottom" = "Botten"; + +/* Settings label for top position for the address bar */ +"address.bar.top" = "Topp"; + /* No comment provided by engineer. */ "addWidget.button" = "Lägg till widget"; @@ -142,6 +151,225 @@ /* Shown on authentication screen */ "app.authentication.unlock" = "Lås upp DuckDuckGo."; +/* First part of about page content (note the trailing space) */ +"appTP.about.content1" = "You’ve probably heard about companies like Google and Facebook tracking you behind the scenes on third-party websites. But did you know they also track your personal information through apps on your device?\n\nIn 2022, DuckDuckGo found that "; + +/* Second part of about page content (note the trailing space) */ +"appTP.about.content2" = "over 85% of free iOS apps tested contained hidden trackers from other companies."; + +/* Third part of about page content (note the leading space) */ +"appTP.about.content3" = " Of the 395 apps tested, 60% sent data to Google. This happens even while you’re not using your device.\n\nTrackers in apps may have access to a lot more information than their website tracker cousins, such as your location down to which floor of a building you're on, how often you play games while at work, and when and how long you sleep each day. Even if you haven’t given apps explicit permission to collect data, they can still take it without your knowledge.\n\nTracking networks like Facebook and Google use these little pieces of information to build a digital profile about you. With it, tracking networks can manipulate what you see online and allow advertisers to bid on access to you based on your data.\n\nTrackers in apps is a BIG problem for privacy. But DuckDuckGo has a solution that can help.\n\nWhen enabled in the DuckDuckGo Privacy Browser app, App Tracking Protection blocks many trackers in other apps, not just the trackers we find on websites when you browse. These dual layers of protection reduce what companies know about you overall, so you can use your apps with more peace of mind, knowing you’re more protected."; + +/* Navigation Title for AppTP about page */ +"appTP.about.navTitle" = "About App Trackers"; + +/* Title for AppTP about page */ +"appTP.about.title" = "What Are App Trackers?"; + +/* Title for 'Report an Issue' button in the activity view. */ +"appTP.activityView.reportIssue" = "Report Issue"; + +/* Text label for switch that turns blocking on or off for a tracker */ +"appTP.blockTrackerText" = "Block this Tracker"; + +/* Detail string describing what AppTP is */ +"appTP.cell.detail" = "Blockera appspårare på din enhet"; + +/* String indicating AppTP is disabled when viewed from the settings screen */ +"appTP.cell.disabled" = "Inaktiverad"; + +/* String indicating AppTP is enabled when viewed from the settings screen */ +"appTP.cell.enabled" = "Aktiverad"; + +/* Info string informing the user what App Tracking Protection does. */ +"appTP.empty.disabled.info" = "Aktivera App Tracking Protection så att vi kan blockera irriterande spårare i andra appar."; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.heading" = "We’re blocking hidden trackers"; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.info" = "Come back soon to see a list of all the app trackers we’ve blocked."; + +/* First answer for AppTP FAQ page */ +"appTP.faq.answer1" = "App Tracking Protection blocks app trackers from other companies, like when Facebook tries to track you in a banking app. Companies may still track you in apps they own."; + +/* Second answer for AppTP FAQ page */ +"appTP.faq.answer2" = "Yes! App Tracking Protection works across all apps on your device to block the most common hidden trackers we find trying to collect your personal info."; + +/* Third answer for AppTP FAQ page */ +"appTP.faq.answer3" = "We currently only block the most common trackers that we find on iOS. This helps us to comprehensively test App Tracking Protection and lower frequency of app breakage, while blocking up to 70% of all tracking requests."; + +/* Fourth answer for AppTP FAQ page */ +"appTP.faq.answer4" = "You’ll be asked to set up a virtual private network (VPN) connection, but you don't need to install a VPN app for App Tracking Protection to work.\n\nThis permission, which works only on your device, allows App Tracking Protection to monitor network traffic so that it can block known trackers."; + +/* Fifth answer for AppTP FAQ page */ +"appTP.faq.answer5" = "You can use App Tracking Protection at the same time as using an IKEv2 protocol VPN app on an iOS device. You won’t be able to use App Tracking Protection on an iOS device if you’re using a VPN app that uses a different type of protocol, like WireGuard or OpenVPN type VPNs."; + +/* Sixth answer for AppTP FAQ page */ +"appTP.faq.answer6" = "A VPN sends your data from the device to its own server, where it secures and anonymizes your data from prying eyes. However, this allows the VPN company to see your network traffic.\n\nApp Tracking Protection is different. Instead of sending your data to a VPN server, App Tracking Protection works only on your device, sitting between your apps and the servers they talk to.\n\nWhenever App Tracking Protection recognizes a known tracker, it blocks the tracker from sending personal information (such as your IP address, activity, and device details) off your device. All other traffic reaches its destination, so your apps work normally."; + +/* Seventh answer for AppTP FAQ page */ +"appTP.faq.answer7" = "App Tracking Protection works only on your device and doesn’t send your data off your device to DuckDuckGo. We don’t collect or store any data from your apps."; + +/* First question for AppTP FAQ page */ +"appTP.faq.question1" = "How does App Tracking Protection work?"; + +/* Second question for AppTP FAQ page */ +"appTP.faq.question2" = "Does App Tracking Protection block trackers in all apps on my device?"; + +/* Third question for AppTP FAQ page */ +"appTP.faq.question3" = "Does App Tracking Protection block all app trackers?"; + +/* Fourth question for AppTP FAQ page */ +"appTP.faq.question4" = "Why does App Tracking Protection use a VPN connection?"; + +/* Fifth question for AppTP FAQ page */ +"appTP.faq.question5" = "Will App Tracking Protection work if I also use a VPN app?"; + +/* Sixth question for AppTP FAQ page */ +"appTP.faq.question6" = "How is App Tracking Protection different from a VPN?"; + +/* Seventh question for AppTP FAQ page */ +"appTP.faq.question7" = "Is my data private?"; + +/* Title for AppTP FAQ page */ +"appTP.faq.title" = "App Tracking Protection FAQ"; + +/* Do not translate. StringsDict entry -- Count part of string 'App Tracking Protection blocked x tracking attempts today' */ +"appTP.home.blockedCount" = "appTP.home.blockedCount"; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the trailing space) */ +"appTP.home.blockedPrefix" = "App Tracking Protection har blockerat "; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the leading space) */ +"appTP.home.blockedSuffix" = " i dina appar idag."; + +/* Prefix of string 'App Tracking Protection disabled. Tap to re-enable.' (note the trailing space) */ +"appTP.home.disabledPrefix" = "App Tracking Protection disabled. "; + +/* Suffix of string 'App Tracking Protection disabled. Tap to re-enable.' */ +"appTP.home.disabledSuffix" = "Tap to continue blocking tracking attempts across your apps."; + +/* Text indicating the tracking event occured 'just now'. Example: Last attempt 'just now' */ +"appTP.justNow" = "Nyss"; + +/* View to manage trackers for AppTP. Allows the user to turn trackers on or off. */ +"appTP.manageTrackers" = "Hantera spårare"; + +/* Button title for AppTP onboarding */ +"appTP.onboarding.continueButton" = "Continue"; + +/* Button title for AppTP onboarding to enable AppTP */ +"appTP.onboarding.enableeButton" = "Enable App Tracking Protection"; + +/* Button title for AppTP onboarding to learn more about AppTP */ +"appTP.onboarding.learnMoreButton" = "Learn More"; + +/* First part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info1" = "Over 85% of free iOS apps"; + +/* Second part of info on the first AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page1Info2" = " we’ve tested allow other companies to track your personal information, even when you’re sleeping."; + +/* Third part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info3" = "See who we catch trying to track you in your apps and take back control."; + +/* First part of info on the second AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page2Info1" = "App Tracking Protection "; + +/* Second part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info2" = "detects and blocks app trackers from other companies,"; + +/* Third part of info on the second AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page2Info3" = " like when Google attempts to track you in a health app."; + +/* Fourth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info4" = "It’s free,"; + +/* Fifth part of info on the second AppTP onboarding page (note the leading and trailing space) */ +"appTP.onboarding.page2Info5" = " and you can enjoy your apps as you normally would. Working in the background, it helps "; + +/* Sixth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info6" = "protect you night and day."; + +/* First part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info1" = "App Tracking Protection is not a VPN."; + +/* Second part of info on the third AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page3Info2" = " However, your device will recognize it as one. This is because it uses a local VPN connection to work."; + +/* Third part of info on the third AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page3Info3" = "App Tracking Protection is different. "; + +/* Fourth part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info4" = "It never routes app data through an external server."; + +/* Title for first AppTP onboarding page */ +"appTP.onboarding.title1" = "One easy step for better app privacy!"; + +/* Title for second AppTP onboarding page */ +"appTP.onboarding.title2" = "How does it work?"; + +/* Title for third AppTP onboarding page */ +"appTP.onboarding.title3" = "Who sees your data?"; + +/* Breakage report app name label */ +"appTP.report.appLabel" = "Which app is having issues?"; + +/* Breakage report app name placeholder */ +"appTP.report.appPlaceholder" = "App name"; + +/* Breakage report category label */ +"appTP.report.categoryLabel" = "Vad har hänt?"; + +/* Breakage report comment label */ +"appTP.report.commentLabel" = "Comments"; + +/* Breakage report comment placeholder */ +"appTP.report.commentPlaceholder" = "Add additional details"; + +/* Breakage report footer explaining what is collected in the breakage report */ +"appTP.report.footer" = "In addition to the details entered into this form, your app issue report will contain:\n• A list of trackers blocked in the last 10 min\n• Whether App Tracking Protection is enabled\n• Aggregate DuckDuckGo app diagnostics"; + +/* Breakage report submit button */ +"appTP.report.submit" = "Skicka"; + +/* Breakage report form title */ +"appTP.report.title" = "Report Issue"; + +/* Breakage report succcess message */ +"appTP.report.toast" = "Tack! Återkopplingen har skickats."; + +/* Cancel button for 'Report an Issue' alert. */ +"appTP.reportAlert.cancel" = "Inte nu"; + +/* Confirm button for 'Report an Issue' alert. */ +"appTP.reportAlert.confirm" = "Rapportera problem"; + +/* Message for 'Report an Issue' alert. */ +"appTP.reportAlert.message" = "Let us know if you disabled App Tracking Protection for this specific tracker because it caused app issues. Your feedback helps us improve!"; + +/* Title for 'Report an Issue' alert. */ +"appTP.reportAlert.title" = "Report Issue?"; + +/* Toast notification diplayed after restoring the blocklist to default settings */ +"appTP.restoreDefaultsToast" = "Default settings restored"; + +/* Button to restore the blocklist to its default state. */ +"appTP.restoreDefualts" = "Återställ standardinställningar"; + +/* Title for the App Tracking Protection feature */ +"appTP.title" = "Skydd mot appspårning"; + +/* Text indicating when the tracker was last allowed. Example: Last attempt allowed (timeString) */ +"appTP.trackerAllowedTimestamp" = "Senaste försöket tilläts %@"; + +/* Text indicating when the tracker was last blocked. Example: Last attempt blocked (timeString) */ +"appTP.trackerBlockedTimestamp" = "Senaste försöket blockerades %@"; + +/* Do not translate. StringsDict entry -- Subtitle for tracking attempts in App Tracking Protection Activity View. Example: (count) tracking attempts */ +"appTP.trackingattempts" = "appTP.trackingattempts"; + /* Authentication Alert Sign In Button */ "auth.alert.login.button" = "Logga in"; @@ -301,6 +529,18 @@ /* Toast message when a login item without a title is deleted */ "autofill.logins.list.login-deleted-message-no-title" = "Inloggning raderad"; +/* Title for a button that allows a user to reset their list of never saved sites */ +"autofill.logins.list.never.saved" = "Återställ exkluderade webbplatser"; + +/* Cancel button for resetting list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.cancel" = "Avbryt"; + +/* Confirm button to reset list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.confirm" = "Återställ exkluderade webbplatser"; + +/* Alert title */ +"autofill.logins.list.never.saved.reset.action.title" = "Om du återställer exkluderade webbplatser kommer du att uppmanas att spara din inloggning nästa gång du loggar in på någon av dessa webbplatser."; + /* Placeholder for search field on autofill login listing */ "autofill.logins.list.search-placeholder" = "Sök inloggningar"; @@ -335,7 +575,7 @@ "autofill.logins.prompt.password.button.title" = "Lösenord för %@"; /* Title for autofill login prompt */ -"autofill.logins.prompt.title" = "Använd sparad inloggning?"; +"autofill.logins.prompt.title" = "Vill du använda ett sparat lösenord?"; /* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ "autofill.logins.search.no-results.subtitle" = "för ”%@”"; @@ -385,27 +625,24 @@ /* Title for the alert dialog telling the user an updated username is no longer a private email address */ "autofill.removed.duck.address.title" = "Användarnamn för privat Duck Address har tagits bort"; +/* CTA displayed on modal asking if the user never wants to be prompted to save a login for this website agin */ +"autofill.save-login.never-prompt.CTA" = "Fråga aldrig för den här webbplatsen"; + /* Message displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.message" = "Inloggningar lagras säkert på din enhet i menyn Inloggningar."; +"autofill.save-login.new-user.message" = "Lösenord lagras säkert på din enhet i menyn Inloggningar."; /* Title displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.title" = "Vill du att DuckDuckGo ska spara din inloggning?"; +"autofill.save-login.new-user.title" = "Vill du att DuckDuckGo ska spara ditt lösenord?"; /* Cancel CTA displayed on modal asking for the user to save the login */ "autofill.save-login.not-now.CTA" = "Spara inte"; -/* Confirm CTA displayed on modal asking for the user to save the login */ -"autofill.save-login.save.CTA" = "Spara inloggning"; - /* Title displayed on modal asking for the user to save the login */ "autofill.save-login.title" = "Spara inloggning?"; /* Confirm CTA displayed on modal asking for the user to save the password */ "autofill.save-password.save.CTA" = "Spara lösenord"; -/* Title displayed on modal asking for the user to save the password */ -"autofill.save-password.title" = "Spara lösenord?"; - /* Accessibility title for a Show Password button displaying actial password instead of ***** */ "autofill.show-password" = "Visa lösenord"; @@ -413,7 +650,7 @@ "autofill.signin.to.manage" = "%@ för att hantera Duck Addresses på denna enhet."; /* Message displayed on modal asking for the user to update the password */ -"autofill.update-password.message" = "DuckDuckGo uppdaterar den här lagrade inloggningen på din enhet."; +"autofill.update-password.message" = "DuckDuckGo kommer att uppdatera det lagrade lösenordet på din enhet."; /* Confirm CTA displayed on modal asking for the user to update the password */ "autofill.update-password.save.CTA" = "Uppdatera lösenord"; @@ -442,6 +679,15 @@ /* Placeholder in the add bookmark form */ "bookmark.address.placeholder" = "www.example.com"; +/* Delete bookmark alert message */ +"bookmark.delete.alert.message" = "Ditt bokmärke för %@ kommer att raderas"; + +/* Delete bookmark alert title */ +"bookmark.delete.alert.title" = "Delete?"; + +/* The message shown after a bookmark has been deleted */ +"bookmark.deleted.toast" = "Bokmärke raderat"; + /* Delete bookmark folder alert delete button */ "bookmark.deleteFolderAlert.deleteButton" = "Ta bort"; @@ -538,9 +784,21 @@ /* Broken Site Category */ "brokensite.category.videos" = "Videon spelades inte upp"; +/* Broken Site Category Placeholder */ +"brokensite.categoryPlaceholder" = "Välj din fråga i listan ..."; + +/* Broken Site Category Section Title */ +"brokensite.categoryTitle" = "VÄLJ EN KATEGORI"; + +/* Broken Site Comment Placeholder */ +"brokensite.commentPlaceholder" = "Om du delar mer information kan det hjälpa oss att lösa det här problemet"; + /* Broken Site Section Title */ "brokensite.sectionTitle" = "BERÄTTA VAD SOM HÄNDE"; +/* No comment provided by engineer. */ +"bucket: %@" = "bucket: %@"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Senaste månaden"; @@ -772,6 +1030,39 @@ /* Empty search placeholder on bookmarks search */ "empty.search" = "Inga träffar hittades"; +/* No comment provided by engineer. */ +"Error" = "Fel"; + +/* Button title to Sign In */ +"error.email-protection-sign-in.action" = "Logga in"; + +/* Alert message */ +"error.email-protection-sign-in.body" = "Logga in igen för att återaktivera webbläsarens funktioner för Email Protection."; + +/* Alert title */ +"error.email-protection-sign-in.title" = "Fel i Email Protection"; + +/* Button title to open device settings */ +"error.insufficient-disk-space.action" = "Öppna inställningar"; + +/* Alert message */ +"error.insufficient-disk-space.body" = "Det verkar inte finnas ledigt lagringsutrymme på din enhet. Frigör utrymme för att fortsätta."; + +/* Alert title */ +"error.insufficient-disk-space.title" = "Inte tillräckligt med lagringsutrymme"; + +/* Button title that is shutting down the app */ +"error.preemptive-crash.action" = "Stäng app"; + +/* Alert message */ +"error.preemptive-crash.body" = "Det är ett problem med appen och den behöver stängas ner. Öppna den på nytt för att fortsätta."; + +/* Alert title */ +"error.preemptive-crash.title" = "Problem med appen har upptäckts"; + +/* Generic error message on a dialog for when the cause is not known. */ +"error.unknown.try.again" = "An unknown error has occurred"; + /* No comment provided by engineer. */ "favorite" = "Favorit"; @@ -991,6 +1282,18 @@ /* Home tab title */ "homeTab.title" = "Hem"; +/* OK title for invite screen alert dismissal button */ +"invite.alert.ok.button" = "OK"; + +/* Continue button on an invite dialog */ +"invite.dialog.continue.button" = "Continue"; + +/* Get Started button on an invite dialog */ +"invite.dialog.get.started.button" = "Get Started"; + +/* Message to show after user enters an unrecognized invite code */ +"invite.dialog.unrecognized.code.message" = "We didn’t recognize this Invite Code."; + /* No comment provided by engineer. */ "keyCommandAddBookmark" = "Lägg till bokmärke"; @@ -1057,15 +1360,15 @@ /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "DuckDuckGo för Mac har hastigheten du behöver, webbläsarfunktionerna du förväntar dig och våra många förstklassiga integritetsfunktioner."; -/* Body text for the macOS waitlist notification */ -"mac-waitlist.available.notification.body" = "Öppna din inbjudan"; - /* Title for the macOS waitlist notification */ "mac-waitlist.available.notification.title" = "DuckDuckGo för Mac är klar!"; /* Title for the copy action */ "mac-waitlist.copy" = "Kopiera"; +/* Description text above the Share Link button */ +"mac-waitlist.join-waitlist-screen.on-your-computer-go-to" = "På din Windows-dator går du till:"; + /* Description text above the Share Link button */ "mac-waitlist.join-waitlist-screen.on-your-mac-go-to" = "På din Mac går du till:"; @@ -1078,6 +1381,9 @@ /* Disclaimer for the Join Waitlist screen */ "mac-waitlist.join-waitlist-screen.windows" = "Windows kommer snart!"; +/* Title for the macOS waitlist button redirecting to Windows waitlist */ +"mac-waitlist.join-waitlist-screen.windows-waitlist" = "Letar du efter Windows-versionen?"; + /* Title for the settings subtitle */ "mac-waitlist.settings.browse-privately" = "Surfa privat med vår app för Mac"; @@ -1088,7 +1394,7 @@ "mac-waitlist.share-sheet.title" = "DuckDuckGo för Mac"; /* Title for the Mac Waitlist feature */ -"mac-waitlist.title" = "DuckDuckGo-appen för dator"; +"mac-waitlist.title" = "DuckDuckGo-app för Mac"; /* No comment provided by engineer. */ "menu.button.hint" = "Bläddra-meny"; @@ -1105,9 +1411,81 @@ /* Edit button */ "navigation.title.edit" = "Redigera"; +/* String indicating NetP is connected when viewed from the settings screen */ +"netP.cell.connected" = "Connected"; + +/* String indicating NetP is disconnected when viewed from the settings screen */ +"netP.cell.disconnected" = "Not connected"; + +/* Title for the Network Protection feature */ +"netP.title" = "Network Protection"; + +/* Message for the network protection invite dialog */ +"network.protection.invite.dialog.message" = "Enter your invite code to get started."; + +/* Title for the network protection invite screen */ +"network.protection.invite.dialog.title" = "You're invited to try Network Protection"; + +/* Prompt for the network protection invite code text field */ +"network.protection.invite.field.prompt" = "Invite Code"; + +/* Message explaining that netP is invite only */ +"network.protection.invite.only.message" = "DuckDuckGo Network Protection is currently invite-only."; + +/* Message for the network protection invite success view */ +"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; + +/* Title for the network protection invite success view */ +"network.protection.invite.success.title" = "Success! You’re in."; + +/* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ +"network.protection.status.connected.format" = "Connected - %@"; + +/* The label for the NetP VPN when connecting */ +"network.protection.status.connecting" = "Connecting..."; + +/* The label for the NetP VPN when disconnected */ +"network.protection.status.disconnected" = "Not connected"; + +/* The label for the NetP VPN when disconnecting */ +"network.protection.status.disconnecting" = "Disconnecting..."; + +/* Message label text for the netP status view */ +"network.protection.status.header.message" = "DuckDuckGo's VPN secures all of your device's Internet traffic anytime, anywhere."; + +/* Header title label text for the status view when netP is disconnected */ +"network.protection.status.header.title.off" = "Network Protection is Off"; + +/* Header title label text for the status view when netP is connected */ +"network.protection.status.header.title.on" = "Network Protection is On"; + +/* The status view 'Share Feedback' button which is shown inline on the status view after the \(netPInviteOnlyMessage) text */ +"network.protection.status.menu.share.feedback" = "Share Feedback"; + +/* Connection details label shown in NetworkProtection's status view. */ +"network.protection.status.view.connection.details" = "Connection Details"; + +/* Generic connection failed error message shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.message" = "Please try again later."; + +/* Generic connection failed error title shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; + +/* IP Address label shown in NetworkProtection's status view. */ +"network.protection.status.view.ip.address" = "IP Address"; + +/* Location label shown in NetworkProtection's status view. */ +"network.protection.status.view.location" = "Location"; + +/* Title label text for the status view when netP is disconnected */ +"network.protection.status.view.title" = "Network Protection"; + /* Do not translate - stringsdict entry */ "number.of.tabs" = "number.of.tabs"; +/* No comment provided by engineer. */ +"Ok" = "OK"; + /* Text displayed on notification appearing in the address bar when the browser dismissed the cookie popup automatically rejecting it */ "omnibar.notification.cookies-managed" = "Cookies hanteras"; @@ -1147,6 +1525,18 @@ /* ’Link’ is link on a website */ "open.externally.failed" = "Kan inte hitta appen som krävs för att öppna den här länken"; +/* No comment provided by engineer. */ +"Please enter which app on your device is broken." = "Ange vilken app som inte fungerar på din enhet."; + +/* Activate button */ +"pm.activate" = "Reactivate"; + +/* Cancel button */ +"pm.cancel" = "Cancel"; + +/* Deactivate button */ +"pm.deactivate" = "Deactivate"; + /* No comment provided by engineer. */ "preserveLogins.domain.list.footer" = "Webbplatser använder kakor för att hålla dig inloggad. Om du brandsäkrar en webbplats kommer kakor inte att raderas och du förblir inloggad även efter att du har använt brännarknappen. Vi blockerar fortfarande tredje parters spårare som finns på brandsäkrade webbplatser."; @@ -1216,8 +1606,8 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Favoriter"; -/* No comment provided by engineer. */ -"settings.about.text" = "På DuckDuckGo sätter vi en ny standard för förtroende på nätet.\n\nDuckDuckGo Privacy Browser tillhandahåller alla de grundläggande sekretesstjänster du behöver för att skydda dig själv när du söker och surfar på nätet, inklusive blockering av spårare, smartare kryptering och DuckDuckGo privat sökning.\n\nInternet borde ju trots allt inte kännas så skrämmande och det bör vara lika enkelt att få det integritetsskydd du förtjänar som det är att dra ner rullgardinen."; +/* about page */ +"settings.about.text" = "DuckDuckGo är det oberoende integritetsskyddsföretaget som grundades 2008 för alla som är trötta på att bli spårade online och vill ha en enkel lösning. Vi är beviset på att man kan få riktigt integritetsskydd på nätet utan att kompromissa.\n\nDuckDuckGo-webbläsaren har de funktioner du förväntar dig av en vanlig webbläsare, som bokmärken, flikar, lösenord och mer, plus över [ett dussin kraftfulla integritetsskydd](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/) som inte ingår som standard i de flesta populära webbläsare. Den här omfattande uppsättningen av integritetsskydd hjälper dig att skydda dina onlineaktiviteter, från sökning och surfning till e-post och mycket mer.\n\nVårt integritetsskydd fungerar utan att du behöver känna till de tekniska detaljerna eller ta itu med komplicerade inställningar. Det enda du behöver göra är att byta webbläsare till DuckDuckGo på alla dina enheter för att få integritet som standard.\n\nMen om du * verkligen* vill ta en titt under huven hittar du mer information om hur DuckDuckGos integritetsskydd fungerar på våra [hjälpsidor](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/)."; /* Report a Broken Site screen confirmation button */ "siteFeedback.buttonText" = "Skicka in rapport"; @@ -1237,6 +1627,9 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Vilken webbplats är skadad?"; +/* No comment provided by engineer. */ +"sync.remove-device.message" = "”%@” kommer inte längre att få tillgång till dina synkroniserade data."; + /* Accessibility label on remove button */ "tab.close.home" = "Stäng hemfliken"; @@ -1309,6 +1702,57 @@ /* Voice-search footer note with on-device privacy warning */ "voiceSearch.footer.note" = "Ljudet bearbetas på enheten. Det lagras inte och delas inte med någon, inte ens DuckDuckGo."; +/* Title for the button to enable push notifications in system settings */ +"waitlist.allow-notifications" = "Tillåt aviseringar"; + +/* Body text for the waitlist notification */ +"waitlist.available.notification.body" = "Öppna din inbjudan"; + +/* Title for the copy action */ +"waitlist.copy" = "Kopiera"; + +/* Label text for the invite code */ +"waitlist.invite-code" = "Inbjudningskod"; + +/* Step title on the invite screen */ +"waitlist.invite-screen.step.title" = "Steg %d"; + +/* Title for the invite code screen */ +"waitlist.invite-screen.youre-invited" = "Du är inbjuden!"; + +/* Title for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.join" = "Ställ dig i den privata väntelistan"; + +/* Temporary status text for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.joining" = "Går med i väntelista ..."; + +/* Title for the Share Link button */ +"waitlist.join-waitlist-screen.share-link" = "Dela länk"; + +/* Notification text for the waitlist */ +"waitlist.joined.no-notification.get-notification" = "MEDDELA MIG"; + +/* Title for the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.get-notification-confirmation-title" = "Vill du få ett meddelande när det är din tur?"; + +/* Cancel button in the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.no-thanks" = "Nej tack"; + +/* Text used for the Notifications Disabled state */ +"waitlist.notification.disabled" = "Vi kan meddela dig när det är din tur, men meddelanden är för närvarande avstängda för DuckDuckGo."; + +/* Privacy disclaimer for the Waitlist feature */ +"waitlist.privacy-disclaimer" = "Du behöver inte dela någon personlig information för att ställa dig på väntelistan. Du reserverar din plats i kön med en tidsstämpel som bara finns på din enhet, så att vi kan meddela dig när det är din tur."; + +/* Title for the queue screen */ +"waitlist.queue-screen.on-the-list" = "Du står på listan!"; + +/* Title for the settings subtitle */ +"waitlist.settings.download-available" = "Nedladdning tillgänglig"; + +/* Title for the share sheet entry */ +"waitlist.share-sheet.title" = "Du är inbjuden!"; + /* Confirmation message */ "web.url.remove.favorite.done" = "Favorit borttagen"; @@ -1333,3 +1777,51 @@ /* Alert title explaining the message is shown by a website */ "webJSAlert.website-message.format" = "Ett meddelande från %@:"; +/* No comment provided by engineer. */ +"Welcome to the Duck Side!" = "Välkommen till Duck-sidan!"; + +/* Title for the Windows waitlist notification */ +"windows-waitlist.available.notification.title" = "Testa DuckDuckGo för Windows!"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-1.description" = "Gå till den här webbadressen på din Windows-enhet för att ladda ner:"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-2.description" = "Öppna installationsprogrammet för DuckDuckGo i Nerladdningar, välj Installera och ange din inbjudningskod."; + +/* Subtitle for the Windows Waitlist Invite screen */ +"windows-waitlist.invite-screen.subtitle" = "Är du redo att använda DuckDuckGo på Windows?"; + +/* Title for the Windows waitlist button redirecting to Mac waitlist */ +"windows-waitlist.join-waitlist-screen.mac-waitlist" = "Letar du efter Mac-versionen?"; + +/* Title for the Join Windows Waitlist screen */ +"windows-waitlist.join-waitlist-screen.try-duckduckgo-for-windows" = "Få tidigare tillgång och prova DuckDuckGo för Windows!"; + +/* Message for the alert to confirm enabling notifications */ +"windows-waitlist.joined.no-notification.get-notification-confirmation-message" = "Vi skickar ett meddelande till dig när ditt exemplar av DuckDuckGo för Mac är klar att laddas ner. "; + +/* Label text for the Joined Waitlist state with notifications declined */ +"windows-waitlist.joined.notifications-declined" = "Din inbjudan att prova DuckDuckGo för Mac visas här. Kom tillbaka igen snart. Vi kan också skicka dig ett meddelande när det är din tur."; + +/* Label text for the Joined Waitlist state with notifications enabled */ +"windows-waitlist.joined.notifications-enabled" = "Vi skickar ett meddelande till dig när ditt exemplar av DuckDuckGo för Mac är klar att laddas ner."; + +/* Title for the settings subtitle */ +"windows-waitlist.settings.browse-privately" = "Surfa privat med vår app för Windows"; + +/* Message used when sharing to iMessage. Parameter is an eight digit invite code. */ +"windows-waitlist.share-sheet.invite-code-message" = "Du har fått en inbjudan!\n\nÄr du redo att använda DuckDuckGo på Windows?\n\nSteg 1\nGå till den här webbadressen på din Windows-enhet för att ladda ner:\nhttps://duckduckgo.com/windows\n\nSteg 2\nÖppna installationsprogrammet för DuckDuckGo i Nerladdningar, välj Installera och ange din inbjudningskod.\n\nInbjudningskod: %@"; + +/* Message used when sharing to iMessage */ +"windows-waitlist.share-sheet.message" = "Är du redo att börja surfa privat på Windows?\n\nLadda ner genom att gå till den här webbadressen på din dator:\nhttps://duckduckgo.com/windows"; + +/* Summary text for the Windows browser waitlist */ +"windows-waitlist.summary" = "DuckDuckGo för Windows har allt du behöver för att surfa med högre integritet – privat sökning, spårningsblockering, tvingad kryptering och blockering av popup-cookies – och fler förstklassiga skydd är på gång."; + +/* Title for the Windows Waitlist feature */ +"windows-waitlist.title" = "DuckDuckGo-app för Windows"; + +/* Title for the Windows browser download link page */ +"windows-waitlist.waitlist-download-screen.try-duckduckgo-for-windows" = "Skaffa DuckDuckGo för Windows!"; + diff --git a/DuckDuckGo/sv.lproj/Settings.strings b/DuckDuckGo/sv.lproj/Settings.strings index 1313eaac44..50d25a1314 100644 --- a/DuckDuckGo/sv.lproj/Settings.strings +++ b/DuckDuckGo/sv.lproj/Settings.strings @@ -37,6 +37,9 @@ /* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ "9Ko-0g-T3h.text" = "Textstorlek"; +/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ +"9kt-6R-XiZ.text" = "Rubrik"; + /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "App-start"; @@ -73,6 +76,12 @@ /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Global Privacy Control (GPC)"; +/* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ +"CxT-QK-iVn.title" = "Lägg till"; + +/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ +"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; + /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Avsluta app, inaktiv i 15 minuter"; @@ -151,6 +160,9 @@ /* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ "HLr-R8-xxF.text" = "Förhandsvisning vid nedhållning"; +/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ +"hoT-Nu-KXP.text" = "Surfa privat med vår app för Windows"; + /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Integritetsskydd aktiverat för alla webbplatser"; @@ -163,6 +175,12 @@ /* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ "m23-t6-9cb.text" = "Berätta vad du tycker"; +/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ +"mLn-1x-Fl5.title" = "Adressfältsläge"; + +/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ +"mqV-pf-NZ1.text" = "Adressfältsläge"; + /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "Låt DuckDuckGo hantera popup-fönster för godkännande av cookies"; @@ -175,15 +193,27 @@ /* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ "oM7-1o-9oY.text" = "Om DuckDuckGo"; +/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ +"opn-JO-idF.text" = "Topp"; + /* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ "OxE-MQ-uJk.headerTitle" = "Mer från DuckDuckGo"; +/* Class = "UILabel"; text = "Sync"; ObjectID = "oXN-ez-gct"; */ +"oXN-ez-gct.text" = "Synkronisering"; + /* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ "P0F-ts-ekd.text" = "Surfa privat med vår app för Mac "; +/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ +"qah-gb-udB.text" = "Network Protection"; + /* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ "qeN-SV-zy7.text" = "Etikett"; +/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ +"RQ8-H1-Ez1.text" = "DuckDuckGo-app för Windows"; + /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Textstorlek"; @@ -232,8 +262,8 @@ /* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ "yvj-LL-MiR.text" = "Lägg till app i din Dock"; -/* Class = "UILabel"; text = "DuckDuckGo Desktop App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "DuckDuckGo-appen för dator"; +/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ +"Yz9-17-qnn.text" = "DuckDuckGo-app för Mac"; /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Ny flik"; diff --git a/DuckDuckGo/tr.lproj/Feedback.strings b/DuckDuckGo/tr.lproj/Feedback.strings index 13e4b8fdd1..c16a94b613 100644 --- a/DuckDuckGo/tr.lproj/Feedback.strings +++ b/DuckDuckGo/tr.lproj/Feedback.strings @@ -28,9 +28,6 @@ /* Class = "UIBarButtonItem"; title = "Back"; ObjectID = "JF8-Ow-zXL"; */ "JF8-Ow-zXL.title" = "Geri"; -/* Class = "UIBarButtonItem"; title = "Submit"; ObjectID = "jr6-Al-4fr"; */ -"jr6-Al-4fr.title" = "Gönder"; - /* Class = "UIButton"; normalTitle = "Submit"; ObjectID = "lsB-Kq-U8p"; */ "lsB-Kq-U8p.normalTitle" = "Gönder"; @@ -58,9 +55,6 @@ /* Class = "UINavigationItem"; title = "Report Broken Site"; ObjectID = "WjT-HM-yim"; */ "WjT-HM-yim.title" = "Hatalı Siteyi Bildir"; -/* Class = "UILabel"; text = "Broken Site Reason"; ObjectID = "Wx3-Vb-ItS"; */ -"Wx3-Vb-ItS.text" = "Bozuk Site Sebebi"; - /* Class = "UINavigationItem"; title = "Share Feedback"; ObjectID = "Xb0-Zu-opM"; */ "Xb0-Zu-opM.title" = "Geri Bildirim Paylaş"; diff --git a/DuckDuckGo/tr.lproj/Localizable.strings b/DuckDuckGo/tr.lproj/Localizable.strings index 6b694dd10b..1b87ef3f5d 100644 --- a/DuckDuckGo/tr.lproj/Localizable.strings +++ b/DuckDuckGo/tr.lproj/Localizable.strings @@ -1,6 +1,9 @@ /* No comment provided by engineer. */ "%@" = "%@"; +/* No comment provided by engineer. */ +"%@ [%@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)" = "%1$@ [%2$@](https://form.asana.com/?k=_wNLt6YcT5ILpQjDuW0Mxw&d=137249556945)"; + /* Buton label for Edit action */ "action.generic.edit" = "Düzenle"; @@ -103,6 +106,12 @@ /* Share action */ "action.title.share" = "Paylaş"; +/* Settings label for bottom position for the address bar */ +"address.bar.bottom" = "Alt"; + +/* Settings label for top position for the address bar */ +"address.bar.top" = "Top"; + /* No comment provided by engineer. */ "addWidget.button" = "Widget Ekle"; @@ -142,6 +151,225 @@ /* Shown on authentication screen */ "app.authentication.unlock" = "DuckDuckGo'nun kilidini açın."; +/* First part of about page content (note the trailing space) */ +"appTP.about.content1" = "You’ve probably heard about companies like Google and Facebook tracking you behind the scenes on third-party websites. But did you know they also track your personal information through apps on your device?\n\nIn 2022, DuckDuckGo found that "; + +/* Second part of about page content (note the trailing space) */ +"appTP.about.content2" = "over 85% of free iOS apps tested contained hidden trackers from other companies."; + +/* Third part of about page content (note the leading space) */ +"appTP.about.content3" = " Of the 395 apps tested, 60% sent data to Google. This happens even while you’re not using your device.\n\nTrackers in apps may have access to a lot more information than their website tracker cousins, such as your location down to which floor of a building you're on, how often you play games while at work, and when and how long you sleep each day. Even if you haven’t given apps explicit permission to collect data, they can still take it without your knowledge.\n\nTracking networks like Facebook and Google use these little pieces of information to build a digital profile about you. With it, tracking networks can manipulate what you see online and allow advertisers to bid on access to you based on your data.\n\nTrackers in apps is a BIG problem for privacy. But DuckDuckGo has a solution that can help.\n\nWhen enabled in the DuckDuckGo Privacy Browser app, App Tracking Protection blocks many trackers in other apps, not just the trackers we find on websites when you browse. These dual layers of protection reduce what companies know about you overall, so you can use your apps with more peace of mind, knowing you’re more protected."; + +/* Navigation Title for AppTP about page */ +"appTP.about.navTitle" = "About App Trackers"; + +/* Title for AppTP about page */ +"appTP.about.title" = "What Are App Trackers?"; + +/* Title for 'Report an Issue' button in the activity view. */ +"appTP.activityView.reportIssue" = "Report Issue"; + +/* Text label for switch that turns blocking on or off for a tracker */ +"appTP.blockTrackerText" = "Block this Tracker"; + +/* Detail string describing what AppTP is */ +"appTP.cell.detail" = "Cihazınızdaki uygulama izleyicileri engelleyin"; + +/* String indicating AppTP is disabled when viewed from the settings screen */ +"appTP.cell.disabled" = "Devre Dışı"; + +/* String indicating AppTP is enabled when viewed from the settings screen */ +"appTP.cell.enabled" = "Etkin"; + +/* Info string informing the user what App Tracking Protection does. */ +"appTP.empty.disabled.info" = "Diğer uygulamalardaki sinir bozucu izleyicileri engelleyebilmek için App Tracking Protection özelliğini etkinleştirin."; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.heading" = "We’re blocking hidden trackers"; + +/* Info string informing the user we're looking for trackers in other apps. */ +"appTP.empty.enabled.info" = "Come back soon to see a list of all the app trackers we’ve blocked."; + +/* First answer for AppTP FAQ page */ +"appTP.faq.answer1" = "App Tracking Protection blocks app trackers from other companies, like when Facebook tries to track you in a banking app. Companies may still track you in apps they own."; + +/* Second answer for AppTP FAQ page */ +"appTP.faq.answer2" = "Yes! App Tracking Protection works across all apps on your device to block the most common hidden trackers we find trying to collect your personal info."; + +/* Third answer for AppTP FAQ page */ +"appTP.faq.answer3" = "We currently only block the most common trackers that we find on iOS. This helps us to comprehensively test App Tracking Protection and lower frequency of app breakage, while blocking up to 70% of all tracking requests."; + +/* Fourth answer for AppTP FAQ page */ +"appTP.faq.answer4" = "You’ll be asked to set up a virtual private network (VPN) connection, but you don't need to install a VPN app for App Tracking Protection to work.\n\nThis permission, which works only on your device, allows App Tracking Protection to monitor network traffic so that it can block known trackers."; + +/* Fifth answer for AppTP FAQ page */ +"appTP.faq.answer5" = "You can use App Tracking Protection at the same time as using an IKEv2 protocol VPN app on an iOS device. You won’t be able to use App Tracking Protection on an iOS device if you’re using a VPN app that uses a different type of protocol, like WireGuard or OpenVPN type VPNs."; + +/* Sixth answer for AppTP FAQ page */ +"appTP.faq.answer6" = "A VPN sends your data from the device to its own server, where it secures and anonymizes your data from prying eyes. However, this allows the VPN company to see your network traffic.\n\nApp Tracking Protection is different. Instead of sending your data to a VPN server, App Tracking Protection works only on your device, sitting between your apps and the servers they talk to.\n\nWhenever App Tracking Protection recognizes a known tracker, it blocks the tracker from sending personal information (such as your IP address, activity, and device details) off your device. All other traffic reaches its destination, so your apps work normally."; + +/* Seventh answer for AppTP FAQ page */ +"appTP.faq.answer7" = "App Tracking Protection works only on your device and doesn’t send your data off your device to DuckDuckGo. We don’t collect or store any data from your apps."; + +/* First question for AppTP FAQ page */ +"appTP.faq.question1" = "How does App Tracking Protection work?"; + +/* Second question for AppTP FAQ page */ +"appTP.faq.question2" = "Does App Tracking Protection block trackers in all apps on my device?"; + +/* Third question for AppTP FAQ page */ +"appTP.faq.question3" = "Does App Tracking Protection block all app trackers?"; + +/* Fourth question for AppTP FAQ page */ +"appTP.faq.question4" = "Why does App Tracking Protection use a VPN connection?"; + +/* Fifth question for AppTP FAQ page */ +"appTP.faq.question5" = "Will App Tracking Protection work if I also use a VPN app?"; + +/* Sixth question for AppTP FAQ page */ +"appTP.faq.question6" = "How is App Tracking Protection different from a VPN?"; + +/* Seventh question for AppTP FAQ page */ +"appTP.faq.question7" = "Is my data private?"; + +/* Title for AppTP FAQ page */ +"appTP.faq.title" = "App Tracking Protection FAQ"; + +/* Do not translate. StringsDict entry -- Count part of string 'App Tracking Protection blocked x tracking attempts today' */ +"appTP.home.blockedCount" = "appTP.home.blockedCount"; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the trailing space) */ +"appTP.home.blockedPrefix" = "App Tracking Protection engelledi: "; + +/* Prefix of string 'App Tracking Protection blocked x tracking attempts today' (note the leading space) */ +"appTP.home.blockedSuffix" = " bugün uygulamalarınızda."; + +/* Prefix of string 'App Tracking Protection disabled. Tap to re-enable.' (note the trailing space) */ +"appTP.home.disabledPrefix" = "App Tracking Protection disabled. "; + +/* Suffix of string 'App Tracking Protection disabled. Tap to re-enable.' */ +"appTP.home.disabledSuffix" = "Tap to continue blocking tracking attempts across your apps."; + +/* Text indicating the tracking event occured 'just now'. Example: Last attempt 'just now' */ +"appTP.justNow" = "Şimdi"; + +/* View to manage trackers for AppTP. Allows the user to turn trackers on or off. */ +"appTP.manageTrackers" = "İzleyicileri Yönet"; + +/* Button title for AppTP onboarding */ +"appTP.onboarding.continueButton" = "Continue"; + +/* Button title for AppTP onboarding to enable AppTP */ +"appTP.onboarding.enableeButton" = "Enable App Tracking Protection"; + +/* Button title for AppTP onboarding to learn more about AppTP */ +"appTP.onboarding.learnMoreButton" = "Learn More"; + +/* First part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info1" = "Over 85% of free iOS apps"; + +/* Second part of info on the first AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page1Info2" = " we’ve tested allow other companies to track your personal information, even when you’re sleeping."; + +/* Third part of info on the first AppTP onboarding page */ +"appTP.onboarding.page1Info3" = "See who we catch trying to track you in your apps and take back control."; + +/* First part of info on the second AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page2Info1" = "App Tracking Protection "; + +/* Second part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info2" = "detects and blocks app trackers from other companies,"; + +/* Third part of info on the second AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page2Info3" = " like when Google attempts to track you in a health app."; + +/* Fourth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info4" = "It’s free,"; + +/* Fifth part of info on the second AppTP onboarding page (note the leading and trailing space) */ +"appTP.onboarding.page2Info5" = " and you can enjoy your apps as you normally would. Working in the background, it helps "; + +/* Sixth part of info on the second AppTP onboarding page */ +"appTP.onboarding.page2Info6" = "protect you night and day."; + +/* First part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info1" = "App Tracking Protection is not a VPN."; + +/* Second part of info on the third AppTP onboarding page (note the leading space) */ +"appTP.onboarding.page3Info2" = " However, your device will recognize it as one. This is because it uses a local VPN connection to work."; + +/* Third part of info on the third AppTP onboarding page (note the trailing space) */ +"appTP.onboarding.page3Info3" = "App Tracking Protection is different. "; + +/* Fourth part of info on the third AppTP onboarding page */ +"appTP.onboarding.page3Info4" = "It never routes app data through an external server."; + +/* Title for first AppTP onboarding page */ +"appTP.onboarding.title1" = "One easy step for better app privacy!"; + +/* Title for second AppTP onboarding page */ +"appTP.onboarding.title2" = "How does it work?"; + +/* Title for third AppTP onboarding page */ +"appTP.onboarding.title3" = "Who sees your data?"; + +/* Breakage report app name label */ +"appTP.report.appLabel" = "Which app is having issues?"; + +/* Breakage report app name placeholder */ +"appTP.report.appPlaceholder" = "App name"; + +/* Breakage report category label */ +"appTP.report.categoryLabel" = "Ne oluyor?"; + +/* Breakage report comment label */ +"appTP.report.commentLabel" = "Comments"; + +/* Breakage report comment placeholder */ +"appTP.report.commentPlaceholder" = "Add additional details"; + +/* Breakage report footer explaining what is collected in the breakage report */ +"appTP.report.footer" = "In addition to the details entered into this form, your app issue report will contain:\n• A list of trackers blocked in the last 10 min\n• Whether App Tracking Protection is enabled\n• Aggregate DuckDuckGo app diagnostics"; + +/* Breakage report submit button */ +"appTP.report.submit" = "Gönder"; + +/* Breakage report form title */ +"appTP.report.title" = "Report Issue"; + +/* Breakage report succcess message */ +"appTP.report.toast" = "Teşekkürler! Geri bildirim gönderildi."; + +/* Cancel button for 'Report an Issue' alert. */ +"appTP.reportAlert.cancel" = "Şimdi Değil"; + +/* Confirm button for 'Report an Issue' alert. */ +"appTP.reportAlert.confirm" = "Sorun Bildir"; + +/* Message for 'Report an Issue' alert. */ +"appTP.reportAlert.message" = "Let us know if you disabled App Tracking Protection for this specific tracker because it caused app issues. Your feedback helps us improve!"; + +/* Title for 'Report an Issue' alert. */ +"appTP.reportAlert.title" = "Report Issue?"; + +/* Toast notification diplayed after restoring the blocklist to default settings */ +"appTP.restoreDefaultsToast" = "Default settings restored"; + +/* Button to restore the blocklist to its default state. */ +"appTP.restoreDefualts" = "Varsayılanları Geri Yükle"; + +/* Title for the App Tracking Protection feature */ +"appTP.title" = "Uygulama İzleme Koruması"; + +/* Text indicating when the tracker was last allowed. Example: Last attempt allowed (timeString) */ +"appTP.trackerAllowedTimestamp" = "İzin verilen son girişim: %@"; + +/* Text indicating when the tracker was last blocked. Example: Last attempt blocked (timeString) */ +"appTP.trackerBlockedTimestamp" = "Engellenen son girişim: %@"; + +/* Do not translate. StringsDict entry -- Subtitle for tracking attempts in App Tracking Protection Activity View. Example: (count) tracking attempts */ +"appTP.trackingattempts" = "appTP.trackingattempts"; + /* Authentication Alert Sign In Button */ "auth.alert.login.button" = "Oturum Aç"; @@ -301,6 +529,18 @@ /* Toast message when a login item without a title is deleted */ "autofill.logins.list.login-deleted-message-no-title" = "Giriş bilgileri silindi"; +/* Title for a button that allows a user to reset their list of never saved sites */ +"autofill.logins.list.never.saved" = "Hariç Tutulan Siteleri Sıfırla"; + +/* Cancel button for resetting list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.cancel" = "İptal"; + +/* Confirm button to reset list of never saved sites */ +"autofill.logins.list.never.saved.reset.action.confirm" = "Hariç Tutulan Siteleri Sıfırla"; + +/* Alert title */ +"autofill.logins.list.never.saved.reset.action.title" = "Hariç tutulan siteleri sıfırlarsanız, bu sitelerden herhangi birinde bir sonraki oturum açışınızda Giriş bilgilerinizi kaydetmeniz istenecektir."; + /* Placeholder for search field on autofill login listing */ "autofill.logins.list.search-placeholder" = "Giriş Ara"; @@ -335,7 +575,7 @@ "autofill.logins.prompt.password.button.title" = "%@ için parola"; /* Title for autofill login prompt */ -"autofill.logins.prompt.title" = "Kayıtlı Giriş kullanılsın mı?"; +"autofill.logins.prompt.title" = "Kayıtlı bir şifre mi kullanıyorsunuz?"; /* Subtitle displayed when there are no results on Autofill search, example : No Result (Title) for Duck (Subtitle) */ "autofill.logins.search.no-results.subtitle" = "\"%@\" için"; @@ -385,27 +625,24 @@ /* Title for the alert dialog telling the user an updated username is no longer a private email address */ "autofill.removed.duck.address.title" = "Özel Duck Address kullanıcı adı kaldırıldı"; +/* CTA displayed on modal asking if the user never wants to be prompted to save a login for this website agin */ +"autofill.save-login.never-prompt.CTA" = "Bu Site için Hiçbir Zaman Sorma"; + /* Message displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.message" = "Giriş bilgileri cihazınızdaki Giriş Bilgileri menüsünde güvenli bir şekilde saklanır."; +"autofill.save-login.new-user.message" = "Şifreler cihazınızdaki Oturum Açma menüsünde güvenli bir şekilde saklanır."; /* Title displayed on modal asking for the user to save the login for the first time */ -"autofill.save-login.new-user.title" = "DuckDuckGo'nun Girişinizi kaydetmesini istiyor musunuz?"; +"autofill.save-login.new-user.title" = "DuckDuckGo'nun şifrenizi kaydetmesini istiyor musunuz?"; /* Cancel CTA displayed on modal asking for the user to save the login */ "autofill.save-login.not-now.CTA" = "Kaydetme"; -/* Confirm CTA displayed on modal asking for the user to save the login */ -"autofill.save-login.save.CTA" = "Girişi Kaydet"; - /* Title displayed on modal asking for the user to save the login */ "autofill.save-login.title" = "Giriş Kaydedilsin mi?"; /* Confirm CTA displayed on modal asking for the user to save the password */ "autofill.save-password.save.CTA" = "Şifre Kaydet"; -/* Title displayed on modal asking for the user to save the password */ -"autofill.save-password.title" = "Şifre Kaydedilsin mi?"; - /* Accessibility title for a Show Password button displaying actial password instead of ***** */ "autofill.show-password" = "Parolayı göster"; @@ -413,7 +650,7 @@ "autofill.signin.to.manage" = "Bu cihazdaki Duck Address'leri yönetmek için %@."; /* Message displayed on modal asking for the user to update the password */ -"autofill.update-password.message" = "DuckDuckGo, cihazınızdaki bu kayıtlı Giriş bilgisini güncelleyecektir."; +"autofill.update-password.message" = "DuckDuckGo bu kayıtlı şifreyi cihazınızda güncelleyecektir."; /* Confirm CTA displayed on modal asking for the user to update the password */ "autofill.update-password.save.CTA" = "Şifreyi Güncelle"; @@ -442,6 +679,15 @@ /* Placeholder in the add bookmark form */ "bookmark.address.placeholder" = "www.example.com"; +/* Delete bookmark alert message */ +"bookmark.delete.alert.message" = "Bu, \"%@\" için yer işaretinizi silecektir"; + +/* Delete bookmark alert title */ +"bookmark.delete.alert.title" = "Delete?"; + +/* The message shown after a bookmark has been deleted */ +"bookmark.deleted.toast" = "Yer işareti silindi"; + /* Delete bookmark folder alert delete button */ "bookmark.deleteFolderAlert.deleteButton" = "Sil"; @@ -538,9 +784,21 @@ /* Broken Site Category */ "brokensite.category.videos" = "Video oynatılamadı"; +/* Broken Site Category Placeholder */ +"brokensite.categoryPlaceholder" = "Listeden yaşadığınız sorunu seçin..."; + +/* Broken Site Category Section Title */ +"brokensite.categoryTitle" = "BİR KATEGORİ SEÇİN"; + +/* Broken Site Comment Placeholder */ +"brokensite.commentPlaceholder" = "Daha fazla ayrıntı paylaşmak, bu sorunu çözmemize yardımcı olabilir"; + /* Broken Site Section Title */ "brokensite.sectionTitle" = "NE OLDUĞUNU AÇIKLAYIN"; +/* No comment provided by engineer. */ +"bucket: %@" = "kova: %@"; + /* Title for a section containing only items from past month */ "date.range.past-month" = "Geçen ay"; @@ -772,6 +1030,39 @@ /* Empty search placeholder on bookmarks search */ "empty.search" = "Eşleşme bulunamadı"; +/* No comment provided by engineer. */ +"Error" = "Hata"; + +/* Button title to Sign In */ +"error.email-protection-sign-in.action" = "Oturum Aç"; + +/* Alert message */ +"error.email-protection-sign-in.body" = "Üzgünüz. Bu tarayıcıda E-mail Protection özelliklerini yeniden etkinleştirmek için lütfen tekrar oturum açın."; + +/* Alert title */ +"error.email-protection-sign-in.title" = "Email Protection Hatası"; + +/* Button title to open device settings */ +"error.insufficient-disk-space.action" = "Ayarları Aç"; + +/* Alert message */ +"error.insufficient-disk-space.body" = "Cihazınızın depolama alanı bitmiş gibi görünüyor. Devam etmek için lütfen yer açın."; + +/* Alert title */ +"error.insufficient-disk-space.title" = "Yeterli depolama alanı yok"; + +/* Button title that is shutting down the app */ +"error.preemptive-crash.action" = "Uygulamayı kapat"; + +/* Alert message */ +"error.preemptive-crash.body" = "Görünüşe göre uygulamayla ilgili bir sorun var ve kapatılması gerekiyor. Devam etmek için lütfen yeniden açın."; + +/* Alert title */ +"error.preemptive-crash.title" = "Uygulama sorunu tespit edildi"; + +/* Generic error message on a dialog for when the cause is not known. */ +"error.unknown.try.again" = "An unknown error has occurred"; + /* No comment provided by engineer. */ "favorite" = "Favori"; @@ -991,6 +1282,18 @@ /* Home tab title */ "homeTab.title" = "Ana Sayfa"; +/* OK title for invite screen alert dismissal button */ +"invite.alert.ok.button" = "OK"; + +/* Continue button on an invite dialog */ +"invite.dialog.continue.button" = "Continue"; + +/* Get Started button on an invite dialog */ +"invite.dialog.get.started.button" = "Get Started"; + +/* Message to show after user enters an unrecognized invite code */ +"invite.dialog.unrecognized.code.message" = "We didn’t recognize this Invite Code."; + /* No comment provided by engineer. */ "keyCommandAddBookmark" = "Yer İmi ekle"; @@ -1057,15 +1360,15 @@ /* Summary text for the macOS browser waitlist */ "mac-browser.waitlist.summary" = "Mac için DuckDuckGo, ihtiyacınız olan hız ve beklediğiniz tarama özelliklerinin yanı sıra sınıfındaki en iyi gizlilik özelliklerini (Privacy Essentials) sunuyor."; -/* Body text for the macOS waitlist notification */ -"mac-waitlist.available.notification.body" = "Davetiyenizi açın"; - /* Title for the macOS waitlist notification */ "mac-waitlist.available.notification.title" = "Mac için DuckDuckGo hazır!"; /* Title for the copy action */ "mac-waitlist.copy" = "Kopyala"; +/* Description text above the Share Link button */ +"mac-waitlist.join-waitlist-screen.on-your-computer-go-to" = "Windows bilgisayarınızda şu adrese gidin:"; + /* Description text above the Share Link button */ "mac-waitlist.join-waitlist-screen.on-your-mac-go-to" = "Mac cihazınızda şu adrese gidin:"; @@ -1078,6 +1381,9 @@ /* Disclaimer for the Join Waitlist screen */ "mac-waitlist.join-waitlist-screen.windows" = "Windows sürümü çok yakında!"; +/* Title for the macOS waitlist button redirecting to Windows waitlist */ +"mac-waitlist.join-waitlist-screen.windows-waitlist" = "Windows Sürümünü mü arıyorsunuz?"; + /* Title for the settings subtitle */ "mac-waitlist.settings.browse-privately" = "Mac için uygulamamızla internette gezinirken gizliliğinizi koruyun"; @@ -1088,7 +1394,7 @@ "mac-waitlist.share-sheet.title" = "Mac için DuckDuckGo"; /* Title for the Mac Waitlist feature */ -"mac-waitlist.title" = "DuckDuckGo Masaüstü Uygulaması"; +"mac-waitlist.title" = "Mac için DuckDuckGo Uygulaması"; /* No comment provided by engineer. */ "menu.button.hint" = "Göz Atma Menüsü"; @@ -1105,9 +1411,81 @@ /* Edit button */ "navigation.title.edit" = "Düzenle"; +/* String indicating NetP is connected when viewed from the settings screen */ +"netP.cell.connected" = "Connected"; + +/* String indicating NetP is disconnected when viewed from the settings screen */ +"netP.cell.disconnected" = "Not connected"; + +/* Title for the Network Protection feature */ +"netP.title" = "Network Protection"; + +/* Message for the network protection invite dialog */ +"network.protection.invite.dialog.message" = "Enter your invite code to get started."; + +/* Title for the network protection invite screen */ +"network.protection.invite.dialog.title" = "You're invited to try Network Protection"; + +/* Prompt for the network protection invite code text field */ +"network.protection.invite.field.prompt" = "Invite Code"; + +/* Message explaining that netP is invite only */ +"network.protection.invite.only.message" = "DuckDuckGo Network Protection is currently invite-only."; + +/* Message for the network protection invite success view */ +"network.protection.invite.success.message" = "Hide your location from websites and conceal your online activity from Internet providers and others on your network."; + +/* Title for the network protection invite success view */ +"network.protection.invite.success.title" = "Success! You’re in."; + +/* The label for when NetP VPN is connected plus the length of time connected as a formatter HH:MM:SS string */ +"network.protection.status.connected.format" = "Connected - %@"; + +/* The label for the NetP VPN when connecting */ +"network.protection.status.connecting" = "Connecting..."; + +/* The label for the NetP VPN when disconnected */ +"network.protection.status.disconnected" = "Not connected"; + +/* The label for the NetP VPN when disconnecting */ +"network.protection.status.disconnecting" = "Disconnecting..."; + +/* Message label text for the netP status view */ +"network.protection.status.header.message" = "DuckDuckGo's VPN secures all of your device's Internet traffic anytime, anywhere."; + +/* Header title label text for the status view when netP is disconnected */ +"network.protection.status.header.title.off" = "Network Protection is Off"; + +/* Header title label text for the status view when netP is connected */ +"network.protection.status.header.title.on" = "Network Protection is On"; + +/* The status view 'Share Feedback' button which is shown inline on the status view after the \(netPInviteOnlyMessage) text */ +"network.protection.status.menu.share.feedback" = "Share Feedback"; + +/* Connection details label shown in NetworkProtection's status view. */ +"network.protection.status.view.connection.details" = "Connection Details"; + +/* Generic connection failed error message shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.message" = "Please try again later."; + +/* Generic connection failed error title shown in NetworkProtection's status view. */ +"network.protection.status.view.error.connection.failed.title" = "Failed to Connect."; + +/* IP Address label shown in NetworkProtection's status view. */ +"network.protection.status.view.ip.address" = "IP Address"; + +/* Location label shown in NetworkProtection's status view. */ +"network.protection.status.view.location" = "Location"; + +/* Title label text for the status view when netP is disconnected */ +"network.protection.status.view.title" = "Network Protection"; + /* Do not translate - stringsdict entry */ "number.of.tabs" = "number.of.tabs"; +/* No comment provided by engineer. */ +"Ok" = "Tamam"; + /* Text displayed on notification appearing in the address bar when the browser dismissed the cookie popup automatically rejecting it */ "omnibar.notification.cookies-managed" = "Yönetilen Çerezler"; @@ -1147,6 +1525,18 @@ /* ’Link’ is link on a website */ "open.externally.failed" = "Bu bağlantıyı açmak için gereken uygulama bulunamıyor"; +/* No comment provided by engineer. */ +"Please enter which app on your device is broken." = "Lütfen cihazınızda hangi uygulamanın bozuk olduğunu girin."; + +/* Activate button */ +"pm.activate" = "Reactivate"; + +/* Cancel button */ +"pm.cancel" = "Cancel"; + +/* Deactivate button */ +"pm.deactivate" = "Deactivate"; + /* No comment provided by engineer. */ "preserveLogins.domain.list.footer" = "Web siteleri oturumunuzu açık tutmak için çerezler kullanır. Bir siteyi Korumalı hâle getirdiğinizde çerezler silinmez ve Yangın Düğmesi'ni kullandıktan sonra bile oturumunuz açık kalır. Korumalı web sitelerinde bulunan üçüncü taraf izleyicileri yine engelleriz."; @@ -1216,8 +1606,8 @@ /* No comment provided by engineer. */ "section.title.favorites" = "Favoriler"; -/* No comment provided by engineer. */ -"settings.about.text" = "DuckDuckGo'da, internet için yeni güven standardını belirliyoruz.\n\nDuckDuckGo Gizlilik Tarayıcısı izleyici engelleme, akıllı şifreleme ve DuckDuckGo gizli arama da dâhil olmak üzere web'de arama ve göz atma sırasında kendinizi korumak için ihtiyacınız olan tüm gizlilik özelliklerini sağlar.\n\nNe de olsa, internet sizi korkutmamalı ve internette hakkınız olan gizliliği elde etmek panjurları kapatmak kadar basit olmalı."; +/* about page */ +"settings.about.text" = "DuckDuckGo, çevrim içi takip edilmeyi hiç istemeyen ve pratik bir çözüm arayan herkese hizmet veren, 2008 yılında kurulmuş bağımsız bir İnternet gizlilik şirketidir. Biz, çevrim içi ortamda ödün vermeden gerçek gizlilik koruması elde edebileceğinizin kanıtıyız.\n\nDuckDuckGo tarayıcı; yer imleri, sekmeler, parolalar ve daha fazlası gibi en çok kullandığınız tarayıcıdan beklediğiniz ama çoğu popüler tarayıcıda varsayılan olarak bulunmayan özelliklerin yanı sıra [sayısı ondan fazla güçlü gizlilik koruması](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/privacy/web-tracking-protections/) ile birlikte gelir. Son derece kapsamlı olan bu gizlilik koruma seti; aramadan gezinmeye, e-posta göndermeye ve daha fazlasına kadar çevrim içi etkinliklerinizde koruma sağlamaya yardımcı olur.\n\nGizlilik korumalarımız, teknik ayrıntılar hakkında hiçbir şey bilmenize veya karmaşık ayarlarla uğraşmanıza gerek kalmadan çalışır. Tüm cihazlarınızda tarayıcı olarak DuckDuckGo'yu kullanarak aradığınız gizliliği sağlayabilirsiniz.\n\nAncak ayrıntılara bir göz atmak *istiyorsanız*, DuckDuckGo gizlilik korumalarının nasıl çalıştığı hakkında daha fazla bilgiyi [yardım sayfalarımızda](ddgQuickLink://duckduckgo.com/duckduckgo-help-pages/) bulabilirsiniz."; /* Report a Broken Site screen confirmation button */ "siteFeedback.buttonText" = "Rapor Gönder"; @@ -1237,6 +1627,9 @@ /* No comment provided by engineer. */ "siteFeedback.urlPlaceholder" = "Hangi web sitesi hatalı?"; +/* No comment provided by engineer. */ +"sync.remove-device.message" = "\"%@\" artık senkronize edilmiş verilerinize erişemeyecektir."; + /* Accessibility label on remove button */ "tab.close.home" = "Ana sekmeyi kapat"; @@ -1309,6 +1702,57 @@ /* Voice-search footer note with on-device privacy warning */ "voiceSearch.footer.note" = "Ses cihazda işlenir. Saklanmaz ve DuckDuckGo da dâhil olmak üzere kimseyle paylaşılmaz."; +/* Title for the button to enable push notifications in system settings */ +"waitlist.allow-notifications" = "Bildirimlere İzin Ver"; + +/* Body text for the waitlist notification */ +"waitlist.available.notification.body" = "Davetiyenizi açın"; + +/* Title for the copy action */ +"waitlist.copy" = "Kopyala"; + +/* Label text for the invite code */ +"waitlist.invite-code" = "Davet Kodu"; + +/* Step title on the invite screen */ +"waitlist.invite-screen.step.title" = "Adım %d"; + +/* Title for the invite code screen */ +"waitlist.invite-screen.youre-invited" = "Davetlisiniz!"; + +/* Title for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.join" = "Özel Bekleme Listesine Katılın"; + +/* Temporary status text for the Join Waitlist screen */ +"waitlist.join-waitlist-screen.joining" = "Bekleme listesine katılıyorsunuz..."; + +/* Title for the Share Link button */ +"waitlist.join-waitlist-screen.share-link" = "Bağlantıyı paylaş"; + +/* Notification text for the waitlist */ +"waitlist.joined.no-notification.get-notification" = "BANA BİLDİR"; + +/* Title for the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.get-notification-confirmation-title" = "Sıra size geldiğinde bildirim gelsin mi?"; + +/* Cancel button in the alert to confirm enabling notifications */ +"waitlist.joined.no-notification.no-thanks" = "Hayır Teşekkürler"; + +/* Text used for the Notifications Disabled state */ +"waitlist.notification.disabled" = "Sıra size geldiğinde bunu bildirebiliriz, ancak bildirimler şu anda DuckDuckGo için devre dışı bırakılmış."; + +/* Privacy disclaimer for the Waitlist feature */ +"waitlist.privacy-disclaimer" = "Bekleme listesine katılmak için kişisel bilgilerinizi paylaşmanız gerekmez. Yerinizi yalnızca cihazınıza yerleştirilen bir zaman damgası ile güvence altına alacaksınız. Böylece sıranız geldiğinde sizi bilgilendirebiliriz."; + +/* Title for the queue screen */ +"waitlist.queue-screen.on-the-list" = "Listedesiniz!"; + +/* Title for the settings subtitle */ +"waitlist.settings.download-available" = "İndirme mevcut"; + +/* Title for the share sheet entry */ +"waitlist.share-sheet.title" = "Davetlisiniz!"; + /* Confirmation message */ "web.url.remove.favorite.done" = "Favori kaldırıldı"; @@ -1333,3 +1777,51 @@ /* Alert title explaining the message is shown by a website */ "webJSAlert.website-message.format" = "%@ size bir mesaj gönderdi:"; +/* No comment provided by engineer. */ +"Welcome to the Duck Side!" = "Duck Side'a hoş geldiniz!"; + +/* Title for the Windows waitlist notification */ +"windows-waitlist.available.notification.title" = "Windows için DuckDuckGo'yu deneyin!"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-1.description" = "İndirmek için Windows cihazınızda bu URL'yi ziyaret edin:"; + +/* Description on the invite screen */ +"windows-waitlist.invite-screen.step-2.description" = "İndirilenler'de DuckDuckGo Yükleyici'yi açın, Yükle'yi seçin ve ardından davet kodunuzu girin."; + +/* Subtitle for the Windows Waitlist Invite screen */ +"windows-waitlist.invite-screen.subtitle" = "Windows'ta DuckDuckGo'yu kullanmaya hazır mısınız?"; + +/* Title for the Windows waitlist button redirecting to Mac waitlist */ +"windows-waitlist.join-waitlist-screen.mac-waitlist" = "Mac Sürümünü mü arıyorsunuz?"; + +/* Title for the Join Windows Waitlist screen */ +"windows-waitlist.join-waitlist-screen.try-duckduckgo-for-windows" = "Windows için DuckDuckGo'yu denemek için erken erişim elde edin!"; + +/* Message for the alert to confirm enabling notifications */ +"windows-waitlist.joined.no-notification.get-notification-confirmation-message" = "Windows için DuckDuckGo kopyanız indirilmeye hazır olduğunda size bir bildirim göndereceğiz. "; + +/* Label text for the Joined Waitlist state with notifications declined */ +"windows-waitlist.joined.notifications-declined" = "Windows için DuckDuckGo'yu deneme davetiniz buraya gelecek. Kısa süre içinde tekrar kontrol edin ya da sıranız geldiğinde size bir bildirim gönderebiliriz."; + +/* Label text for the Joined Waitlist state with notifications enabled */ +"windows-waitlist.joined.notifications-enabled" = "Windows için DuckDuckGo kopyanız indirilmeye hazır olduğunda size bir bildirim göndereceğiz."; + +/* Title for the settings subtitle */ +"windows-waitlist.settings.browse-privately" = "Windows uygulamamızla gizliliğinizi koruyarak gezinin"; + +/* Message used when sharing to iMessage. Parameter is an eight digit invite code. */ +"windows-waitlist.share-sheet.invite-code-message" = "Davetlisiniz!\n\nWindows'ta DuckDuckGo'yu kullanmaya hazır mısınız?\n\n1. Adım\nİndirmek için Windows cihazınızda şu URL'yi ziyaret edin:\nhttps://duckduckgo.com/windows\n\n2. Adım\nİndirilenler'de DuckDuckGo Installer'ı açın, Yükle'yi seçin ve davet kodunuzu girin.\n\nDavet kodu: %@"; + +/* Message used when sharing to iMessage */ +"windows-waitlist.share-sheet.message" = "Windows'ta gizli olarak gezinmeye başlamaya hazır mısınız?\n\nİndirmek için Bilgisayarınızda şu URL'yi ziyaret edin:\nhttps://duckduckgo.com/windows"; + +/* Summary text for the Windows browser waitlist */ +"windows-waitlist.summary" = "Windows için DuckDuckGo, daha fazla gizlilikle gezinmek için ihtiyacınız olan her şeye sahiptir: gizli arama, izleyici engelleme, zorunlu şifreleme ve çerez açılır penceresi engelleme. Ayrıca, sınıfının en iyisi daha fazla koruma da yolda."; + +/* Title for the Windows Waitlist feature */ +"windows-waitlist.title" = "Windows için DuckDuckGo Uygulaması"; + +/* Title for the Windows browser download link page */ +"windows-waitlist.waitlist-download-screen.try-duckduckgo-for-windows" = "Windows için DuckDuckGo'yu edinin!"; + diff --git a/DuckDuckGo/tr.lproj/Settings.strings b/DuckDuckGo/tr.lproj/Settings.strings index 02483ab66d..f15463e3e5 100644 --- a/DuckDuckGo/tr.lproj/Settings.strings +++ b/DuckDuckGo/tr.lproj/Settings.strings @@ -37,6 +37,9 @@ /* Class = "UILabel"; text = "Text Size"; ObjectID = "9Ko-0g-T3h"; */ "9Ko-0g-T3h.text" = "Metin Boyutu"; +/* Class = "UILabel"; text = "Title"; ObjectID = "9kt-6R-XiZ"; */ +"9kt-6R-XiZ.text" = "Title"; + /* Class = "UILabel"; text = "App Launch"; ObjectID = "13n-KI-KLq"; */ "13n-KI-KLq.text" = "Uygulamayı Başlatma"; @@ -73,6 +76,12 @@ /* Class = "UILabel"; text = "Global Privacy Control (GPC)"; ObjectID = "cW7-OM-2oW"; */ "cW7-OM-2oW.text" = "Küresel Gizlilik Kontrolü (GPC)"; +/* Class = "UIBarButtonItem"; title = "Add"; ObjectID = "CxT-QK-iVn"; */ +"CxT-QK-iVn.title" = "Ekle"; + +/* Class = "UILabel"; text = "Hide your location and conceal your online activity"; ObjectID = "Cyw-ir-cSK"; */ +"Cyw-ir-cSK.text" = "Hide your location and conceal your online activity"; + /* Class = "UILabel"; text = "App Exit, Inactive for 15 Minutes"; ObjectID = "D0R-jp-UY8"; */ "D0R-jp-UY8.text" = "Uygulamadan Çık, 15 Dakika Boyunca İnaktif"; @@ -151,6 +160,9 @@ /* Class = "UILabel"; text = "Long-Press Previews"; ObjectID = "HLr-R8-xxF"; */ "HLr-R8-xxF.text" = "Uzun Basma Önizlemeleri"; +/* Class = "UILabel"; text = "Browse privately with our app for Windows"; ObjectID = "hoT-Nu-KXP"; */ +"hoT-Nu-KXP.text" = "Windows uygulamamızla gizliliğinizi koruyarak gezinin"; + /* Class = "UILabel"; text = "Privacy Protection enabled for all sites"; ObjectID = "Hu1-5i-vjL"; */ "Hu1-5i-vjL.text" = "Gizlilik Koruması tüm siteler için etkin"; @@ -163,6 +175,12 @@ /* Class = "UILabel"; text = "Share Feedback"; ObjectID = "m23-t6-9cb"; */ "m23-t6-9cb.text" = "Geri Bildirim Paylaş"; +/* Class = "UINavigationItem"; title = "Address Bar Position"; ObjectID = "mLn-1x-Fl5"; Note = "Fire button animation setting page title"; */ +"mLn-1x-Fl5.title" = "Adres Çubuğu Konumu"; + +/* Class = "UILabel"; text = "Address Bar Position"; ObjectID = "mqV-pf-NZ1"; Note = "Fire button animation settings item"; */ +"mqV-pf-NZ1.text" = "Adres Çubuğu Konumu"; + /* Class = "UILabel"; text = "Let DuckDuckGo manage cookie consent pop-ups"; ObjectID = "nX1-F1-Frd"; */ "nX1-F1-Frd.text" = "DuckDuckGo'nun çerez izni açılır pencerelerini yönetmesine izin verin"; @@ -175,15 +193,27 @@ /* Class = "UILabel"; text = "About DuckDuckGo"; ObjectID = "oM7-1o-9oY"; */ "oM7-1o-9oY.text" = "DuckDuckGo Hakkında"; +/* Class = "UILabel"; text = "Top"; ObjectID = "opn-JO-idF"; */ +"opn-JO-idF.text" = "Top"; + /* Class = "UITableViewSection"; headerTitle = "More From DuckDuckGo"; ObjectID = "OxE-MQ-uJk"; */ "OxE-MQ-uJk.headerTitle" = "DuckDuckGo'dan Daha Fazlası"; +/* Class = "UILabel"; text = "Sync"; ObjectID = "oXN-ez-gct"; */ +"oXN-ez-gct.text" = "Senkronizasyon"; + /* Class = "UILabel"; text = "Browse privately with our app for Mac "; ObjectID = "P0F-ts-ekd"; */ "P0F-ts-ekd.text" = "Mac için uygulamamızla internette gezinirken gizliliğinizi koruyun "; +/* Class = "UILabel"; text = "Network Protection"; ObjectID = "qah-gb-udB"; */ +"qah-gb-udB.text" = "Network Protection"; + /* Class = "UILabel"; text = "Label"; ObjectID = "qeN-SV-zy7"; */ "qeN-SV-zy7.text" = "Label"; +/* Class = "UILabel"; text = "DuckDuckGo Windows App"; ObjectID = "RQ8-H1-Ez1"; */ +"RQ8-H1-Ez1.text" = "DuckDuckGo Windows Uygulaması"; + /* Class = "UINavigationItem"; title = "Text Size"; ObjectID = "ssa-zd-L3T"; */ "ssa-zd-L3T.title" = "Metin Boyutu"; @@ -232,8 +262,8 @@ /* Class = "UILabel"; text = "Add App to Your Dock"; ObjectID = "yvj-LL-MiR"; */ "yvj-LL-MiR.text" = "Uygulamayı Dock'a Ekle"; -/* Class = "UILabel"; text = "DuckDuckGo Desktop App"; ObjectID = "Yz9-17-qnn"; */ -"Yz9-17-qnn.text" = "DuckDuckGo Masaüstü Uygulaması"; +/* Class = "UILabel"; text = "DuckDuckGo Mac App"; ObjectID = "Yz9-17-qnn"; */ +"Yz9-17-qnn.text" = "DuckDuckGo Mac Uygulaması"; /* Class = "UILabel"; text = "New Tab"; ObjectID = "Zpg-h0-rYv"; */ "Zpg-h0-rYv.text" = "Yeni Sekme"; diff --git a/DuckDuckGoTests/AppSettingsMock.swift b/DuckDuckGoTests/AppSettingsMock.swift index a31dd2a1e1..72ec6924a2 100644 --- a/DuckDuckGoTests/AppSettingsMock.swift +++ b/DuckDuckGoTests/AppSettingsMock.swift @@ -17,10 +17,20 @@ // limitations under the License. // +import Bookmarks import Foundation @testable import DuckDuckGo class AppSettingsMock: AppSettings { + + var isSyncBookmarksPaused: Bool = false + + var isSyncCredentialsPaused: Bool = false + + var currentAddressBarPosition: DuckDuckGo.AddressBarPosition = .top + + var showFullSiteAddress: Bool = false + var autofillCredentialsEnabled: Bool = false var autofillCredentialsSavePromptShowAtLeastOnce: Bool = false @@ -49,6 +59,8 @@ class AppSettingsMock: AppSettings { var textSize: Int = 14 + var favoritesDisplayMode: FavoritesDisplayMode = .displayNative(.mobile) + var autofill: Bool = false var voiceSearchEnabled: Bool = false diff --git a/DuckDuckGoTests/AppURLsTests.swift b/DuckDuckGoTests/AppURLsTests.swift index eaac31d5f8..0d2adfdbe1 100644 --- a/DuckDuckGoTests/AppURLsTests.swift +++ b/DuckDuckGoTests/AppURLsTests.swift @@ -275,7 +275,7 @@ final class AppURLsTests: XCTestCase { func testExternalDependencyURLsNotChanged() { XCTAssertEqual(URL.surrogates.absoluteString, "https://staticcdn.duckduckgo.com/surrogates.txt") - XCTAssertEqual(URL.privacyConfig.absoluteString, "https://staticcdn.duckduckgo.com/trackerblocking/config/v3/ios-config.json") + XCTAssertEqual(URL.privacyConfig.absoluteString, "https://staticcdn.duckduckgo.com/trackerblocking/config/v4/ios-config.json") XCTAssertEqual(URL.trackerDataSet.absoluteString, "https://staticcdn.duckduckgo.com/trackerblocking/v5/current/ios-tds.json") XCTAssertEqual(URL.bloomFilter.absoluteString, "https://staticcdn.duckduckgo.com/https/https-mobile-v2-bloom.bin") XCTAssertEqual(URL.bloomFilterSpec.absoluteString, "https://staticcdn.duckduckgo.com/https/https-mobile-v2-bloom-spec.json") diff --git a/DuckDuckGoTests/AutofillLoginListViewModelTests.swift b/DuckDuckGoTests/AutofillLoginListViewModelTests.swift index cd6ff8c211..bf6d6eace3 100644 --- a/DuckDuckGoTests/AutofillLoginListViewModelTests.swift +++ b/DuckDuckGoTests/AutofillLoginListViewModelTests.swift @@ -31,6 +31,18 @@ class AutofillLoginListViewModelTests: XCTestCase { private let tld = TLD() private let appSettings = AppUserDefaults() private let vault = (try? MockSecureVaultFactory.makeVault(errorReporter: nil))! + private var manager: AutofillNeverPromptWebsitesManager! + + override func setUpWithError() throws { + try super.setUpWithError() + manager = AutofillNeverPromptWebsitesManager(secureVault: vault) + } + + override func tearDownWithError() throws { + manager = nil + + try super.tearDownWithError() + } func testWhenOneLoginDeletedWithNoSuggestionsThenAlphabeticalSectionIsDeleted() { let accountIdToDelete = "1" @@ -38,7 +50,7 @@ class AutofillLoginListViewModelTests: XCTestCase { SecureVaultModels.WebsiteAccount(id: accountIdToDelete, title: nil, username: "", domain: "testsite.com", created: Date(), lastUpdated: Date()) ] - let model = AutofillLoginListViewModel(appSettings: appSettings, tld: tld, secureVault: vault) + let model = AutofillLoginListViewModel(appSettings: appSettings, tld: tld, secureVault: vault, autofillNeverPromptWebsitesManager: manager) let tableContentsToDelete = model.tableContentsToDelete(accountId: accountIdToDelete) XCTAssertEqual(tableContentsToDelete.sectionsToDelete.count, 1) XCTAssertEqual(tableContentsToDelete.rowsToDelete.count, 0) @@ -52,7 +64,7 @@ class AutofillLoginListViewModelTests: XCTestCase { SecureVaultModels.WebsiteAccount(id: "3", title: nil, username: "", domain: "testsite3.com", created: Date(), lastUpdated: Date()) ] - let model = AutofillLoginListViewModel(appSettings: appSettings, tld: tld, secureVault: vault) + let model = AutofillLoginListViewModel(appSettings: appSettings, tld: tld, secureVault: vault, autofillNeverPromptWebsitesManager: manager) let tableContentsToDelete = model.tableContentsToDelete(accountId: accountIdToDelete) XCTAssertEqual(tableContentsToDelete.sectionsToDelete.count, 0) XCTAssertEqual(tableContentsToDelete.rowsToDelete.count, 1) @@ -100,6 +112,31 @@ class AutofillLoginListViewModelTests: XCTestCase { XCTAssertEqual(tableContentsToDelete.sectionsToDelete.count, 0) XCTAssertEqual(tableContentsToDelete.rowsToDelete.count, 2) } + + func testWhenNoNeverPromptWebsitesSavedThenNeverPromptSectionIsNotShown() { + XCTAssertTrue(manager.deleteAllNeverPromptWebsites()) + let model = AutofillLoginListViewModel(appSettings: appSettings, tld: tld, secureVault: vault, autofillNeverPromptWebsitesManager: manager) + XCTAssertEqual(model.rowsInSection(0), 1) + } + + func testWhenOneNeverPromptWebsiteSavedThenNeverPromptSectionIsShown() { + XCTAssertTrue(manager.deleteAllNeverPromptWebsites()) + XCTAssertNoThrow(try manager.saveNeverPromptWebsite("example.com")) + let model = AutofillLoginListViewModel(appSettings: appSettings, tld: tld, secureVault: vault, autofillNeverPromptWebsitesManager: manager) + XCTAssertEqual(model.rowsInSection(0), 2) + } + + func testWhenManyNeverPromptWebsiteSavedThenNeverPromptSectionIsShown() { + XCTAssertTrue(manager.deleteAllNeverPromptWebsites()) + XCTAssertNoThrow(try manager.saveNeverPromptWebsite("example.com")) + XCTAssertNoThrow(try manager.saveNeverPromptWebsite("example.co.uk")) + XCTAssertNoThrow(try manager.saveNeverPromptWebsite("duckduckgo.com")) + XCTAssertNoThrow(try manager.saveNeverPromptWebsite("daxisawesome.com")) + XCTAssertNoThrow(try manager.saveNeverPromptWebsite("123domain.com")) + + let model = AutofillLoginListViewModel(appSettings: appSettings, tld: tld, secureVault: vault, autofillNeverPromptWebsitesManager: manager) + XCTAssertEqual(model.rowsInSection(0), 2) + } } class AutofillLoginListSectionTypeTests: XCTestCase { diff --git a/DuckDuckGoTests/AutofillNeverPromptWebsitesManagerTests.swift b/DuckDuckGoTests/AutofillNeverPromptWebsitesManagerTests.swift new file mode 100644 index 0000000000..cea49d96c2 --- /dev/null +++ b/DuckDuckGoTests/AutofillNeverPromptWebsitesManagerTests.swift @@ -0,0 +1,85 @@ +// +// AutofillNeverPromptWebsitesManagerTests.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import XCTest + +@testable import DuckDuckGo +import BrowserServicesKit + +final class AutofillNeverPromptWebsitesManagerTests: XCTestCase { + + private var manager: AutofillNeverPromptWebsitesManager! + private let vault = (try? MockSecureVaultFactory.makeVault(errorReporter: nil))! + + override func setUpWithError() throws { + try super.setUpWithError() + manager = AutofillNeverPromptWebsitesManager(secureVault: vault) + } + + override func tearDownWithError() throws { + manager = nil + + try super.tearDownWithError() + } + + func testWhenSavingNeverPromptWebsiteThenHasNeverPromptWebsiteForDomain() throws { + let domain = "example.com" + _ = try manager.saveNeverPromptWebsite(domain) + + let result = manager.hasNeverPromptWebsitesFor(domain: domain) + + XCTAssertTrue(result) + } + + func testWhenMultipleNeverPromptWebsitesThenHasNeverPromptWebsiteForDomain() { + XCTAssertTrue(manager.deleteAllNeverPromptWebsites()) + + XCTAssertNoThrow(try manager.saveNeverPromptWebsite("example.com")) + XCTAssertNoThrow(try manager.saveNeverPromptWebsite("sub.example.com")) + XCTAssertNoThrow(try manager.saveNeverPromptWebsite("anotherdomain.com")) + + XCTAssertEqual(manager.neverPromptWebsites.count, 3) + XCTAssertTrue(manager.hasNeverPromptWebsitesFor(domain: "example.com")) + } + + func testWhenDeletingAllNeverPromptWebsitesTheAllNeverPromptWebsitesDeleted() { + XCTAssertTrue(manager.deleteAllNeverPromptWebsites()) + + let domain = "example.com" + XCTAssertNoThrow(try manager.saveNeverPromptWebsite(domain)) + XCTAssertEqual(manager.neverPromptWebsites.count, 1) + + XCTAssertTrue(manager.deleteAllNeverPromptWebsites()) + XCTAssertEqual(manager.neverPromptWebsites.count, 0) + } + + func testWhenNoNeverPromptWebsitesForDomainThenNoHasNeverPromptWebsitesForDomain() { + let domain = "example.com" + XCTAssertTrue(manager.deleteAllNeverPromptWebsites()) + XCTAssertFalse(manager.hasNeverPromptWebsitesFor(domain: domain)) + } + + func testWhenSaveNeverPromptWebsiteThatAlreadyExistsThenHasNeverPromptWebsiteForDomain() { + let domain = "example.com" + XCTAssertNoThrow(try manager.saveNeverPromptWebsite(domain)) + XCTAssertNoThrow(try manager.saveNeverPromptWebsite(domain)) + XCTAssertTrue(manager.hasNeverPromptWebsitesFor(domain: "example.com")) + } + +} diff --git a/DuckDuckGoTests/BarsAnimatorTests.swift b/DuckDuckGoTests/BarsAnimatorTests.swift index 40efd123f9..07b5c065f4 100644 --- a/DuckDuckGoTests/BarsAnimatorTests.swift +++ b/DuckDuckGoTests/BarsAnimatorTests.swift @@ -179,7 +179,7 @@ private class BrowserChromeDelegateMock: BrowserChromeDelegate { var barsMaxHeight: CGFloat = 30 - var omniBar: OmniBar! = OmniBar(frame: CGRect(x: 0, y: 0, width: 300, height: 30)) + var omniBar: OmniBar = OmniBar(frame: CGRect(x: 0, y: 0, width: 300, height: 30)) var tabBarContainer: UIView = UIView() } diff --git a/DuckDuckGoTests/BookmarkEditorViewModelTests.swift b/DuckDuckGoTests/BookmarkEditorViewModelTests.swift index a293c8fc3a..6e4dbb5e7e 100644 --- a/DuckDuckGoTests/BookmarkEditorViewModelTests.swift +++ b/DuckDuckGoTests/BookmarkEditorViewModelTests.swift @@ -55,6 +55,7 @@ class BookmarkEditorViewModelTests: XCTestCase { func testWhenCreatingFolderWithoutParentThenModelCanSave() { let model = BookmarkEditorViewModel(creatingFolderWithParentID: nil, bookmarksDatabase: db, + favoritesDisplayMode: .displayNative(.mobile), errorEvents: errorValidatingHandler()) XCTAssertFalse(model.canAddNewFolder) @@ -72,6 +73,7 @@ class BookmarkEditorViewModelTests: XCTestCase { XCTAssertNotNil(root) let model = BookmarkEditorViewModel(creatingFolderWithParentID: root?.objectID, bookmarksDatabase: db, + favoritesDisplayMode: .displayNative(.mobile), errorEvents: errorValidatingHandler()) XCTAssertFalse(model.canAddNewFolder) @@ -95,6 +97,7 @@ class BookmarkEditorViewModelTests: XCTestCase { let model = BookmarkEditorViewModel(editingEntityID: firstBookmark.objectID, bookmarksDatabase: db, + favoritesDisplayMode: .displayNative(.mobile), errorEvents: errorValidatingHandler()) XCTAssertFalse(model.isNew) @@ -115,6 +118,7 @@ class BookmarkEditorViewModelTests: XCTestCase { let model = BookmarkEditorViewModel(editingEntityID: firstBookmark.objectID, bookmarksDatabase: db, + favoritesDisplayMode: .displayNative(.mobile), errorEvents: errorValidatingHandler()) XCTAssertFalse(model.isNew) @@ -136,15 +140,16 @@ class BookmarkEditorViewModelTests: XCTestCase { let model = BookmarkEditorViewModel(editingEntityID: firstBookmark.objectID, bookmarksDatabase: db, + favoritesDisplayMode: .displayNative(.mobile), errorEvents: errorValidatingHandler()) let folders = model.locations let fetchFolders = BookmarkEntity.fetchRequest() - fetchFolders.predicate = NSPredicate(format: "%K == true AND %K != %@ AND %K == false", + fetchFolders.predicate = NSPredicate(format: "%K == true AND NOT %K IN %@ AND %K == false", #keyPath(BookmarkEntity.isFolder), #keyPath(BookmarkEntity.uuid), - BookmarkEntity.Constants.favoritesFolderID, + FavoritesFolderID.allCases.map(\.rawValue), #keyPath(BookmarkEntity.isPendingDeletion)) let allFolders = (try? context.fetch(fetchFolders)) ?? [] @@ -179,11 +184,12 @@ class BookmarkEditorViewModelTests: XCTestCase { let model = BookmarkEditorViewModel(editingEntityID: firstBookmark.objectID, bookmarksDatabase: db, + favoritesDisplayMode: .displayNative(.mobile), errorEvents: errorValidatingHandler()) - XCTAssert(model.bookmark.isFavorite) + XCTAssert(model.bookmark.isFavorite(on: .mobile)) model.removeFromFavorites() - XCTAssertFalse(model.bookmark.isFavorite) + XCTAssertFalse(model.bookmark.isFavorite(on: .mobile)) XCTAssert(model.canSave) model.save() } @@ -194,6 +200,7 @@ class BookmarkEditorViewModelTests: XCTestCase { let errorReported = expectation(description: "Error reported") let model = BookmarkEditorViewModel(creatingFolderWithParentID: nil, bookmarksDatabase: db, + favoritesDisplayMode: .displayNative(.mobile), errorEvents: .init(mapping: { event, _, _, _ in XCTAssertEqual(event, .saveFailed(.edit)) errorReported.fulfill() diff --git a/DuckDuckGoTests/BookmarkEntityTests.swift b/DuckDuckGoTests/BookmarkEntityTests.swift index 3a9cf3aad9..6b7ba6989b 100644 --- a/DuckDuckGoTests/BookmarkEntityTests.swift +++ b/DuckDuckGoTests/BookmarkEntityTests.swift @@ -28,7 +28,7 @@ class BookmarkEntityTests: XCTestCase { var db: CoreDataDatabase! var context: NSManagedObjectContext! var root: BookmarkEntity! - var favorites: BookmarkEntity! + var favoritesFolders: [BookmarkEntity] = [] override func setUpWithError() throws { try super.setUpWithError() @@ -45,15 +45,15 @@ class BookmarkEntityTests: XCTestCase { root = BookmarkUtils.fetchRootFolder(context) XCTAssertNotNil(root) - favorites = BookmarkUtils.fetchFavoritesFolder(context) - XCTAssertNotNil(favorites) + favoritesFolders = BookmarkUtils.fetchFavoritesFolders(for: .displayNative(.mobile), in: context) + XCTAssertNotNil(favoritesFolders) } override func tearDownWithError() throws { try super.tearDownWithError() root = nil - favorites = nil + favoritesFolders = [] context = nil try db.tearDown(deleteStores: true) @@ -73,7 +73,7 @@ class BookmarkEntityTests: XCTestCase { url: "u", parent: root, context: context) - favorite.addToFavorites(favoritesRoot: favorites) + favorite.addToFavorites(folders: favoritesFolders) try context.save() } diff --git a/DuckDuckGoTests/BookmarkListViewModelTests.swift b/DuckDuckGoTests/BookmarkListViewModelTests.swift index a2ff0e6108..195ea53bc0 100644 --- a/DuckDuckGoTests/BookmarkListViewModelTests.swift +++ b/DuckDuckGoTests/BookmarkListViewModelTests.swift @@ -27,9 +27,11 @@ import DuckDuckGo private extension BookmarkListViewModel { convenience init(bookmarksDatabase: CoreDataDatabase, - parentID: NSManagedObjectID?) { + parentID: NSManagedObjectID?, + favoritesDisplayMode: FavoritesDisplayMode) { self.init(bookmarksDatabase: bookmarksDatabase, parentID: parentID, + favoritesDisplayMode: favoritesDisplayMode, errorEvents: .init(mapping: { event, _, _, _ in XCTFail("Unexpected error: \(event)") })) @@ -65,7 +67,8 @@ class BookmarkListViewModelTests: XCTestCase { func testWhenFolderIsSetThenBookmarksFetchedFromThatLocation() { let viewModel = BookmarkListViewModel(bookmarksDatabase: db, - parentID: nil) + parentID: nil, + favoritesDisplayMode: .displayNative(.mobile)) let context = db.makeContext(concurrencyType: .mainQueueConcurrencyType) XCTAssertNotNil(viewModel.currentFolder?.objectID) XCTAssertEqual(viewModel.currentFolder?.objectID, BookmarkUtils.fetchRootFolder(context)?.objectID) @@ -77,7 +80,8 @@ class BookmarkListViewModelTests: XCTestCase { XCTAssertEqual(names, BasicBookmarksStructure.topLevelTitles) let nestedViewModel = BookmarkListViewModel(bookmarksDatabase: db, - parentID: result[1].objectID) + parentID: result[1].objectID, + favoritesDisplayMode: .displayNative(.mobile)) XCTAssertEqual(nestedViewModel.currentFolder?.objectID, result[1].objectID) let result2 = nestedViewModel.bookmarks @@ -89,7 +93,8 @@ class BookmarkListViewModelTests: XCTestCase { func testWhenDeletingABookmarkItIsRemoved() { let viewModel = BookmarkListViewModel(bookmarksDatabase: db, - parentID: nil) + parentID: nil, + favoritesDisplayMode: .displayNative(.mobile)) let result = viewModel.bookmarks let idSet = Set(result.map { $0.objectID }) @@ -99,7 +104,8 @@ class BookmarkListViewModelTests: XCTestCase { viewModel.softDeleteBookmark(bookmark) let newViewModel = BookmarkListViewModel(bookmarksDatabase: db, - parentID: nil) + parentID: nil, + favoritesDisplayMode: .displayNative(.mobile)) let newResult = newViewModel.bookmarks let newIdSet = Set(newResult.map { $0.objectID }) @@ -112,7 +118,8 @@ class BookmarkListViewModelTests: XCTestCase { func testWhenDeletingABookmarkFolderItIsRemovedWithContents() { let viewModel = BookmarkListViewModel(bookmarksDatabase: db, - parentID: nil) + parentID: nil, + favoritesDisplayMode: .displayNative(.mobile)) let result = viewModel.bookmarks let idSet = Set(result.map { $0.objectID }) @@ -125,7 +132,8 @@ class BookmarkListViewModelTests: XCTestCase { viewModel.softDeleteBookmark(folder) let newViewModel = BookmarkListViewModel(bookmarksDatabase: db, - parentID: nil) + parentID: nil, + favoritesDisplayMode: .displayNative(.mobile)) let newResult = newViewModel.bookmarks let newIdSet = Set(newResult.map { $0.objectID }) @@ -139,15 +147,17 @@ class BookmarkListViewModelTests: XCTestCase { func testWhenGettingTotalCountThenFoldersAreNotTakenIntoAccount() { let viewModel = BookmarkListViewModel(bookmarksDatabase: db, - parentID: nil) - + parentID: nil, + favoritesDisplayMode: .displayNative(.mobile)) + XCTAssertEqual(viewModel.totalBookmarksCount, 5) } func testWhenMovingBookmarkItGoesToNewPosition() { let viewModel = BookmarkListViewModel(bookmarksDatabase: db, - parentID: nil) + parentID: nil, + favoritesDisplayMode: .displayNative(.mobile)) let result = viewModel.bookmarks let first = result[0] @@ -158,7 +168,8 @@ class BookmarkListViewModelTests: XCTestCase { toIndex: 1) let newViewModel = BookmarkListViewModel(bookmarksDatabase: db, - parentID: nil) + parentID: nil, + favoritesDisplayMode: .displayNative(.mobile)) let newResult = newViewModel.bookmarks let newFirst = newResult[0] let newSecond = newResult[1] @@ -171,9 +182,11 @@ class BookmarkListViewModelTests: XCTestCase { func testWhenContextSavesThenChangesArePropagated() { let viewModel = BookmarkListViewModel(bookmarksDatabase: db, - parentID: nil) + parentID: nil, + favoritesDisplayMode: .displayNative(.mobile)) let listeningViewModel = BookmarkListViewModel(bookmarksDatabase: db, - parentID: nil) + parentID: nil, + favoritesDisplayMode: .displayNative(.mobile)) let expectation = expectation(description: "Changes propagated") @@ -203,6 +216,7 @@ class BookmarkListViewModelTests: XCTestCase { expectation.expectedFulfillmentCount = 3 let viewModel = BookmarkListViewModel(bookmarksDatabase: db, parentID: nil, + favoritesDisplayMode: .displayNative(.mobile), errorEvents: .init(mapping: { event, _, _, _ in let expectedEvent = expectedEvents.popLast() XCTAssertEqual(event, expectedEvent) @@ -230,7 +244,8 @@ class BookmarkListViewModelTests: XCTestCase { toIndex: 10) let newViewModel = BookmarkListViewModel(bookmarksDatabase: db, - parentID: nil) + parentID: nil, + favoritesDisplayMode: .displayNative(.mobile)) let newResult = newViewModel.bookmarks let newFirst = newResult[0] let newSecond = newResult[1] @@ -245,7 +260,8 @@ class BookmarkListViewModelTests: XCTestCase { func testWhenFolderIsNotAFolderThenErrorIsReported() { let viewModel = BookmarkListViewModel(bookmarksDatabase: db, - parentID: nil) + parentID: nil, + favoritesDisplayMode: .displayNative(.mobile)) let result = viewModel.bookmarks let bookmark = result[0] @@ -254,6 +270,7 @@ class BookmarkListViewModelTests: XCTestCase { let errorReported = expectation(description: "Error reported") let brokenViewModel = BookmarkListViewModel(bookmarksDatabase: db, parentID: bookmark.objectID, + favoritesDisplayMode: .displayNative(.mobile), errorEvents: .init(mapping: { event, _, _, _ in errorReported.fulfill() XCTAssertEqual(event, .bookmarkFolderExpected) @@ -267,7 +284,8 @@ class BookmarkListViewModelTests: XCTestCase { func testWhenFolderIsMissingThenErrorIsReported() { let viewModel = BookmarkListViewModel(bookmarksDatabase: db, - parentID: nil) + parentID: nil, + favoritesDisplayMode: .displayNative(.mobile)) let result = viewModel.bookmarks let bookmark = result[0] @@ -282,6 +300,7 @@ class BookmarkListViewModelTests: XCTestCase { let errorReported = expectation(description: "Error reported") let brokenViewModel = BookmarkListViewModel(bookmarksDatabase: db, parentID: tmpFolder.objectID, + favoritesDisplayMode: .displayNative(.mobile), errorEvents: .init(mapping: { event, _, _, _ in errorReported.fulfill() XCTAssertEqual(event, .bookmarksListMissingFolder) @@ -300,6 +319,7 @@ class BookmarkListViewModelTests: XCTestCase { let expectation = expectation(description: "Error reported") _ = BookmarkListViewModel(bookmarksDatabase: db, parentID: nil, + favoritesDisplayMode: .displayNative(.mobile), errorEvents: .init(mapping: { event, _, _, _ in XCTAssertEqual(event, .fetchingRootItemFailed(.bookmarks)) expectation.fulfill() diff --git a/DuckDuckGoTests/BookmarkUtilsTests.swift b/DuckDuckGoTests/BookmarkUtilsTests.swift index 1c7314e273..5930ce4a46 100644 --- a/DuckDuckGoTests/BookmarkUtilsTests.swift +++ b/DuckDuckGoTests/BookmarkUtilsTests.swift @@ -54,12 +54,12 @@ class BookmarkUtilsTests: XCTestCase { BookmarkUtils.prepareFoldersStructure(in: context) count = try context.count(for: countRequest) - XCTAssertEqual(count, 2) + XCTAssertEqual(count, 4) BookmarkUtils.prepareFoldersStructure(in: context) count = try context.count(for: countRequest) - XCTAssertEqual(count, 2) + XCTAssertEqual(count, 4) } } diff --git a/DuckDuckGoTests/BookmarksExporterTests.swift b/DuckDuckGoTests/BookmarksExporterTests.swift index 4606ecc4a4..5d68848704 100644 --- a/DuckDuckGoTests/BookmarksExporterTests.swift +++ b/DuckDuckGoTests/BookmarksExporterTests.swift @@ -33,8 +33,8 @@ class BookmarksExporterTests: XCTestCase { htmlLoader = HtmlTestDataLoader() - importer = BookmarksImporter(coreDataStore: storage) - exporter = BookmarksExporter(coreDataStore: storage) + importer = BookmarksImporter(coreDataStore: storage, favoritesDisplayMode: .displayNative(.mobile)) + exporter = BookmarksExporter(coreDataStore: storage, favoritesDisplayMode: .displayNative(.mobile)) } override func tearDownWithError() throws { diff --git a/DuckDuckGoTests/BookmarksImporterTests.swift b/DuckDuckGoTests/BookmarksImporterTests.swift index 001a488c3d..e8a4dc44ee 100644 --- a/DuckDuckGoTests/BookmarksImporterTests.swift +++ b/DuckDuckGoTests/BookmarksImporterTests.swift @@ -33,7 +33,7 @@ class BookmarksImporterTests: XCTestCase { try super.setUpWithError() htmlLoader = HtmlTestDataLoader() - importer = BookmarksImporter(coreDataStore: storage) + importer = BookmarksImporter(coreDataStore: storage, favoritesDisplayMode: .displayNative(.mobile)) } override func tearDownWithError() throws { diff --git a/DuckDuckGoTests/BookmarksMigrationTests.swift b/DuckDuckGoTests/BookmarksMigrationTests.swift index fd6a1a6e44..7ffeddde97 100644 --- a/DuckDuckGoTests/BookmarksMigrationTests.swift +++ b/DuckDuckGoTests/BookmarksMigrationTests.swift @@ -100,7 +100,7 @@ class BookmarksMigrationTests: XCTestCase { LegacyBookmarksStoreMigration.migrate(from: nil, to: context) XCTAssertNotNil(BookmarkUtils.fetchRootFolder(context)) - XCTAssertNotNil(BookmarkUtils.fetchFavoritesFolder(context)) + XCTAssertEqual(BookmarkUtils.fetchFavoritesFolders(withUUIDs: Set(FavoritesFolderID.allCases.map(\.rawValue)), in: context).count, 1) // Simulate subsequent app instantiations LegacyBookmarksStoreMigration.migrate(from: nil, to: context) @@ -119,9 +119,18 @@ class BookmarksMigrationTests: XCTestCase { LegacyBookmarksStoreMigration.migrate(from: sourceStack, to: context) XCTAssertNotNil(BookmarkUtils.fetchRootFolder(context)) - XCTAssertNotNil(BookmarkUtils.fetchFavoritesFolder(context)) - - let topLevel = BookmarkListViewModel(bookmarksDatabase: destinationStack, parentID: nil, syncService: nil) + XCTAssertEqual( + BookmarkUtils.fetchFavoritesFolders(withUUIDs: Set(FavoritesFolderID.allCases.map(\.rawValue)), in: context).count, + FavoritesFolderID.allCases.count + ) + + let topLevel = BookmarkListViewModel( + bookmarksDatabase: destinationStack, + parentID: nil, + favoritesDisplayMode: .displayNative(.mobile), + syncService: nil + ) + XCTAssertEqual(topLevel.bookmarks.count, 4) let topLevelNames = topLevel.bookmarks.map { $0.title } @@ -130,18 +139,18 @@ class BookmarksMigrationTests: XCTestCase { let favFirst = topLevel.bookmarks[0] XCTAssertEqual(favFirst.isFolder, false) - XCTAssertEqual(favFirst.isFavorite, true) + XCTAssertEqual(favFirst.isFavorite(on: .mobile), true) XCTAssertEqual(favFirst.title, "First") XCTAssertEqual(favFirst.url, url(for: "first").absoluteString) let favThird = topLevel.bookmarks[1] XCTAssertEqual(favThird.isFolder, false) - XCTAssertEqual(favThird.isFavorite, true) + XCTAssertEqual(favThird.isFavorite(on: .mobile), true) XCTAssertEqual(favThird.title, "Third") let bookOne = topLevel.bookmarks[2] XCTAssertEqual(bookOne.isFolder, false) - XCTAssertEqual(bookOne.isFavorite, false) + XCTAssertEqual(bookOne.isFavorite(on: .mobile), false) XCTAssertEqual(bookOne.title, "One") let folderA = topLevel.bookmarks[3] @@ -150,7 +159,7 @@ class BookmarksMigrationTests: XCTestCase { let folderAContents = folderA.childrenArray XCTAssertEqual(folderAContents[0].isFolder, false) - XCTAssertEqual(folderAContents[0].isFavorite, true) + XCTAssertEqual(folderAContents[0].isFavorite(on: .mobile), true) XCTAssertEqual(folderAContents[0].title, "Two") let folderB = folderAContents[1] @@ -160,7 +169,7 @@ class BookmarksMigrationTests: XCTestCase { let folderBContents = folderB.childrenArray XCTAssertEqual(folderBContents.count, 1) XCTAssertEqual(folderBContents[0].isFolder, false) - XCTAssertEqual(folderBContents[0].isFavorite, false) + XCTAssertEqual(folderBContents[0].isFavorite(on: .mobile), false) XCTAssertEqual(folderBContents[0].title, "Three") } diff --git a/DuckDuckGoTests/BookmarksTestHelpers.swift b/DuckDuckGoTests/BookmarksTestHelpers.swift index 698e6866c5..03b91bfa81 100644 --- a/DuckDuckGoTests/BookmarksTestHelpers.swift +++ b/DuckDuckGoTests/BookmarksTestHelpers.swift @@ -63,12 +63,14 @@ struct BasicBookmarksStructure { BookmarkUtils.prepareFoldersStructure(in: context) - guard let rootFolder = BookmarkUtils.fetchRootFolder(context), - let favoritesFolder = BookmarkUtils.fetchFavoritesFolder(context) else { + guard let rootFolder = BookmarkUtils.fetchRootFolder(context) + else { XCTFail("Couldn't find required folders") return } - + + let favoritesFolders = BookmarkUtils.fetchFavoritesFolders(for: .displayNative(.mobile), in: context) + let topLevel = createBookmarksList(usingNames: topLevelTitles, parent: rootFolder, in: context) let parent = topLevel[1] @@ -80,10 +82,10 @@ struct BasicBookmarksStructure { nestedLevel[0].url = nil nestedLevel[0].isFolder = true - topLevel[0].addToFavorites(favoritesRoot: favoritesFolder) - topLevel[2].addToFavorites(favoritesRoot: favoritesFolder) - nestedLevel[1].addToFavorites(favoritesRoot: favoritesFolder) - topLevel[3].addToFavorites(favoritesRoot: favoritesFolder) + topLevel[0].addToFavorites(folders: favoritesFolders) + topLevel[2].addToFavorites(folders: favoritesFolders) + nestedLevel[1].addToFavorites(folders: favoritesFolders) + topLevel[3].addToFavorites(folders: favoritesFolders) do { try context.save() diff --git a/DuckDuckGoTests/BrokenSiteReportingTests.swift b/DuckDuckGoTests/BrokenSiteReportingTests.swift index b36c95b240..6c663fda98 100644 --- a/DuckDuckGoTests/BrokenSiteReportingTests.swift +++ b/DuckDuckGoTests/BrokenSiteReportingTests.swift @@ -75,6 +75,7 @@ final class BrokenSiteReportingTests: XCTestCase { tdsETag: test.blocklistVersion, ampUrl: nil, urlParametersRemoved: false, + protectionsState: test.protectionsEnabled, model: test.model ?? "", manufacturer: test.manufacturer ?? "", systemVersion: test.os ?? "", @@ -147,6 +148,7 @@ private struct Test: Codable { let exceptPlatforms: [String] let manufacturer, model, os: String? let gpcEnabled: Bool? + let protectionsEnabled: Bool } // MARK: - ExpectReportURLParam diff --git a/DuckDuckGoTests/FavoriteListViewModelTests.swift b/DuckDuckGoTests/FavoriteListViewModelTests.swift index 4285b8780a..92b662145e 100644 --- a/DuckDuckGoTests/FavoriteListViewModelTests.swift +++ b/DuckDuckGoTests/FavoriteListViewModelTests.swift @@ -30,7 +30,8 @@ private extension FavoritesListViewModel { self.init(bookmarksDatabase: bookmarksDatabase, errorEvents: .init(mapping: { event, _, _, _ in XCTFail("Unexpected error: \(event)") - })) + }), + favoritesDisplayMode: .displayNative(.mobile)) } } @@ -135,7 +136,8 @@ class FavoriteListViewModelTests: XCTestCase { let expectedEvent = expectedEvents.popLast() XCTAssertEqual(event, expectedEvent) expectation.fulfill() - })) + }), + favoritesDisplayMode: .displayNative(.mobile)) let result = viewModel.favorites @@ -175,11 +177,13 @@ class FavoriteListViewModelTests: XCTestCase { try context.save() let expectation = expectation(description: "Error reported") + expectation.assertForOverFulfill = false _ = FavoritesListViewModel(bookmarksDatabase: db, errorEvents: .init(mapping: { event, _, _, _ in XCTAssertEqual(event, .fetchingRootItemFailed(.favorites)) expectation.fulfill() - })) + }), + favoritesDisplayMode: .displayNative(.mobile)) waitForExpectations(timeout: 1) } diff --git a/DuckDuckGoTests/MenuBookmarksViewModelTests.swift b/DuckDuckGoTests/MenuBookmarksViewModelTests.swift index 62a690304e..d2bce8cb21 100644 --- a/DuckDuckGoTests/MenuBookmarksViewModelTests.swift +++ b/DuckDuckGoTests/MenuBookmarksViewModelTests.swift @@ -59,17 +59,19 @@ class MenuBookmarksViewModelTests: XCTestCase { private func validateNewBookmark(_ bookmark: BookmarkEntity?) { guard let bookmark = bookmark else { XCTFail("Missing bookmark"); return } XCTAssertNotNil(bookmark) - XCTAssertFalse(bookmark.isFavorite) - XCTAssertNil(bookmark.favoriteFolder) + XCTAssertFalse(bookmark.isFavorite(on: .mobile)) + XCTAssertTrue(bookmark.favoriteFoldersSet.isEmpty) XCTAssertEqual(bookmark, bookmark.parent?.childrenArray.last) } private func validateNewFavorite(_ favorite: BookmarkEntity?) { guard let favorite = favorite else { XCTFail("Missing favorite"); return } - XCTAssert(favorite.isFavorite) - XCTAssertNotNil(favorite.favoriteFolder) + XCTAssert(favorite.isFavorite(on: .mobile)) + XCTAssertFalse(favorite.favoriteFoldersSet.isEmpty) XCTAssertEqual(favorite, favorite.parent?.childrenArray.last) - XCTAssertEqual(favorite, favorite.favoriteFolder?.favorites?.lastObject as? BookmarkEntity) + XCTAssertEqual(favorite, favorite.favoriteFoldersSet + .first(where: { $0.uuid == FavoritesFolderID.mobile.rawValue })? + .favorites?.lastObject as? BookmarkEntity) } func testWhenCheckingBookmarkStatusThenReturnOneIfFound() { diff --git a/DuckDuckGoTests/MockDependencyProvider.swift b/DuckDuckGoTests/MockDependencyProvider.swift index e16288eeb9..7676843d3c 100644 --- a/DuckDuckGoTests/MockDependencyProvider.swift +++ b/DuckDuckGoTests/MockDependencyProvider.swift @@ -34,6 +34,7 @@ class MockDependencyProvider: DependencyProvider { var voiceSearchHelper: VoiceSearchHelperProtocol var downloadManager: DownloadManager var autofillLoginSession: AutofillLoginSession + var autofillNeverPromptWebsitesManager: AutofillNeverPromptWebsitesManager var configurationManager: ConfigurationManager init() { @@ -48,6 +49,7 @@ class MockDependencyProvider: DependencyProvider { voiceSearchHelper = defaultProvider.voiceSearchHelper downloadManager = defaultProvider.downloadManager autofillLoginSession = defaultProvider.autofillLoginSession + autofillNeverPromptWebsitesManager = defaultProvider.autofillNeverPromptWebsitesManager configurationManager = defaultProvider.configurationManager } } diff --git a/DuckDuckGoTests/MockSecureVault.swift b/DuckDuckGoTests/MockSecureVault.swift index 9949ea064a..9dfc62644f 100644 --- a/DuckDuckGoTests/MockSecureVault.swift +++ b/DuckDuckGoTests/MockSecureVault.swift @@ -44,6 +44,7 @@ final class MockSecureVault: AutofillSecureVault { var storedAccounts: [SecureVaultModels.WebsiteAccount] = [] var storedCredentials: [Int64: SecureVaultModels.WebsiteCredentials] = [:] + var storedNeverPromptWebsites = [SecureVaultModels.NeverPromptWebsites]() var storedNotes: [SecureVaultModels.Note] = [] var storedIdentities: [SecureVaultModels.Identity] = [] var storedCards: [SecureVaultModels.CreditCard] = [] @@ -99,6 +100,29 @@ final class MockSecureVault: AutofillSecureVault { storedCredentials[accountId] = nil } + func neverPromptWebsites() throws -> [SecureVaultModels.NeverPromptWebsites] { + return storedNeverPromptWebsites + } + + func hasNeverPromptWebsitesFor(domain: String) throws -> Bool { + return !storedNeverPromptWebsites.filter { $0.domain == domain }.isEmpty + } + + func storeNeverPromptWebsites(_ neverPromptWebsite: SecureVaultModels.NeverPromptWebsites) throws -> Int64 { + if let neverPromptWebsiteId = neverPromptWebsite.id { + storedNeverPromptWebsites.append(neverPromptWebsite) + return neverPromptWebsiteId + } else { + storedNeverPromptWebsites.append(neverPromptWebsite) + return -1 + } + + } + + func deleteAllNeverPromptWebsites() throws { + storedNeverPromptWebsites = [] + } + func notes() throws -> [SecureVaultModels.Note] { return storedNotes } @@ -203,6 +227,8 @@ class MockDatabaseProvider: AutofillDatabaseProvider { var _forDomain = [String]() var _credentialsDict = [Int64: SecureVaultModels.WebsiteCredentials]() var _note: SecureVaultModels.Note? + var _neverPromptWebsites = [SecureVaultModels.NeverPromptWebsites]() + var db: GRDB.DatabaseWriter // swiftlint:enable identifier_name @@ -247,6 +273,33 @@ class MockDatabaseProvider: AutofillDatabaseProvider { return _accounts } + func neverPromptWebsites() throws -> [SecureVaultModels.NeverPromptWebsites] { + return _neverPromptWebsites + } + + func hasNeverPromptWebsitesFor(domain: String) throws -> Bool { + return false + } + + func storeNeverPromptWebsite(_ neverPromptWebsite: SecureVaultModels.NeverPromptWebsites) throws -> Int64 { + if let neverPromptWebsiteId = neverPromptWebsite.id { + _neverPromptWebsites.append(neverPromptWebsite) + return neverPromptWebsiteId + } else { + return -1 + } + } + + func deleteAllNeverPromptWebsites() throws { + _neverPromptWebsites.removeAll() + } + + func updateNeverPromptWebsite(_ neverPromptWebsite: SecureVaultModels.NeverPromptWebsites) throws { + } + + func insertNeverPromptWebsite(_ neverPromptWebsite: SecureVaultModels.NeverPromptWebsites) throws { + } + func notes() throws -> [SecureVaultModels.Note] { return _notes } diff --git a/DuckDuckGoTests/NetworkProtectionAccessControllerTests.swift b/DuckDuckGoTests/NetworkProtectionAccessControllerTests.swift new file mode 100644 index 0000000000..0e8d5cae7e --- /dev/null +++ b/DuckDuckGoTests/NetworkProtectionAccessControllerTests.swift @@ -0,0 +1,183 @@ +// +// NetworkProtectionAccessControllerTests.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#if NETWORK_PROTECTION + +import XCTest +import BrowserServicesKit +import NetworkProtection +import NetworkExtension +import NetworkProtectionTestUtils +import WaitlistMocks +@testable import DuckDuckGo + +final class NetworkProtectionAccessControllerTests: XCTestCase { + + var internalUserDeciderStore: MockInternalUserStoring! + + override func setUp() { + super.setUp() + internalUserDeciderStore = MockInternalUserStoring() + + // True by default until NetP ships, as it is not visible at all to external users. + internalUserDeciderStore.isInternalUser = true + } + + override func tearDown() { + internalUserDeciderStore = nil + super.tearDown() + } + + func testWhenFeatureFlagsAreDisabled_AndTheUserHasNotBeenDirectlyInvited_ThenNetworkProtectionIsNotAccessible() { + let controller = createMockAccessController( + isInternalUser: false, + featureActivated: false, + termsAccepted: false, + featureFlagsEnabled: false, + hasJoinedWaitlist: false, + hasBeenInvited: false + ) + + XCTAssertEqual(controller.networkProtectionAccessType(), .none) + } + + func testWhenFeatureFlagsAreDisabled_AndTheUserHasBeenDirectlyInvited_ThenNetworkProtectionIsNotAccessible() { + let controller = createMockAccessController( + featureActivated: true, + termsAccepted: false, + featureFlagsEnabled: false, + hasJoinedWaitlist: false, + hasBeenInvited: false + ) + + XCTAssertEqual(controller.networkProtectionAccessType(), .inviteCodeInvited) + } + + func testWhenFeatureFlagsAreEnabled_AndTheUserHasNotSignedUp_ThenNetworkProtectionIsAccessible() { + let controller = createMockAccessController( + featureActivated: false, + termsAccepted: false, + featureFlagsEnabled: true, + hasJoinedWaitlist: false, + hasBeenInvited: false + ) + + XCTAssertEqual(controller.networkProtectionAccessType(), .waitlistAvailable) + } + + func testWhenFeatureFlagsAreEnabled_AndTheUserHasSignedUp_ThenNetworkProtectionIsAccessible() { + let controller = createMockAccessController( + featureActivated: false, + termsAccepted: false, + featureFlagsEnabled: true, + hasJoinedWaitlist: true, + hasBeenInvited: false + ) + + XCTAssertEqual(controller.networkProtectionAccessType(), .waitlistJoined) + } + + func testWhenFeatureFlagsAreEnabled_AndTheUserHasBeenInvited_ThenNetworkProtectionIsAccessible() { + let controller = createMockAccessController( + featureActivated: true, + termsAccepted: false, + featureFlagsEnabled: true, + hasJoinedWaitlist: true, + hasBeenInvited: true + ) + + XCTAssertEqual(controller.networkProtectionAccessType(), .waitlistInvitedPendingTermsAcceptance) + } + + func testWhenFeatureFlagsAreEnabled_AndTheUserHasAcceptedTerms_ThenNetworkProtectionIsAccessible() { + let controller = createMockAccessController( + featureActivated: true, + termsAccepted: true, + featureFlagsEnabled: true, + hasJoinedWaitlist: true, + hasBeenInvited: true + ) + + XCTAssertEqual(controller.networkProtectionAccessType(), .waitlistInvited) + } + + // MARK: - Mock Creation + + private func createMockAccessController( + isInternalUser: Bool = true, + featureActivated: Bool, + termsAccepted: Bool, + featureFlagsEnabled: Bool, + hasJoinedWaitlist: Bool, + hasBeenInvited: Bool + ) -> NetworkProtectionAccessController { + internalUserDeciderStore.isInternalUser = isInternalUser + + let mockActivation = MockNetworkProtectionFeatureActivation() + mockActivation.isFeatureActivated = featureActivated + + let mockWaitlistStorage = MockWaitlistStorage() + + if hasJoinedWaitlist { + mockWaitlistStorage.store(waitlistTimestamp: 1) + mockWaitlistStorage.store(waitlistToken: "token") + + if hasBeenInvited { + mockWaitlistStorage.store(inviteCode: "INVITECODE") + } + } + + let mockTermsAndConditionsStore = MockNetworkProtectionTermsAndConditionsStore() + mockTermsAndConditionsStore.networkProtectionWaitlistTermsAndConditionsAccepted = termsAccepted + let mockFeatureFlagger = createFeatureFlagger(withSubfeatureEnabled: featureFlagsEnabled) + + return NetworkProtectionAccessController( + networkProtectionActivation: mockActivation, + networkProtectionWaitlistStorage: mockWaitlistStorage, + networkProtectionTermsAndConditionsStore: mockTermsAndConditionsStore, + featureFlagger: mockFeatureFlagger + ) + } + + private func createFeatureFlagger(withSubfeatureEnabled enabled: Bool) -> DefaultFeatureFlagger { + let mockManager = MockPrivacyConfigurationManager() + mockManager.privacyConfig = mockConfiguration(subfeatureEnabled: enabled) + + let internalUserDecider = DefaultInternalUserDecider(store: internalUserDeciderStore) + return DefaultFeatureFlagger(internalUserDecider: internalUserDecider, privacyConfig: mockManager.privacyConfig) + } + + private func mockConfiguration(subfeatureEnabled: Bool) -> PrivacyConfiguration { + let mockPrivacyConfiguration = MockPrivacyConfiguration() + mockPrivacyConfiguration.isSubfeatureKeyEnabled = { _, _ in + return subfeatureEnabled + } + + return mockPrivacyConfiguration + } + +} + +private class MockNetworkProtectionTermsAndConditionsStore: NetworkProtectionTermsAndConditionsStore { + + var networkProtectionWaitlistTermsAndConditionsAccepted: Bool = false + +} + +#endif diff --git a/DuckDuckGoTests/NetworkProtectionStatusViewModelTests.swift b/DuckDuckGoTests/NetworkProtectionStatusViewModelTests.swift index 18cb7e3ee4..4858680264 100644 --- a/DuckDuckGoTests/NetworkProtectionStatusViewModelTests.swift +++ b/DuckDuckGoTests/NetworkProtectionStatusViewModelTests.swift @@ -54,6 +54,12 @@ final class NetworkProtectionStatusViewModelTests: XCTestCase { super.tearDown() } + func testInit_prefetchesLocationList() throws { + let locationListRepo = MockNetworkProtectionLocationListRepository() + viewModel = NetworkProtectionStatusViewModel(locationListRepository: locationListRepo) + waitFor(condition: locationListRepo.didCallFetchLocationList) + } + func testStatusUpdate_connected_setsIsNetPEnabledToTrue() throws { whenStatusUpdate_connected() } diff --git a/DuckDuckGoTests/NetworkProtectionVPNLocationViewModelTests.swift b/DuckDuckGoTests/NetworkProtectionVPNLocationViewModelTests.swift new file mode 100644 index 0000000000..8bfdafd4db --- /dev/null +++ b/DuckDuckGoTests/NetworkProtectionVPNLocationViewModelTests.swift @@ -0,0 +1,639 @@ +// +// NetworkProtectionVPNLocationViewModelTests.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import XCTest +import NetworkProtection +import NetworkExtension +import NetworkProtectionTestUtils +@testable import DuckDuckGo + +// swiftlint:disable type_body_length +// swiftlint:disable file_length + +final class NetworkProtectionVPNLocationViewModelTests: XCTestCase { + private var listRepository: MockNetworkProtectionLocationListRepository! + private var tunnelSettings: TunnelSettings! + private var viewModel: NetworkProtectionVPNLocationViewModel! + + @MainActor + override func setUp() { + super.setUp() + listRepository = MockNetworkProtectionLocationListRepository() + let testDefaults = UserDefaults(suiteName: #file + Thread.current.debugDescription)! + tunnelSettings = TunnelSettings(defaults: testDefaults) + viewModel = NetworkProtectionVPNLocationViewModel(locationListRepository: listRepository, tunnelSettings: tunnelSettings) + } + + override func tearDown() { + tunnelSettings.selectedLocation = .nearest + tunnelSettings = nil + listRepository = nil + viewModel = nil + super.tearDown() + } + + // MARK: onViewAppeared + + func test_onViewAppeared_setsCorrectCountryTitle() async throws { + try await assertOnListLoadSetsCorrectCountryTitle { [weak self] in + await self?.viewModel.onViewAppeared() + } + } + + func test_onViewAppeared_setsCountryID() async throws { + try await assertOnListLoadSetsCorrectCountryID { [weak self] in + await self?.viewModel.onViewAppeared() + } + } + + func test_onViewAppeared_setsCorrectEmoji() async throws { + try await assertOnListLoadSetsCorrectEmoji { [weak self] in + await self?.viewModel.onViewAppeared() + } + } + + func test_onViewAppeared_selectedCountryFromSettings_isSelectedSetToTrue() async throws { + try await assertOnListLoad_countryIsSelected { [weak self] testCaseCountryId in + self?.tunnelSettings.selectedLocation = .location(NetworkProtectionSelectedLocation(country: testCaseCountryId)) + await self?.viewModel.onViewAppeared() + } + } + + func test_onViewAppeared_NOTSelectedCountryFromSettings_isSelectedSetToFalse() async throws { + try await assertOnListLoad_isSelectedSetToFalse { [weak self] in + self?.tunnelSettings.selectedLocation = .location(NetworkProtectionSelectedLocation(country: "US")) + await self?.viewModel.onViewAppeared() + } + } + + func test_onViewAppeared_nearestSelectedInSettings_isNearestSelectedSetToTrue() async throws { + try await assertNearestSelectedSetToTrue { [weak self] in + self?.tunnelSettings.selectedLocation = .nearest + await self?.viewModel.onViewAppeared() + } + } + + func test_onViewAppeared_nearestNOTSelectedInSettings_isNearestSelectedSetToFalse() async throws { + try await assertNearestSelectedSetToFalse { [weak self] testCaseCountryId in + self?.tunnelSettings.selectedLocation = .location(NetworkProtectionSelectedLocation(country: testCaseCountryId)) + await self?.viewModel.onViewAppeared() + } + } + + func test_onViewAppeared_countryHas1City_subtitleIsNil() async throws { + try await assertOnListLoad_countryWith1City_hasNilSubtitle { [weak self] in + await self?.viewModel.onViewAppeared() + } + } + + func test_onViewAppeared_countryHas1City_shouldShowPickerIsFalse() async throws { + try await assertOnListLoad_countryWith1City_shouldShowPickerIsFalse { [weak self] in + await self?.viewModel.onViewAppeared() + } + } + + func test_onViewAppeared_countryHasMoreThan1City_shouldShowPickerIsTrue() async throws { + try await assertOnListLoad_countryHasMoreThan1City_shouldShowPickerIsTrue { [weak self] in + await self?.viewModel.onViewAppeared() + } + } + + func test_onViewAppeared_countryHasMoreThan1City_subtitleShowsCityCount() async throws { + try await assertOnListLoad_countryHasMoreThan1City_subtitleShowsCityCount { [weak self] in + await self?.viewModel.onViewAppeared() + } + } + + func test_onViewAppeared_showsCityTitles() async throws { + try await assertOnListLoad_showsCityTitles { [weak self] in + await self?.viewModel.onViewAppeared() + } + } + + func test_onViewAppeared_cityIsSelected_itemIsSelected() async throws { + try await assertOnListLoad_itemIsSelected { [weak self] testCase in + self?.tunnelSettings.selectedLocation = .location(testCase) + await self?.viewModel.onViewAppeared() + } + } + + func test_onViewAppeared_cityIsNOTSelected_itemIsNOTSelected() async throws { + let countries: [NetworkProtectionLocation] = [ + try .testData(country: "NL", cityNames: ["Rotterdam", "Amsterdam"]), + try .testData(country: "DE", cityNames: ["Berlin", "Frankfurt", "Bremen"]) + ] + + listRepository.stubLocationList = countries + tunnelSettings.selectedLocation = .location(NetworkProtectionSelectedLocation(country: "US", city: "New York")) + await viewModel.onViewAppeared() + let selectedItems = try loadedItems().flatMap { $0.cityPickerItems }.filter(\.isSelected) + XCTAssertEqual(selectedItems.count, 0) + } + + func test_onViewAppeared_countryWithoutCityIsSelected_nearestItemIsSelected() async throws { + try await assertOnListLoad_nearestItemIsSelected { [weak self] testCase in + self?.tunnelSettings.selectedLocation = .location(testCase) + await self?.viewModel.onViewAppeared() + } + } + + // MARK: onNearestItemSelection + + func test_onNearestItemSelection_setsCorrectCountryTitle() async throws { + try await assertOnListLoadSetsCorrectCountryTitle { [weak self] in + await self?.viewModel.onNearestItemSelection() + } + } + + func test_onNearestItemSelection_setsCountryID() async throws { + try await assertOnListLoadSetsCorrectCountryID { [weak self] in + await self?.viewModel.onViewAppeared() + } + } + + func test_onNearestItemSelection_setsCorrectEmoji() async throws { + try await assertOnListLoadSetsCorrectEmoji { [weak self] in + await self?.viewModel.onNearestItemSelection() + } + } + + func test_onNearestItemSelection_isNearestSelectedSetToTrue() async throws { + try await assertNearestSelectedSetToTrue { [weak self] in + await self?.viewModel.onNearestItemSelection() + } + } + + func test_onNearestItemSelection_countryHas1City_subtitleIsNil() async throws { + try await assertOnListLoad_countryWith1City_hasNilSubtitle { [weak self] in + await self?.viewModel.onNearestItemSelection() + } + } + + func test_onNearestItemSelection_countryHas1City_shouldShowPickerIsFalse() async throws { + try await assertOnListLoad_countryWith1City_shouldShowPickerIsFalse { [weak self] in + await self?.viewModel.onNearestItemSelection() + } + } + + func test_onNearestItemSelection_countryHasMoreThan1City_shouldShowPickerIsTrue() async throws { + try await assertOnListLoad_countryHasMoreThan1City_shouldShowPickerIsTrue { [weak self] in + await self?.viewModel.onNearestItemSelection() + } + } + + func test_onNearestItemSelection_countryHasMoreThan1City_subtitleShowsCityCount() async throws { + try await assertOnListLoad_countryHasMoreThan1City_subtitleShowsCityCount { [weak self] in + await self?.viewModel.onNearestItemSelection() + } + } + + func test_onNearestItemSelection_showsCityTitles() async throws { + try await assertOnListLoad_showsCityTitles { [weak self] in + await self?.viewModel.onNearestItemSelection() + } + } + + // MARK: onCountryItemSelection + + func test_onCountryItemSelection_setsCorrectCountryTitle() async throws { + try await assertOnListLoadSetsCorrectCountryTitle { [weak self] in + await self?.viewModel.onCountryItemSelection(id: "NL", cityId: "Rotterdam") + } + } + + func test_onCountryItemSelection_setsCountryID() async throws { + try await assertOnListLoadSetsCorrectCountryID { [weak self] in + await self?.viewModel.onCountryItemSelection(id: "NL", cityId: "Rotterdam") + } + } + + func test_onCountryItemSelection_setsCorrectEmoji() async throws { + try await assertOnListLoadSetsCorrectEmoji { [weak self] in + await self?.viewModel.onCountryItemSelection(id: "NL", cityId: "Rotterdam") + } + } + + func test_onCountryItemSelection_selectedCountryFromSettings_isSelectedSetToTrue() async throws { + try await assertOnListLoad_countryIsSelected { [weak self] testCaseCountryId in + await self?.viewModel.onCountryItemSelection(id: testCaseCountryId) + } + } + + func test_onCountryItemSelection_NOTSelectedCountry_isSelectedSetToFalse() async throws { + try await assertOnListLoad_isSelectedSetToFalse { [weak self] in + await self?.viewModel.onCountryItemSelection(id: "US") + } + } + + func test_onCountryItemSelection_isNearestSelectedSetToFalse() async throws { + try await assertNearestSelectedSetToFalse { [weak self] testCaseCountryId in + await self?.viewModel.onCountryItemSelection(id: testCaseCountryId) + } + } + + func test_onCountryItemSelection_countryHas1City_subtitleIsNil() async throws { + try await assertOnListLoad_countryWith1City_hasNilSubtitle { [weak self] in + await self?.viewModel.onCountryItemSelection(id: "NL", cityId: "Rotterdam") + } + } + + func test_onCountryItemSelection_countryHas1City_shouldShowPickerIsFalse() async throws { + try await assertOnListLoad_countryWith1City_shouldShowPickerIsFalse { [weak self] in + await self?.viewModel.onCountryItemSelection(id: "NL", cityId: "Rotterdam") + } + } + + func test_onCountryItemSelection_countryHasMoreThan1City_shouldShowPickerIsTrue() async throws { + try await assertOnListLoad_countryHasMoreThan1City_shouldShowPickerIsTrue { [weak self] in + await self?.viewModel.onCountryItemSelection(id: "NL", cityId: "Rotterdam") + } + } + + func test_onCountryItemSelection_countryHasMoreThan1City_subtitleShowsCityCount() async throws { + try await assertOnListLoad_countryHasMoreThan1City_subtitleShowsCityCount { [weak self] in + await self?.viewModel.onCountryItemSelection(id: "NL", cityId: "Rotterdam") + } + } + + func test_onCountryItemSelection_showsCityTitles() async throws { + try await assertOnListLoad_showsCityTitles { [weak self] in + await self?.viewModel.onCountryItemSelection(id: "NL", cityId: "Rotterdam") + } + } + + func test_onCountryItemSelection_cityIsSelected_itemIsSelected() async throws { + try await assertOnListLoad_itemIsSelected { [weak self] testCase in + await self?.viewModel.onCountryItemSelection(id: testCase.country, cityId: testCase.city) + } + } + + func test_onCountryItemSelection_countryWithoutCityIsSelected_nearestItemIsSelected() async throws { + try await assertOnListLoad_nearestItemIsSelected { [weak self] testCase in + await self?.viewModel.onCountryItemSelection(id: testCase.country, cityId: testCase.city) + } + } + + // MARK: Assertions + + func assertOnListLoadSetsCorrectCountryTitle(when functionUnderTest: () async -> Void, + file: StaticString = #file, + line: UInt = #line) async throws { + let titlesForLocationsIDs = [ + "NL": "Netherlands", + "DE": "Germany", + "SE": "Sweden" + ] + let countryIds = Array(titlesForLocationsIDs.keys) + try stubLocationList(with: countryIds) + + await functionUnderTest() + + let items = try loadedItems() + + for i in 0.. Void, + file: StaticString = #file, + line: UInt = #line) async throws { + let iDsForLocationsIDs = [ + "NL": "NL", + "DE": "DE", + "SE": "SE" + ] + let countryIds = Array(iDsForLocationsIDs.keys) + try stubLocationList(with: countryIds) + + await functionUnderTest() + + let items = try loadedItems() + + for i in 0.. Void, + file: StaticString = #file, + line: UInt = #line) async throws { + let emojisForLocationsIDs = [ + "NL": "🇳🇱", + "DE": "🇩🇪", + "SE": "🇸🇪" + ] + let countryIds = Array(emojisForLocationsIDs.keys) + try stubLocationList(with: countryIds) + + await functionUnderTest() + + let items = try loadedItems() + + for i in 0.. Void, + file: StaticString = #file, + line: UInt = #line) async throws { + let countryIds = ["NL", "DE", "SE"] + try stubLocationList(with: countryIds) + + for i in 0.. Void, + file: StaticString = #file, + line: UInt = #line) async throws { + try stubLocationList(with: ["NL", "DE", "SE"]) + + await functionUnderTest() + + XCTAssertTrue(viewModel.isNearestSelected, file: file, line: line) + } + + func assertNearestSelectedSetToFalse(when functionUnderTestWithTestCaseID: (String) async -> Void, + file: StaticString = #file, + line: UInt = #line) async throws { + let countryIds = ["NL", "DE", "SE"] + try stubLocationList(with: countryIds) + + for i in 0.. Void, + file: StaticString = #file, + line: UInt = #line) async throws { + let countryIds = ["NL", "DE", "SE"] + let countries: [NetworkProtectionLocation] = try countryIds.map { id in + try .testData(country: id, cityNames: ["A city"]) + } + listRepository.stubLocationList = countries + + await functionUnderTest() + + let items = try loadedItems() + + for i in 0.. Void, + file: StaticString = #file, + line: UInt = #line) async throws { + let countryIds = ["NL", "DE", "SE"] + let countries: [NetworkProtectionLocation] = try countryIds.map { id in + try .testData(country: id, cityNames: ["A city"]) + } + listRepository.stubLocationList = countries + await functionUnderTest() + let items = try loadedItems() + + for i in 0.. Void, + file: StaticString = #file, + line: UInt = #line) async throws { + let countryIds = ["NL", "DE", "SE"] + var countries = [NetworkProtectionLocation]() + + for i in 0.. Void, + file: StaticString = #file, + line: UInt = #line) async throws { + struct TestCase { + let countryId: String + let citiesCount: Int + let expectedSubtitle: String + } + let testCases: [TestCase] = [ + .init(countryId: "NL", citiesCount: 2, expectedSubtitle: "2 cities"), + .init(countryId: "DE", citiesCount: 3, expectedSubtitle: "3 cities"), + .init(countryId: "SE", citiesCount: 14, expectedSubtitle: "14 cities") + ] + let countries: [NetworkProtectionLocation] = try testCases.map { testCase in + var cities = [String]() + for i in 0.. Void, + file: StaticString = #file, + line: UInt = #line) async throws { + let countryIds = ["NL", "DE", "SE"] + let countries: [NetworkProtectionLocation] = try countryIds.map { id in + try .testData(country: id) + } + listRepository.stubLocationList = countries + + await functionUnderTest() + + for i in 0.. Void, + file: StaticString = #file, + line: UInt = #line) async throws { + struct TestCase { + let countryId: String + let cityTitles: [String] + } + let testCases: [TestCase] = [ + .init(countryId: "NL", cityTitles: ["Rotterdam", "Amsterdam"]), + .init(countryId: "DE", cityTitles: ["Berlin", "Frankfurt", "Bremen"]) + ] + let countries: [NetworkProtectionLocation] = try testCases.map { testCase in + return try .testData(country: testCase.countryId, cityNames: testCase.cityTitles) + } + listRepository.stubLocationList = countries + + await functionUnderTest() + + let items = try loadedItems() + + for testCaseIndex in 0.. Void, + file: StaticString = #file, + line: UInt = #line) async throws { + let countries: [NetworkProtectionLocation] = [ + try .testData(country: "NL", cityNames: ["Rotterdam", "Amsterdam"]), + try .testData(country: "DE", cityNames: ["Berlin", "Frankfurt", "Bremen"]) + ] + + let selectionTestCases: [NetworkProtectionSelectedLocation] = [ + .init(country: "NL", city: "Amsterdam"), + .init(country: "DE", city: "Frankfurt") + ] + + listRepository.stubLocationList = countries + + for testCase in selectionTestCases { + await functionUnderTestWithTestCase(testCase) + let selectedItems = try loadedItems().flatMap { $0.cityPickerItems }.filter(\.isSelected) + XCTAssertEqual(selectedItems.count, 1, file: file, line: line) + XCTAssertEqual(selectedItems.first?.id, testCase.city, file: file, line: line) + } + } + + func assertOnListLoad_nearestItemIsSelected(when functionUnderTestWithTestCase: (NetworkProtectionSelectedLocation) async -> Void, + file: StaticString = #file, + line: UInt = #line) async throws { + let countries: [NetworkProtectionLocation] = [ + try .testData(country: "NL", cityNames: ["Rotterdam", "Amsterdam"]), + try .testData(country: "DE", cityNames: ["Berlin", "Frankfurt", "Bremen"]), + try .testData(country: "SE", cityNames: ["Stockholm", "Malmö", "Helsingborg"]) + ] + + let selectionTestCases: [NetworkProtectionSelectedLocation] = [ + .init(country: "NL"), + .init(country: "SE") + ] + + listRepository.stubLocationList = countries + + for testCase in selectionTestCases { + await functionUnderTestWithTestCase(testCase) + let selectedItems = try loadedItems().flatMap { $0.cityPickerItems }.filter(\.isSelected) + XCTAssertEqual(selectedItems.count, 1, file: file, line: line) + XCTAssertNil(selectedItems.first?.id, file: file, line: line) + XCTAssertEqual( + selectedItems.first?.name, + UserText.netPVPNLocationNearestAvailableItemTitle, + file: file, + line: line + ) + } + } + + // MARK: Helpers + + private func stubLocationList(with countryIDs: [String]) throws { + let countries: [NetworkProtectionLocation] = try countryIDs.map { id in + try .testData(country: id) + } + listRepository.stubLocationList = countries + } + + private func loadedItems() throws -> [NetworkProtectionVPNCountryItemModel] { + guard case .loaded(let items) = viewModel.state else { + throw TestError.notLoadedState + } + return items + } + + private enum TestError: Error { + case notLoadedState + } +} + +// swiftlint:enable type_body_length + +final class MockNetworkProtectionLocationListRepository: NetworkProtectionLocationListRepository { + var stubLocationList: [NetworkProtectionLocation] = [] + var stubError: Error? + var didCallFetchLocationList: Bool = false + + func fetchLocationList() async throws -> [NetworkProtectionLocation] { + didCallFetchLocationList = true + if let stubError { + throw stubError + } + return stubLocationList + } +} + +extension NetworkProtectionLocation { + static func testData(country: String = "", cityNames: [String] = []) throws -> Self { + let cities = cityNames.map { ["name": $0] } + let dict: [String: Encodable] = ["country": country, "cities": cities] + let wrappedDict = dict.mapValues(EncodableWrapper.init(wrapped:)) + let data = try JSONEncoder().encode(wrappedDict) + return try JSONDecoder().decode(Self.self, from: data) + } +} + +struct EncodableWrapper: Encodable { + let wrapped: Encodable + + func encode(to encoder: Encoder) throws { + try self.wrapped.encode(to: encoder) + } +} + +// swiftlint:enable file_length diff --git a/DuckDuckGoTests/SyncManagementViewModelTests.swift b/DuckDuckGoTests/SyncManagementViewModelTests.swift index 09c1f6338e..7964c0910c 100644 --- a/DuckDuckGoTests/SyncManagementViewModelTests.swift +++ b/DuckDuckGoTests/SyncManagementViewModelTests.swift @@ -31,23 +31,88 @@ class SyncManagementViewModelTests: XCTestCase, SyncManagementViewModelDelegate return model }() - func test_WhenSyncIsNotSetup_AndEnableSyncIsTriggered_ThenManagerBecomesBusy_AndSetupIsShown() { - model.enableSync() + var createAccountAndStartSyncingCalled = false + var caprturedOptionModel: SyncSettingsViewModel? + + func testWhenSingleDeviceSetUpPressed_ThenManagerBecomesBusy_AndAccounCreationRequested() { + model.startSyncPressed() XCTAssertTrue(model.isBusy) + XCTAssertTrue(createAccountAndStartSyncingCalled) + XCTAssertNotNil(caprturedOptionModel) + } + + func testWhenScanQRCodePressed_ThenSyncWithAnotherDeviceViewIsShown() { + model.scanQRCode() + + // You can either test one individual call was made x number of times or check for a whole number of calls + monitor.assert(#selector(showSyncWithAnotherDevice).description, calls: 1) + monitor.assertCalls([ + #selector(showSyncWithAnotherDevice).description: 1 + ]) + } + + func testWhenSyncIsNotSetup_EnterTextCodePressed_ThenSyncWithAnotherDeviceEnterTextViewIsShown() { + model.showEnterTextView() + + // You can either test one individual call was made x number of times or check for a whole number of calls + monitor.assert(#selector(showSyncWithAnotherDeviceEnterText).description, calls: 1) + monitor.assertCalls([ + #selector(showSyncWithAnotherDeviceEnterText).description: 1 + ]) + } + + func testWhenRecoverYourDataPressed_RecoverDataViewIsShown() { + model.showRecoverDataView() + + // You can either test one individual call was made x number of times or check for a whole number of calls + monitor.assert(#selector(showRecoverData).description, calls: 1) + monitor.assertCalls([ + #selector(showRecoverData).description: 1 + ]) + } + + func testWhenSaveRecoveryPDFPressed_recoveryMethodShown() { + model.saveRecoveryPDF() + + // You can either test one individual call was made x number of times or check for a whole number of calls + monitor.assert(#selector(shareRecoveryPDF).description, calls: 1) + monitor.assertCalls([ + #selector(shareRecoveryPDF).description: 1 + ]) + } + + func testWhenManageBookmarksCalled_BookmarksVCIsLaunched() { + model.manageBookmarks() + + // You can either test one individual call was made x number of times or check for a whole number of calls + monitor.assert(#selector(launchBookmarksViewController).description, calls: 1) + monitor.assertCalls([ + #selector(launchBookmarksViewController).description: 1 + ]) + } + + func testWhenManageLogindCalled_AutofillVCIsLaunched() { + model.manageLogins() + // You can either test one individual call was made x number of times or check for a whole number of calls - monitor.assert(#selector(showSyncSetup).description, calls: 1) + monitor.assert(#selector(launchAutofillViewController).description, calls: 1) monitor.assertCalls([ - #selector(showSyncSetup).description: 1 + #selector(launchAutofillViewController).description: 1 ]) } // MARK: Delegate functions - func showSyncSetup() { + func showSyncWithAnotherDeviceEnterText() { monitor.incrementCalls(function: #function.cleaningFunctionName()) } + func createAccountAndStartSyncing(optionsViewModel: SyncSettingsViewModel) { + createAccountAndStartSyncingCalled = true + caprturedOptionModel = optionsViewModel + } + func showRecoverData() { monitor.incrementCalls(function: #function.cleaningFunctionName()) } @@ -64,10 +129,6 @@ class SyncManagementViewModelTests: XCTestCase, SyncManagementViewModelDelegate monitor.incrementCalls(function: #function.cleaningFunctionName()) } - func createAccountAndStartSyncing() { - monitor.incrementCalls(function: #function.cleaningFunctionName()) - } - func confirmAndDisableSync() async -> Bool { monitor.incrementCalls(function: #function.cleaningFunctionName()) return true @@ -103,6 +164,18 @@ class SyncManagementViewModelTests: XCTestCase, SyncManagementViewModelDelegate monitor.incrementCalls(function: #function.cleaningFunctionName()) } + func updateOptions() { + monitor.incrementCalls(function: #function.cleaningFunctionName()) + } + + func launchBookmarksViewController() { + monitor.incrementCalls(function: #function.cleaningFunctionName()) + } + + func launchAutofillViewController() { + monitor.incrementCalls(function: #function.cleaningFunctionName()) + } + } // MARK: An idea... can be made more public if works out diff --git a/DuckDuckGoTests/WindowsBrowserWaitlistTests.swift b/DuckDuckGoTests/WindowsBrowserWaitlistTests.swift index 388295dbba..b7369ffdb0 100644 --- a/DuckDuckGoTests/WindowsBrowserWaitlistTests.swift +++ b/DuckDuckGoTests/WindowsBrowserWaitlistTests.swift @@ -61,7 +61,6 @@ class WindowsBrowserWaitlistTests: XCTestCase { let request = MockWaitlistRequest.failure() let privacyConfigurationManager: PrivacyConfigurationManagerMock = PrivacyConfigurationManagerMock() let privacyConfig = privacyConfigurationManager.privacyConfig as! PrivacyConfigurationMock // swiftlint:disable:this force_cast - var enabledFeatures = privacyConfig.enabledFeaturesForVersions privacyConfig.enabledFeaturesForVersions[.windowsDownloadLink] = Set([AppVersionProvider().appVersion()!]) let waitlist = WindowsBrowserWaitlist(store: store, request: request, privacyConfigurationManager: privacyConfigurationManager) diff --git a/Gemfile b/Gemfile index ebcf642610..6a591a7207 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,3 @@ source 'https://rubygems.org' -gem 'fastlane', :git => 'https://github.com/duckduckgo/fastlane.git', :tag => '2.214.0+ddg' +gem 'fastlane', '2.217.0' diff --git a/Gemfile.lock b/Gemfile.lock index cc08230c4a..d857156940 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,48 +1,3 @@ -GIT - remote: https://github.com/duckduckgo/fastlane.git - revision: b4de9a502e69ff304bf30a0943c00ad8a09951ab - tag: 2.214.0+ddg - specs: - fastlane (2.214.0) - CFPropertyList (>= 2.3, < 4.0.0) - addressable (>= 2.8, < 3.0.0) - artifactory (~> 3.0) - aws-sdk-s3 (~> 1.0) - babosa (>= 1.0.3, < 2.0.0) - bundler (>= 1.12.0, < 3.0.0) - colored - commander (~> 4.6) - dotenv (>= 2.1.1, < 3.0.0) - emoji_regex (>= 0.1, < 4.0) - excon (>= 0.71.0, < 1.0.0) - faraday (~> 1.0) - faraday-cookie_jar (~> 0.0.6) - faraday_middleware (~> 1.0) - fastimage (>= 2.1.0, < 3.0.0) - gh_inspector (>= 1.1.2, < 2.0.0) - google-apis-androidpublisher_v3 (~> 0.3) - google-apis-playcustomapp_v1 (~> 0.1) - google-cloud-storage (~> 1.31) - highline (~> 2.0) - json (< 3.0.0) - jwt (>= 2.1.0, < 3) - mini_magick (>= 4.9.4, < 5.0.0) - multipart-post (>= 2.0.0, < 3.0.0) - naturally (~> 2.2) - optparse (~> 0.1.1) - plist (>= 3.1.0, < 4.0.0) - rubyzip (>= 2.0.0, < 3.0.0) - security (= 0.1.3) - simctl (~> 1.6.3) - terminal-notifier (>= 2.0.0, < 3.0.0) - terminal-table (>= 1.4.5, < 2.0.0) - tty-screen (>= 0.6.3, < 1.0.0) - tty-spinner (>= 0.8.0, < 1.0.0) - word_wrap (~> 1.0.0) - xcodeproj (>= 1.13.0, < 2.0.0) - xcpretty (~> 0.3.0) - xcpretty-travis-formatter (>= 0.0.3) - GEM remote: https://rubygems.org/ specs: @@ -53,20 +8,20 @@ GEM artifactory (3.0.15) atomos (0.1.3) aws-eventstream (1.2.0) - aws-partitions (1.812.0) - aws-sdk-core (3.181.0) + aws-partitions (1.850.0) + aws-sdk-core (3.186.0) aws-eventstream (~> 1, >= 1.0.2) aws-partitions (~> 1, >= 1.651.0) aws-sigv4 (~> 1.5) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.71.0) - aws-sdk-core (~> 3, >= 3.177.0) + aws-sdk-kms (1.72.0) + aws-sdk-core (~> 3, >= 3.184.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.134.0) + aws-sdk-s3 (1.136.0) aws-sdk-core (~> 3, >= 3.181.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.6) - aws-sigv4 (1.6.0) + aws-sigv4 (1.6.1) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) claide (1.1.0) @@ -77,11 +32,10 @@ GEM declarative (0.0.20) digest-crc (0.6.5) rake (>= 12.0.0, < 14.0.0) - domain_name (0.5.20190701) - unf (>= 0.0.5, < 1.0.0) + domain_name (0.6.20231109) dotenv (2.8.1) emoji_regex (3.2.3) - excon (0.102.0) + excon (0.104.0) faraday (1.10.3) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) @@ -111,10 +65,50 @@ GEM faraday_middleware (1.2.0) faraday (~> 1.0) fastimage (2.2.7) + fastlane (2.217.0) + CFPropertyList (>= 2.3, < 4.0.0) + addressable (>= 2.8, < 3.0.0) + artifactory (~> 3.0) + aws-sdk-s3 (~> 1.0) + babosa (>= 1.0.3, < 2.0.0) + bundler (>= 1.12.0, < 3.0.0) + colored + commander (~> 4.6) + dotenv (>= 2.1.1, < 3.0.0) + emoji_regex (>= 0.1, < 4.0) + excon (>= 0.71.0, < 1.0.0) + faraday (~> 1.0) + faraday-cookie_jar (~> 0.0.6) + faraday_middleware (~> 1.0) + fastimage (>= 2.1.0, < 3.0.0) + gh_inspector (>= 1.1.2, < 2.0.0) + google-apis-androidpublisher_v3 (~> 0.3) + google-apis-playcustomapp_v1 (~> 0.1) + google-cloud-storage (~> 1.31) + highline (~> 2.0) + http-cookie (~> 1.0.5) + json (< 3.0.0) + jwt (>= 2.1.0, < 3) + mini_magick (>= 4.9.4, < 5.0.0) + multipart-post (>= 2.0.0, < 3.0.0) + naturally (~> 2.2) + optparse (~> 0.1.1) + plist (>= 3.1.0, < 4.0.0) + rubyzip (>= 2.0.0, < 3.0.0) + security (= 0.1.3) + simctl (~> 1.6.3) + terminal-notifier (>= 2.0.0, < 3.0.0) + terminal-table (~> 3) + tty-screen (>= 0.6.3, < 1.0.0) + tty-spinner (>= 0.8.0, < 1.0.0) + word_wrap (~> 1.0.0) + xcodeproj (>= 1.13.0, < 2.0.0) + xcpretty (~> 0.3.0) + xcpretty-travis-formatter (>= 0.0.3) gh_inspector (1.1.3) - google-apis-androidpublisher_v3 (0.49.0) + google-apis-androidpublisher_v3 (0.52.0) google-apis-core (>= 0.11.0, < 2.a) - google-apis-core (0.11.1) + google-apis-core (0.11.2) addressable (~> 2.5, >= 2.5.1) googleauth (>= 0.16.2, < 2.a) httpclient (>= 2.8.1, < 3.a) @@ -127,26 +121,25 @@ GEM google-apis-core (>= 0.11.0, < 2.a) google-apis-playcustomapp_v1 (0.13.0) google-apis-core (>= 0.11.0, < 2.a) - google-apis-storage_v1 (0.19.0) - google-apis-core (>= 0.9.0, < 2.a) + google-apis-storage_v1 (0.29.0) + google-apis-core (>= 0.11.0, < 2.a) google-cloud-core (1.6.0) google-cloud-env (~> 1.0) google-cloud-errors (~> 1.0) google-cloud-env (1.6.0) faraday (>= 0.17.3, < 3.0) google-cloud-errors (1.3.1) - google-cloud-storage (1.44.0) + google-cloud-storage (1.45.0) addressable (~> 2.8) digest-crc (~> 0.4) google-apis-iamcredentials_v1 (~> 0.1) - google-apis-storage_v1 (~> 0.19.0) + google-apis-storage_v1 (~> 0.29.0) google-cloud-core (~> 1.6) googleauth (>= 0.16.2, < 2.a) mini_mime (~> 1.0) - googleauth (1.7.0) + googleauth (1.8.1) faraday (>= 0.17.3, < 3.a) jwt (>= 1.4, < 3.0) - memoist (~> 0.16) multi_json (~> 1.11) os (>= 0.9, < 2.0) signet (>= 0.16, < 2.a) @@ -157,7 +150,6 @@ GEM jmespath (1.6.2) json (2.6.3) jwt (2.7.1) - memoist (0.16.2) mini_magick (4.12.0) mini_mime (1.1.5) multi_json (1.15.0) @@ -168,7 +160,7 @@ GEM os (1.1.4) plist (3.7.0) public_suffix (5.0.3) - rake (13.0.6) + rake (13.1.0) representable (3.2.0) declarative (< 0.1.0) trailblazer-option (>= 0.1.1, < 0.2.0) @@ -179,7 +171,7 @@ GEM ruby2_keywords (0.0.5) rubyzip (2.3.2) security (0.1.3) - signet (0.17.0) + signet (0.18.0) addressable (~> 2.8) faraday (>= 0.17.5, < 3.a) jwt (>= 1.5, < 3.0) @@ -188,21 +180,18 @@ GEM CFPropertyList naturally terminal-notifier (2.0.0) - terminal-table (1.8.0) - unicode-display_width (~> 1.1, >= 1.1.1) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) trailblazer-option (0.1.2) tty-cursor (0.7.1) tty-screen (0.8.1) tty-spinner (0.9.3) tty-cursor (~> 0.7) uber (0.1.0) - unf (0.1.4) - unf_ext - unf_ext (0.0.8.2) - unicode-display_width (1.8.0) + unicode-display_width (2.5.0) webrick (1.8.1) word_wrap (1.0.0) - xcodeproj (1.22.0) + xcodeproj (1.23.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) @@ -218,7 +207,7 @@ PLATFORMS ruby DEPENDENCIES - fastlane! + fastlane (= 2.217.0) BUNDLED WITH 2.3.26 diff --git a/IntegrationTests/ContentBlockingRulesTests.swift b/IntegrationTests/ContentBlockingRulesTests.swift index a03e8bdcc6..723fa8bec8 100644 --- a/IntegrationTests/ContentBlockingRulesTests.swift +++ b/IntegrationTests/ContentBlockingRulesTests.swift @@ -33,10 +33,10 @@ class ContentBlockingRulesTests: XCTestCase { andTemporaryUnprotectedDomains: []) // Test tracker is set up to be blocked - if let rule = rules.findExactFilter(filter: "^(https?)?(wss?)?://([a-z0-9-]+\\.)*googleadservices\\.com(:?[0-9]+)?/.*") { + if let rule = rules.findExactFilter(filter: "^(https?)?(wss?)?://([a-z0-9-]+\\.)*bad\\.third-party\\.site(:?[0-9]+)?/.*") { XCTAssert(rule.action == .block()) } else { - XCTFail("Missing google ad services rule") + XCTFail("Missing tracking rule") } // Test exceptiions are set to ignore previous rules diff --git a/LocalPackages/SyncUI/Package.swift b/LocalPackages/SyncUI/Package.swift index f8193ce7ef..f95f0f7cc2 100644 --- a/LocalPackages/SyncUI/Package.swift +++ b/LocalPackages/SyncUI/Package.swift @@ -31,13 +31,15 @@ let package = Package( targets: ["SyncUI"]) ], dependencies: [ - .package(path: "../DuckUI") + .package(path: "../DuckUI"), + .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ .target( name: "SyncUI", dependencies: [ - .product(name: "DuckUI", package: "DuckUI") + .product(name: "DuckUI", package: "DuckUI"), + "DesignResourcesKit" ]) ] ) diff --git a/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/ScanOrPasteCodeViewModel.swift b/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/ScanOrPasteCodeViewModel.swift index 3c3c00e315..7c5d1f181a 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/ScanOrPasteCodeViewModel.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/ScanOrPasteCodeViewModel.swift @@ -91,7 +91,6 @@ public class ScanOrPasteCodeViewModel: ObservableObject { Task { @MainActor in let codeUsed = await delegate?.syncCodeEntered(code: string) == true - isValidating = false if !codeUsed { invalidCode = true } diff --git a/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/SyncSettingsViewModel.swift b/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/SyncSettingsViewModel.swift index bf607ec063..3e13d1c9a1 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/SyncSettingsViewModel.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/SyncSettingsViewModel.swift @@ -22,12 +22,12 @@ import UIKit public protocol SyncManagementViewModelDelegate: AnyObject { - func showSyncSetup() func showRecoverData() func showSyncWithAnotherDevice() + func showSyncWithAnotherDeviceEnterText() func showRecoveryPDF() func shareRecoveryPDF() - func createAccountAndStartSyncing() + func createAccountAndStartSyncing(optionsViewModel: SyncSettingsViewModel) func confirmAndDisableSync() async -> Bool func confirmAndDeleteAllData() async -> Bool func copyCode() @@ -35,7 +35,9 @@ public protocol SyncManagementViewModelDelegate: AnyObject { func removeDevice(_ device: SyncSettingsViewModel.Device) func updateDeviceName(_ name: String) func refreshDevices(clearDevices: Bool) - + func updateOptions() + func launchBookmarksViewController() + func launchAutofillViewController() } public class SyncSettingsViewModel: ObservableObject { @@ -74,20 +76,19 @@ public class SyncSettingsViewModel: ObservableObject { } @Published public var devices = [Device]() + @Published public var isFaviconsSyncEnabled = false + @Published public var isUnifiedFavoritesEnabled = true + @Published public var isSyncingDevices = false + @Published public var isSyncBookmarksPaused = false + @Published public var isSyncCredentialsPaused = false + @Published var isBusy = false @Published var recoveryCode = "" - var setupFinishedState: TurnOnSyncViewModel.Result? - public weak var delegate: SyncManagementViewModelDelegate? public init() { } - func enableSync() { - isBusy = true - delegate!.showSyncSetup() - } - func disableSync() { isBusy = true Task { @MainActor in @@ -120,6 +121,14 @@ public class SyncSettingsViewModel: ObservableObject { delegate?.showSyncWithAnotherDevice() } + func showEnterTextView() { + delegate?.showSyncWithAnotherDeviceEnterText() + } + + func showRecoverDataView() { + delegate?.showRecoverData() + } + func createEditDeviceModel(_ device: Device) -> EditDeviceViewModel { return EditDeviceViewModel(device: device) { [weak self] newValue in self?.delegate?.updateDeviceName(newValue.name) @@ -132,37 +141,23 @@ public class SyncSettingsViewModel: ObservableObject { } } - // MARK: Called by the view controller - public func syncEnabled(recoveryCode: String) { isBusy = false isSyncEnabled = true self.recoveryCode = recoveryCode } - public func setupFinished(_ model: TurnOnSyncViewModel) { - setupFinishedState = model.state - switch model.state { - case .turnOn: - delegate?.createAccountAndStartSyncing() - - case .syncWithAnotherDevice: - delegate?.showSyncWithAnotherDevice() - - case .recoverData: - delegate?.showRecoverData() + public func startSyncPressed() { + isBusy = true + delegate?.createAccountAndStartSyncing(optionsViewModel: self) + } - default: - isBusy = false - } + public func manageBookmarks() { + delegate?.launchBookmarksViewController() } - public func codeCollectionCancelled() { - if setupFinishedState == .syncWithAnotherDevice { - delegate?.createAccountAndStartSyncing() - } else { - isBusy = false - } + public func manageLogins() { + delegate?.launchAutofillViewController() } } diff --git a/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/TurnOnSyncViewModel.swift b/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/TurnOnSyncViewModel.swift deleted file mode 100644 index a73404eacd..0000000000 --- a/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/TurnOnSyncViewModel.swift +++ /dev/null @@ -1,61 +0,0 @@ -// -// TurnOnSyncViewModel.swift -// DuckDuckGo -// -// Copyright © 2023 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation - -public class TurnOnSyncViewModel: ObservableObject { - - let finished: () -> Void - - public init(finished: @escaping () -> Void) { - self.finished = finished - } - - @Published var state: Result = .doNothing - - enum Result { - - case doNothing, turnOn, syncWithAnotherDevice, recoverData - - } - - func turnOnSyncAction() { - state = .turnOn - } - - func syncWithAnotherDeviceAction() { - state = .syncWithAnotherDevice - finished() - } - - func notNowAction() { - finished() - } - - func recoverDataAction() { - state = .recoverData - finished() - } - - func cancelAction() { - state = .doNothing - finished() - } - -} diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Views/DeviceConnectedView.swift b/LocalPackages/SyncUI/Sources/SyncUI/Views/DeviceConnectedView.swift index 7d1901dae4..2adec5fc5c 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Views/DeviceConnectedView.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/Views/DeviceConnectedView.swift @@ -19,6 +19,8 @@ import SwiftUI import DuckUI +import DesignResourcesKit + public struct DeviceConnectedView: View { @@ -27,58 +29,66 @@ public struct DeviceConnectedView: View { var isCompact: Bool { verticalSizeClass == .compact } - + let isSingleSetUp: Bool + let shouldShowOptions: Bool @State var showRecoveryPDF = false let saveRecoveryKeyViewModel: SaveRecoveryKeyViewModel + @ObservedObject var optionsViewModel: SyncSettingsViewModel let devices: [SyncSettingsViewModel.Device] - public init(_ saveRecoveryKeyViewModel: SaveRecoveryKeyViewModel, devices: [SyncSettingsViewModel.Device]) { + public init(_ saveRecoveryKeyViewModel: SaveRecoveryKeyViewModel, optionsViewModel: SyncSettingsViewModel, devices: [SyncSettingsViewModel.Device], isSingleSetUp: Bool, shouldShowOptions: Bool) { self.saveRecoveryKeyViewModel = saveRecoveryKeyViewModel self.devices = devices + self.optionsViewModel = optionsViewModel + self.isSingleSetUp = isSingleSetUp + self.shouldShowOptions = shouldShowOptions + } + + var title: String { + if isSingleSetUp { + return UserText.syngleDeviceConnectedTitle + } + return UserText.deviceSyncedTitle + } + + var message: String { + if isSingleSetUp { + return UserText.firstDeviceSyncedMessage + } + if devices.count == 1 { + return UserText.deviceSyncedMessage + } + return UserText.multipleDevicesSyncedMessage + } + + var devicesOnMessageText: String { + if devices.isEmpty { + return "" + } + if devices.count == 1 { + return devices[0].name + } + return "\(devices.count + 1) " + UserText.wordDevices } @ViewBuilder func deviceSyncedView() -> some View { UnderflowContainer { VStack(spacing: 0) { - Image("SyncSuccess") + Image("Sync-Start-128") .padding(.bottom, 20) - Text(UserText.deviceSyncedTitle) - .font(.system(size: 28, weight: .bold)) + Text(title) + .daxTitle1() .padding(.bottom, 24) - ZStack { - RoundedRectangle(cornerRadius: 8) - .stroke(.black.opacity(0.14)) - - ScrollView { - VStack(spacing: 0) { - ForEach(devices.indices, id: \.self) { deviceIndex in - - HStack(spacing: 0) { - Image(systemName: "checkmark.circle") - .padding(.horizontal, 18) - Text(devices[deviceIndex].name) - Spacer() - } - .frame(height: 44) - - if deviceIndex + 1 < devices.count { - Divider() - .padding(.leading, 52) - } - } - } - } - } - .padding(.bottom, 20) - - Text(UserText.deviceSyncedMessage) - .lineLimit(nil) + Text("\(message) \(Text(devicesOnMessageText).bold())") .multilineTextAlignment(.center) - + + if shouldShowOptions { + options() + } } .padding(.horizontal, 20) } foregroundContent: { @@ -97,6 +107,37 @@ public struct DeviceConnectedView: View { .padding(.bottom) } + @ViewBuilder + func options() -> some View { + VStack { + Spacer(minLength: 71) + Text(UserText.options.uppercased()) + .daxFootnoteRegular() + Toggle(isOn: $optionsViewModel.isUnifiedFavoritesEnabled) { + HStack(spacing: 16) { + Image("SyncAllDevices") + VStack(alignment: .leading) { + Text(UserText.unifiedFavoritesTitle) + .foregroundColor(.primary) + .daxBodyRegular() + Text(UserText.unifiedFavoritesInstruction) + .daxCaption() + .foregroundColor(.secondary) + } + } + } + .padding(16) + .background( + RoundedRectangle(cornerRadius: 10) + .fill(.black.opacity(0.01)) + ) + .overlay( + RoundedRectangle(cornerRadius: 10) + .stroke(.black.opacity(0.2), lineWidth: 0.2) + ) + } + } + public var body: some View { if showRecoveryPDF { SaveRecoveryKeyView(model: saveRecoveryKeyViewModel) diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/BackButtonModifier.swift b/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/BackButtonModifier.swift index 17e8d63245..3425a8af5f 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/BackButtonModifier.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/BackButtonModifier.swift @@ -42,3 +42,21 @@ struct BackButtonModifier: ViewModifier { } } + +struct CancelButtonModifier: ViewModifier { + + @Environment(\.presentationMode) var presentation + let action: () -> Void + + func body(content: Content) -> some View { + content + .navigationBarBackButtonHidden() + .toolbar { + ToolbarItem(placement: .navigationBarLeading) { + Button("Cancel", action: action) + .foregroundColor(Color.white) + } + } + } + +} diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/ConnectModeView.swift b/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/ConnectModeView.swift index 354f9fc16d..7ccb491200 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/ConnectModeView.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/ConnectModeView.swift @@ -28,6 +28,8 @@ struct ConnectModeView: View { var body: some View { QRCodeCopierView(model: qrCodeModel) + .padding(.vertical, 20) + .frame(maxWidth: Constants.maxFullScreenWidth) .onAppear { self.qrCodeModel = model.startConnectMode() } diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/QRCodeCopierView.swift b/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/QRCodeCopierView.swift index 4c2a94ae5c..292ae5dd18 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/QRCodeCopierView.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/QRCodeCopierView.swift @@ -18,6 +18,7 @@ // import SwiftUI +import DesignResourcesKit struct QRCodeCopierView: View { @@ -38,7 +39,7 @@ struct QRCodeCopierView: View { VStack(spacing: 20) { Spacer() - QRCodeView(string: code, size: 200) + QRCodeView(string: code, size: 220) .padding() Spacer() @@ -59,6 +60,8 @@ struct QRCodeCopierView: View { if model.code != nil { Text(UserText.viewQRCodeInstructions) + .daxCaption() + .foregroundColor(.secondary) .lineLimit(nil) .multilineTextAlignment(.center) } diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/RemoveDeviceView.swift b/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/RemoveDeviceView.swift index 674a3d961c..b3a95980c7 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/RemoveDeviceView.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/RemoveDeviceView.swift @@ -19,6 +19,7 @@ import SwiftUI import DuckUI +import DesignResourcesKit struct RemoveDeviceView: View { @@ -32,13 +33,13 @@ struct RemoveDeviceView: View { Image(model.imageName) Text(UserText.removeDeviceTitle) - .font(.title) + .daxTitle1() Text(UserText.removeDeviceMessage(model.device.name)) .multilineTextAlignment(.center) .padding(.horizontal, 20) .padding(.top, 20) - .font(.system(size: 14)) + .daxBodyRegular() } .padding(.horizontal, 20) .navigationBarTitleDisplayMode(.inline) diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/SyncLabelButtonStyle.swift b/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/SyncLabelButtonStyle.swift index 433227b6c4..9826ded53e 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/SyncLabelButtonStyle.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/SyncLabelButtonStyle.swift @@ -18,6 +18,7 @@ // import SwiftUI +import DesignResourcesKit struct SyncLabelButtonStyle: ButtonStyle { @@ -36,7 +37,7 @@ struct SyncLabelButtonStyle: ButtonStyle { .frame(height: 44) .background(configuration.isPressed ? backgroundColor.opacity(0.7) : backgroundColor.opacity(1)) .cornerRadius(8) - .font(.system(size: 15, weight: .semibold)) + .daxButton() } diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/UserText.swift b/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/UserText.swift index 24dd4128c7..1fc5c3535b 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/UserText.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/UserText.swift @@ -23,33 +23,48 @@ import Foundation // Localise these later, when feature is closer to exernal release struct UserText { - static let turnOnTitle = "Turn on Sync?" - static let turnOnButton = "Turn on Sync" - static let turnOnMessage = "This will save an encrypted backup of your bookmarks and Autofill logins on DuckDuckGo’s servers, which can be synced with your other devices.\n\nThe decryption key is stored on your device and cannot be read by DuckDuckGo." - - static let syncWithAnotherDeviceTitle = "Sync Another Device?" - static let syncWithAnotherDeviceMessage = "Your bookmarks and Autofill logins will be backed up! Would you like to sync another device now?\n\nIf you’ve already set up Sync on another device, this will allow you to combine bookmarks and Autofill logins from both devices into a single backup." - static let syncWithAnotherDeviceButton = "Sync Another Device" + static let syncWithAnotherDeviceTitle = "Sync with Another Device" + static let syncWithAnotherDeviceMessage = "Securely sync bookmarks and Logins between your devices." static let recoveryMessage = "If you lose access to your devices, you will need this code to recover your synced data." + static let recoveryWarning = "Anyone with access to this code can access your synced data, so please keep it in a safe place." + + static let scanQRCode = "Scan QR Code" + static let enterTextCode = "Enter Text Code" + static let turnSyncOff = "Turn Off Sync & Back Up" + + static let singleDeviceSetUpTitle = "Single-Device Setup" + static let singleDeviceSetUpInstruction = "Set up this device now, sync with other devices later." + static let turnSyncOn = "Start Sync & Back Up" + static let recoverYourData = "Recover Your Data" + + static let options = "Options" + static let unifiedFavoritesTitle = "Share Favorites" + static let unifiedFavoritesInstruction = "Use the same favorites on all devices. Leave off to keep mobile and desktop favorites separate." - static let connectDeviceInstructions = "Go to Settings > Sync in the DuckDuckGo App on a different device and scan the QR code to sync." + static let syncSettingsFooter = "Your data is end-to-end encrypted, and DuckDuckGo does not have access to the decryption key." - static let recoveryModeInstructions = "Scan the QR code on your Recovery PDF, or another synced device, to recover your synced data." + static let connectDeviceInstructions = "Go to Settings > Sync & Back Up in the DuckDuckGo App on a different device and scan the QR code to sync." + + static let recoveryModeInstructions = "Scan the QR code from your Recovery PDF or in the DuckDuckGo app under Settings > Sync & Back Up on a signed-in device." static let validatingCode = "Validating code" - static let validatingCodeFailed = "That code does not appear to be valid. Please try copying it again." + static let validatingCodeFailed = "Invalid code." - static let pasteCodeInstructions = "Copy the code from the Settings > Sync page in the DuckDuckGo App on another synced device and paste it here to sync this device." + static let pasteCodeInstructions = "Copy the code from the\n Settings > Sync & Back Up page in the DuckDuckGo App on another synced device and paste it here to sync this device." - static let viewQRCodeInstructions = "Go to Settings > Sync in the DuckDuckGo App on a different device and scan this QR code to sync." - static let viewQRCodeTitle = "QR Code" + static let viewQRCodeInstructions = "Open the DuckDuckGo app on another device. Navigate to Settings > Sync & Back Up and scan this QR code." + static let viewQRCodeTitle = "Your Sync Code" + static let syngleDeviceConnectedTitle = "All Set!" static let deviceSyncedTitle = "Device Synced!" - static let deviceSyncedMessage = "Your bookmarks and Autofill logins are now syncing with this device." + static let firstDeviceSyncedMessage = "You can sync this device’s bookmarks and Logins with additional devices at any time from the Sync & Back Up menu in Settings." + static let deviceSyncedMessage = "Your bookmarks and Logins are now syncing with " + static let multipleDevicesSyncedMessage = "Your bookmarks and Logins are now syncing on " + static let wordDevices = "devices" - static let saveRecoveryTitle = "Save Recovery Key" - static let saveRecoveryButton = "Save As PDF" + static let saveRecoveryTitle = "Save Recovery Code?" + static let copyCode = "Copy Code" static let cameraPermissionRequired = "Camera Permission is Required" static let cameraPermissionInstructions = "Please go to your device's settings and grant permission for this app to access your camera." @@ -57,8 +72,7 @@ struct UserText { static let goToSettingsButton = "Go to Settings" - static let syncTitle = "Sync" - static let syncSettingsInfo = "Sync your bookmarks and Autofill logins across your devices and save an encrypted backup on DuckDuckGo’s servers." + static let syncTitle = "Sync & Back Up" static let thisDevice = "This Device" static let connectedDevicesTitle = "Synced Devices" @@ -66,14 +80,14 @@ struct UserText { static let pasteLabel = "Paste" static let copyCodeLabel = "Copy Code" - static let manuallyEnterCodeLabel = "Manually Enter Code" - static let manuallyEnterCodeTitle = "Manually Enter Code" + static let manuallyEnterCodeTitle = "Enter Text Code" static let showQRCodeLabel = "Show QR Code" + static let showQRCodeSubLabel = "Display code to scan with another device" - static let recoverDataButton = "Recover Your Synced Data" - - static let settingsNewDeviceInstructions = "Go to Settings > Sync in the DuckDuckGo App on a different device and scan to connect instantly" + static let settingsNewDeviceInstructions1 = "Go to Settings > Sync in the" + static let settingsNewDeviceInstructions2 = "DuckDuckGo App" + static let settingsNewDeviceInstructions3 = "on a different device and scan to connect instantly" static let settingsScanQRCodeButton = "Scan QR Code" static let settingsShowCodeButton = "Show Text Code" static let settingsSaveRecoveryPDFButton = "Save Recovery PDF" @@ -98,5 +112,12 @@ struct UserText { return "\"\(name)\" will no longer be able to access your synced data." } + static let syncLimitExceededTitle = "⚠️ Sync Paused" + static let bookmarksLimitExceededDescription = "Bookmark limit exceeded. Delete some to resume syncing." + static let credentialsLimitExceededDescription = "Logins limit exceeded. Delete some to resume syncing." + static let bookmarksLimitExceededAction = "Manage Bookmarks" + static let credentialsLimitExceededAction = "Manage Logins" + + } // swiftlint:enable line_length diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/PasteCodeView.swift b/LocalPackages/SyncUI/Sources/SyncUI/Views/PasteCodeView.swift similarity index 87% rename from LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/PasteCodeView.swift rename to LocalPackages/SyncUI/Sources/SyncUI/Views/PasteCodeView.swift index 83e62e82ea..2eca97e13b 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Views/Internal/PasteCodeView.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/Views/PasteCodeView.swift @@ -19,8 +19,9 @@ import SwiftUI import DuckUI +import DesignResourcesKit -struct PasteCodeView: View { +public struct PasteCodeView: View { static let codeFontSize = 18.0 static let codeLines = 10 @@ -29,6 +30,13 @@ struct PasteCodeView: View { @State var isEditingCode = false + var isFirstScreen: Bool + + public init(model: ScanOrPasteCodeViewModel, isfirstScreen: Bool = false) { + self.model = model + self.isFirstScreen = isfirstScreen + } + @ViewBuilder func pasteButton() -> some View { Button(action: model.pasteCode) { @@ -96,7 +104,8 @@ struct PasteCodeView: View { .padding() } - var body: some View { + @ViewBuilder + func pastCodeWiewWithNoModifier() -> some View { ZStack(alignment: .top) { VStack(spacing: 20) { codeEntrySection() @@ -106,10 +115,19 @@ struct PasteCodeView: View { } .padding(.horizontal, 20) .navigationTitle(UserText.manuallyEnterCodeTitle) - .modifier(BackButtonModifier()) .ignoresSafeArea(.keyboard) } + public var body: some View { + if isFirstScreen { + pastCodeWiewWithNoModifier() + .modifier(CancelButtonModifier(action: model.cancel)) + } else { + pastCodeWiewWithNoModifier() + .modifier(BackButtonModifier()) + } + } + } private extension String { @@ -146,7 +164,7 @@ struct PasteButtonStyle: ButtonStyle { .frame(height: 44) .background(configuration.isPressed ? backgroundColor.opacity(0.7) : backgroundColor.opacity(1)) .cornerRadius(8) - .font(.system(size: 15, weight: .semibold)) + .daxButton() } diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Views/SaveRecoveryKeyView.swift b/LocalPackages/SyncUI/Sources/SyncUI/Views/SaveRecoveryKeyView.swift index 651a93d683..b2206716a4 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Views/SaveRecoveryKeyView.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/Views/SaveRecoveryKeyView.swift @@ -19,6 +19,7 @@ import SwiftUI import DuckUI +import DesignResourcesKit public struct SaveRecoveryKeyView: View { @@ -56,7 +57,7 @@ public struct SaveRecoveryKeyView: View { .padding(.horizontal, 20) .padding(.bottom, 12) } - .background(RoundedRectangle(cornerRadius: 10).foregroundColor(.black.opacity(0.12))) + .background(RoundedRectangle(cornerRadius: 10).foregroundColor(.black.opacity(0.03))) } @ViewBuilder @@ -67,10 +68,15 @@ public struct SaveRecoveryKeyView: View { } .buttonStyle(PrimaryButtonStyle(compact: isCompact)) - Button("Copy Key") { + Button(UserText.copyCode) { model.copyKey() } .buttonStyle(SecondaryButtonStyle(compact: isCompact)) + .overlay( + RoundedRectangle(cornerRadius: 8) + .inset(by: 0.5) + .stroke(.blue, lineWidth: 1) + ) Button { presentation.wrappedValue.dismiss() @@ -90,17 +96,22 @@ public struct SaveRecoveryKeyView: View { .padding(.bottom, 24) Text(UserText.saveRecoveryTitle) - .font(.system(size: 28, weight: .bold)) + .daxTitle1() .padding(.bottom, 28) Text(UserText.recoveryMessage) .lineLimit(nil) - .font(.system(size: 16)) + .daxBodyRegular() .lineSpacing(1.32) .multilineTextAlignment(.center) .padding(.bottom, 20) recoveryInfo() + .padding(.bottom, 20) + Text(UserText.recoveryWarning) + .daxCaption() + .multilineTextAlignment(.center) + .foregroundColor(.primary.opacity(0.6)) } .padding(.top, isCompact ? 0 : 56) .padding(.horizontal, 30) diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Views/ScanOrPasteCodeView.swift b/LocalPackages/SyncUI/Sources/SyncUI/Views/ScanOrPasteCodeView.swift index 1ff8db037e..5c8a08af55 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Views/ScanOrPasteCodeView.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/Views/ScanOrPasteCodeView.swift @@ -18,6 +18,7 @@ // import SwiftUI +import DesignResourcesKit /// Handles scanning or pasting a code. public struct ScanOrPasteCodeView: View { @@ -49,8 +50,7 @@ public struct ScanOrPasteCodeView: View { } } else { Rectangle() - // White so that the blur / transparent doesn't become too dark - .fill(.white) + .fill(.black) } } .ignoresSafeArea() @@ -73,14 +73,14 @@ public struct ScanOrPasteCodeView: View { .padding(.bottom, 20) Text(UserText.cameraPermissionRequired) - .font(.system(size: 20, weight: .bold)) + .daxTitle3() .lineSpacing(1.05) .padding(.bottom, 8) Text(UserText.cameraPermissionInstructions) .lineLimit(nil) .multilineTextAlignment(.center) - .font(.system(size: 16, weight: .regular)) + .daxBodyRegular() .lineSpacing(1.1) Spacer() @@ -110,7 +110,7 @@ public struct ScanOrPasteCodeView: View { .padding(.bottom, 20) Text(UserText.cameraIsUnavailableTitle) - .font(.system(size: 20, weight: .bold)) + .daxTitle3() .lineSpacing(1.05) } @@ -124,37 +124,55 @@ public struct ScanOrPasteCodeView: View { Text(model.showConnectMode ? UserText.connectDeviceInstructions : UserText.recoveryModeInstructions) .lineLimit(nil) .multilineTextAlignment(.center) - .font(.system(size: 16, weight: .regular)) - .padding(.vertical) + .daxCaption() + .foregroundColor(.white.opacity(0.6)) + .padding(.top, 20) } @ViewBuilder func buttons() -> some View { - Section { - Group { + Group { + Section { NavigationLink { PasteCodeView(model: model) } label: { - Label(UserText.manuallyEnterCodeLabel, image: "SyncKeyboardIcon") + HStack(spacing: 16) { + Image("SyncKeyboardIcon") + Text(UserText.manuallyEnterCodeTitle) + .daxButton() + .foregroundColor(.white.opacity(0.84)) + } } - + } + Section { if model.showConnectMode { NavigationLink { ConnectModeView(model: model) } label: { - Label(UserText.showQRCodeLabel, image: "SyncQRCodeIcon") + HStack(spacing: 16) { + Image("SyncQRCodeIcon") + + VStack(alignment: .leading, spacing: 4) { + Text(UserText.showQRCodeLabel) + .daxButton() + .foregroundColor(.white.opacity(0.84)) + Text(UserText.showQRCodeSubLabel) + .daxCaption() + .foregroundColor(.white.opacity(0.6)) + } + .frame(maxWidth: .infinity, alignment: .leading) + } } } } - .frame(height: 40) - .foregroundColor(.primary) - .onAppear { - model.endConnectMode() - } } - + .frame(height: 40) + .foregroundColor(.primary) + .onAppear { + model.endConnectMode() + } } @ViewBuilder @@ -187,9 +205,8 @@ public struct ScanOrPasteCodeView: View { VStack(spacing: 0) { Rectangle() // Also acts as the blur for the camera - .fill(.clear) + .fill(.black) .frame(height: g.safeAreaInsets.top) - .regularMaterialBackground() ZStack { // Background in case fullscreen camera view doesn't work @@ -210,18 +227,19 @@ public struct ScanOrPasteCodeView: View { ZStack { Rectangle() // Also acts as the blur for the camera - .fill(.clear) + .fill(.black) .regularMaterialBackground() - VStack { - instructions() - .padding(.horizontal, 20) + VStack(spacing: 0) { + Section { + instructions() + .padding(.horizontal, 20) + } List { buttons() } .ignoresSafeArea() - .hideScrollContentBackground() .disableScrolling() } .frame(maxWidth: Constants.maxFullScreenWidth) diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Views/SyncSettingsView.swift b/LocalPackages/SyncUI/Sources/SyncUI/Views/SyncSettingsView.swift index 45b84f2a60..f2fff4bf6c 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Views/SyncSettingsView.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/Views/SyncSettingsView.swift @@ -18,6 +18,7 @@ // import SwiftUI +import DesignResourcesKit public struct SyncSettingsView: View { @@ -29,30 +30,59 @@ public struct SyncSettingsView: View { self.model = model } - @ViewBuilder - func syncToggle() -> some View { - Section { - HStack { - Text(UserText.syncTitle) - Spacer() + public var body: some View { + if model.isSyncingDevices { + SwiftUI.ProgressView() + .onReceive(timer) { _ in + if selectedDevice == nil { + model.delegate?.refreshDevices(clearDevices: false) + } + } + } else { + List { + workInProgress() - if model.isBusy { - SwiftUI.ProgressView() + if model.isSyncEnabled { + + turnOffSync() + + if $model.isSyncBookmarksPaused.wrappedValue { + syncPaused(for: .bookmarks) + } + + if $model.isSyncCredentialsPaused.wrappedValue { + syncPaused(for: .credentials) + } + + devices() + + syncNewDevice() + + OptionsView(isUnifiedFavoritesEnabled: $model.isUnifiedFavoritesEnabled) + .onAppear(perform: { + model.delegate?.updateOptions() + }) + + saveRecoveryPDF() + + deleteAllData() + } else { - Toggle("", isOn: Binding(get: { - return model.isSyncEnabled - }, set: { enabled in - if enabled { - model.enableSync() - } else { - model.disableSync() - } - })) + + syncWithAnotherDeviceView() + + singleDeviceSetUpView() + + recoverYourDataView() + + footerView() } } - } footer: { - Text(UserText.syncSettingsInfo) + .navigationTitle(UserText.syncTitle) + .applyListStyle() + .environmentObject(model) } + } @ViewBuilder @@ -64,6 +94,155 @@ public struct SyncSettingsView: View { @State var selectedDevice: SyncSettingsViewModel.Device? + @ViewBuilder + func workInProgress() -> some View { + Section { + EmptyView() + } footer: { + VStack(alignment: .leading, spacing: 4) { + Text("Work in Progress") + .font(.system(size: 12, weight: .semibold)) + .foregroundColor(.black) + + // swiftlint:disable line_length + Text("This feature is viewable to internal users only and is still being developed and tested. Currently you can create accounts, connect and manage devices, and sync bookmarks, favorites, Autofill logins and Email Protection status. **[More Info](https://app.asana.com/0/1201493110486074/1203756800930481/f)**") + .foregroundColor(.black) + .font(.system(size: 11, weight: .regular)) + // swiftlint:enable line_length + } + .padding() + .background(RoundedRectangle(cornerRadius: 8).foregroundColor(.yellow)) + .padding(.bottom, 10) + } + + } + +} + +// Sync Set up Views +extension SyncSettingsView { + @ViewBuilder + func recoverYourDataView() -> some View { + Section { + Button(UserText.recoverYourData) { + model.showRecoverDataView() + } + } + } + + @ViewBuilder + func footerView() -> some View { + Section {} footer: { + Text(UserText.syncSettingsFooter) + .daxFootnoteRegular() + .foregroundColor(.secondary) + } + } + + @ViewBuilder + func singleDeviceSetUpView() -> some View { + Section { + HStack { + VStack(alignment: .leading, spacing: 4) { + Text(UserText.singleDeviceSetUpTitle) + .daxBodyBold() + Text(UserText.singleDeviceSetUpInstruction) + .daxBodyRegular() + } + Spacer() + Image("Device-Mobile-Upload-96") + + } + if model.isBusy { + SwiftUI.ProgressView() + } else { + Button(UserText.turnSyncOn) { + model.startSyncPressed() + } + } + } + } + + @ViewBuilder + func syncWithAnotherDeviceView() -> some View { + Section { + HStack { + VStack(alignment: .leading, spacing: 4) { + Text(UserText.syncWithAnotherDeviceTitle) + .daxBodyBold() + Text(UserText.syncWithAnotherDeviceMessage) + .daxBodyRegular() + } + Spacer() + Image("Sync-Pair-96") + + } + Button(UserText.scanQRCode) { + model.scanQRCode() + } + Button(UserText.enterTextCode) { + model.showEnterTextView() + } + } + } +} + +// Sync Enabled Views +extension SyncSettingsView { + @ViewBuilder + func deleteAllData() -> some View { + Section { + Button(UserText.settingsDeleteAllButton) { + model.deleteAllData() + } + } + } + + @ViewBuilder + func saveRecoveryPDF() -> some View { + Section { + Button(UserText.settingsSaveRecoveryPDFButton) { + model.saveRecoveryPDF() + } + } footer: { + Text(UserText.settingsRecoveryPDFWarning) + } + } + + @ViewBuilder + func syncNewDevice() -> some View { + Section { + + // Appears off center because the list is padding the trailing to make space for the accessory + VStack(spacing: 0) { + QRCodeView(string: model.recoveryCode, size: 192, style: .dark) + .padding(.bottom, 32) + .padding(.top, 16) + + let instrution1 = Text(UserText.settingsNewDeviceInstructions1) + let instrution2 = Text(UserText.settingsNewDeviceInstructions2).bold() + let instrution3 = Text(UserText.settingsNewDeviceInstructions3) + + Text("\(instrution1)\n \(instrution2) \(instrution3)") + .daxSubheadRegular() + .lineLimit(nil) + .lineSpacing(1.2) + .multilineTextAlignment(.center) + .padding(.bottom, 16) + } + + NavigationLink(UserText.settingsShowCodeButton) { + ShowCodeView(code: model.recoveryCode, copyCode: model.copyCode) + } + + Button(UserText.settingsScanQRCodeButton) { + model.scanQRCode() + } + } header: { + Text("Sync New Device") + } + } + @ViewBuilder func devices() -> some View { Section { @@ -116,101 +295,59 @@ public struct SyncSettingsView: View { } @ViewBuilder - func syncNewDevice() -> some View { + func turnOffSync() -> some View { Section { - - // Appears off center because the list is padding the trailing to make space for the accessory - VStack(spacing: 0) { - QRCodeView(string: model.recoveryCode, size: 192, style: .dark) - .padding(.bottom, 32) - .padding(.top, 16) - - Text(UserText.settingsNewDeviceInstructions) - .font(.system(size: 15)) - .lineLimit(nil) - .lineSpacing(1.2) - .multilineTextAlignment(.center) - .padding(.bottom, 16) - } - - NavigationLink(UserText.settingsShowCodeButton) { - ShowCodeView(code: model.recoveryCode, copyCode: model.copyCode) - } - - Button(UserText.settingsScanQRCodeButton) { - model.scanQRCode() + if model.isBusy { + SwiftUI.ProgressView() + } else { + Button(UserText.turnSyncOff) { + model.disableSync() + } } - } header: { - Text("Sync New Device") } } @ViewBuilder - func saveRecoveryPDF() -> some View { - Section { - Button(UserText.settingsSaveRecoveryPDFButton) { - model.saveRecoveryPDF() + func syncPaused(for itemType: LimitedItemType) -> some View { + var explanation: String { + switch itemType { + case .bookmarks: + return UserText.bookmarksLimitExceededDescription + case .credentials: + return UserText.credentialsLimitExceededDescription } - } footer: { - Text(UserText.settingsRecoveryPDFWarning) } - } - - @ViewBuilder - func deleteAllData() -> some View { - Section { - Button(UserText.settingsDeleteAllButton) { - model.deleteAllData() + var buttonTitle: String { + switch itemType { + case .bookmarks: + return UserText.bookmarksLimitExceededAction + case .credentials: + return UserText.credentialsLimitExceededAction } } - } - @ViewBuilder - func workInProgress() -> some View { Section { - EmptyView() - } footer: { VStack(alignment: .leading, spacing: 4) { - Text("Work in Progress") - .font(.system(size: 12, weight: .semibold)) - .foregroundColor(.black) - - // swiftlint:disable line_length - Text("This feature is viewable to internal users only and is still being developed and tested. Currently you can create accounts, connect and manage devices, and sync bookmarks, favorites, Autofill logins and Email Protection status. **[More Info](https://app.asana.com/0/1201493110486074/1203756800930481/f)**") - .foregroundColor(.black) - .font(.system(size: 11, weight: .regular)) - // swiftlint:enable line_length + Text(UserText.syncLimitExceededTitle) + .daxBodyBold() + Text(explanation) + .daxBodyRegular() } - .padding() - .background(RoundedRectangle(cornerRadius: 8).foregroundColor(.yellow)) - .padding(.bottom, 10) - } - - } - - public var body: some View { - List { - workInProgress() - - syncToggle() - - if model.isSyncEnabled { - devices() - - syncNewDevice() - - saveRecoveryPDF() - - deleteAllData() + Button(buttonTitle) { + switch itemType { + case .bookmarks: + model.manageBookmarks() + case .credentials: + model.manageLogins() + } } - } - .navigationTitle(UserText.syncTitle) - .applyListStyle() - .environmentObject(model) - } + enum LimitedItemType { + case bookmarks + case credentials + } } // Extension to apply custom view modifier @@ -219,3 +356,26 @@ extension View { closure(self) } } + + +public struct OptionsView: View { + @Binding var isUnifiedFavoritesEnabled: Bool + public var body: some View { + Section { + Toggle(isOn: $isUnifiedFavoritesEnabled) { + HStack(spacing: 16) { + Image("SyncAllDevices") + VStack(alignment: .leading) { + Text(UserText.unifiedFavoritesTitle) + .foregroundColor(.primary) + Text(UserText.unifiedFavoritesInstruction) + .daxBodyRegular() + .foregroundColor(.secondary) + } + } + } + } header: { + Text(UserText.options) + } + } +} diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Views/TurnOnSyncView.swift b/LocalPackages/SyncUI/Sources/SyncUI/Views/TurnOnSyncView.swift deleted file mode 100644 index 5081ea549c..0000000000 --- a/LocalPackages/SyncUI/Sources/SyncUI/Views/TurnOnSyncView.swift +++ /dev/null @@ -1,162 +0,0 @@ -// -// TurnOnSyncView.swift -// DuckDuckGo -// -// Copyright © 2023 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import SwiftUI -import DuckUI - -public struct TurnOnSyncView: View { - - @Environment(\.verticalSizeClass) var verticalSizeClass - - var isCompact: Bool { - verticalSizeClass == .compact - } - - @State var turnOnSyncNavigation = false - - @ObservedObject var model: TurnOnSyncViewModel - - public init(model: TurnOnSyncViewModel) { - self.model = model - } - - @ViewBuilder - func turnOnSyncView() -> some View { - ZStack { - NavigationLink(isActive: $turnOnSyncNavigation) { - syncWithAnotherDeviceView() - .padding(.top, verticalSizeClass == .compact ? 8 : 56) - } label: { - EmptyView() - } - - CTAView(imageName: "SyncTurnOnSyncHero", - title: UserText.turnOnTitle, - message: UserText.turnOnMessage, - primaryButtonLabel: UserText.turnOnButton, - secondaryButtonLabel: UserText.recoverDataButton) { - - model.turnOnSyncAction() - turnOnSyncNavigation = true - - } secondaryAction: { - model.recoverDataAction() - } - } - } - - @ViewBuilder - func syncWithAnotherDeviceView() -> some View { - CTAView(imageName: "SyncWithAnotherDeviceHero", - title: UserText.syncWithAnotherDeviceTitle, - message: UserText.syncWithAnotherDeviceMessage, - primaryButtonLabel: UserText.syncWithAnotherDeviceButton, - secondaryButtonLabel: UserText.notNowButton) { - model.syncWithAnotherDeviceAction() - } secondaryAction: { - model.notNowAction() - } - .navigationBarBackButtonHidden(true) - } - - public var body: some View { - NavigationView { - turnOnSyncView() - .toolbar { - ToolbarItem(placement: .cancellationAction) { - Button { - model.cancelAction() - } label: { - Text(UserText.cancelButton) - } - } - } - } - .navigationViewStyle(.stack) - } - -} - -private struct CTAView: View { - - @Environment(\.verticalSizeClass) var verticalSizeClass - - var isCompact: Bool { - verticalSizeClass == .compact - } - - let imageName: String - let title: String - let message: String - let primaryButtonLabel: String - let secondaryButtonLabel: String - let primaryAction: () -> Void - let secondaryAction: () -> Void - - @ViewBuilder - func hero(imageName: String, title: String, text: String) -> some View { - VStack(spacing: verticalSizeClass == .compact ? 12 : 24) { - Image(imageName) - .resizable() - .frame(width: verticalSizeClass == .compact ? 96 : 128, - height: verticalSizeClass == .compact ? 72 : 96) - - Text(title) - .font(.system(size: 28, weight: .bold)) - .padding(.horizontal, 55) - - Text(text) - .font(.system(size: 16, weight: .regular)) - .lineSpacing(3) - .padding(.horizontal, 30) - } - .multilineTextAlignment(.center) - .padding(.top, verticalSizeClass == .compact ? 0 : 20) - } - - @ViewBuilder - func buttons(primaryLabel: String, - secondaryLabel: String, - primaryAction: @escaping () -> Void, - secondaryAction: @escaping () -> Void) -> some View { - VStack(spacing: isCompact ? 4 : 8) { - Button(primaryLabel, action: primaryAction) - .buttonStyle(PrimaryButtonStyle(compact: isCompact)) - - Button(secondaryLabel, action: secondaryAction) - .buttonStyle(SecondaryButtonStyle(compact: isCompact)) - } - .frame(maxWidth: 360) - } - - var body: some View { - UnderflowContainer { - hero(imageName: imageName, - title: title, - text: message) - } foregroundContent: { - buttons(primaryLabel: primaryButtonLabel, - secondaryLabel: secondaryButtonLabel, - primaryAction: primaryAction, - secondaryAction: secondaryAction) - .padding(.horizontal, 8) - } - } - -} diff --git a/LocalPackages/Waitlist/Package.swift b/LocalPackages/Waitlist/Package.swift index 6177b79572..394fad7869 100644 --- a/LocalPackages/Waitlist/Package.swift +++ b/LocalPackages/Waitlist/Package.swift @@ -15,7 +15,7 @@ let package = Package( targets: ["Waitlist", "WaitlistMocks"]) ], dependencies: [ - .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "1.1.1") + .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "2.0.0") ], targets: [ .target( diff --git a/LocalPackages/Waitlist/Sources/Waitlist/Network/ProductWaitlistRequest.swift b/LocalPackages/Waitlist/Sources/Waitlist/Network/ProductWaitlistRequest.swift index 5b9bb09df9..63dab2041f 100644 --- a/LocalPackages/Waitlist/Sources/Waitlist/Network/ProductWaitlistRequest.swift +++ b/LocalPackages/Waitlist/Sources/Waitlist/Network/ProductWaitlistRequest.swift @@ -133,7 +133,7 @@ public class ProductWaitlistRequest: WaitlistRequest { private var endpoint: URL { #if DEBUG - return URL(string: "https://quackdev.duckduckgo.com/api/auth/waitlist/")! + return URL(string: "https://quack.duckduckgo.com/api/auth/waitlist/")! #else return URL(string: "https://quack.duckduckgo.com/api/auth/waitlist/")! #endif diff --git a/LocalPackages/Waitlist/Sources/Waitlist/Views/RoundedButtonStyle.swift b/LocalPackages/Waitlist/Sources/Waitlist/Views/RoundedButtonStyle.swift index 8d75337bd6..835d4c073c 100644 --- a/LocalPackages/Waitlist/Sources/Waitlist/Views/RoundedButtonStyle.swift +++ b/LocalPackages/Waitlist/Sources/Waitlist/Views/RoundedButtonStyle.swift @@ -21,20 +21,46 @@ import SwiftUI public struct RoundedButtonStyle: ButtonStyle { + public enum Style { + case solid + case bordered + } + public let enabled: Bool + private let style: Style - public init(enabled: Bool) { + public init(enabled: Bool, style: Style = .solid) { self.enabled = enabled + self.style = style } public func makeBody(configuration: Self.Configuration) -> some View { - configuration.label + let backgroundColor: Color + let foregroundColor: Color + let borderColor: Color + let borderWidth: CGFloat + + switch style { + case .solid: + backgroundColor = enabled ? Color.waitlistBlue : Color.waitlistBlue.opacity(0.2) + foregroundColor = Color.waitlistButtonText + borderColor = Color.clear + borderWidth = 0 + case .bordered: + backgroundColor = Color.clear + foregroundColor = Color.waitlistBlue + borderColor = Color.waitlistBlue + borderWidth = 2 + } + + return configuration.label .daxHeadline() .frame(maxWidth: .infinity) .padding([.top, .bottom], 16) - .background(enabled ? Color.waitlistBlue : Color.waitlistBlue.opacity(0.2)) - .foregroundColor(.waitlistButtonText) + .background(backgroundColor) + .foregroundColor(foregroundColor) .clipShape(RoundedRectangle(cornerRadius: 8)) + .overlay(RoundedRectangle(cornerRadius: 8).strokeBorder(borderColor, lineWidth: borderWidth)) } } diff --git a/LocalPackages/Waitlist/Sources/Waitlist/Waitlist.swift b/LocalPackages/Waitlist/Sources/Waitlist/Waitlist.swift index 0e4d53d398..2d2e0bf270 100644 --- a/LocalPackages/Waitlist/Sources/Waitlist/Waitlist.swift +++ b/LocalPackages/Waitlist/Sources/Waitlist/Waitlist.swift @@ -30,7 +30,7 @@ public protocol WaitlistConstants { static var backgroundRefreshTaskIdentifier: String { get } static var minimumConfigurationRefreshInterval: TimeInterval { get } - static var notificationIdentitier: String { get } + static var notificationIdentifier: String { get } static var inviteAvailableNotificationTitle: String { get } static var inviteAvailableNotificationBody: String { get } } @@ -189,7 +189,7 @@ public extension Waitlist { notificationContent.title = Self.inviteAvailableNotificationTitle notificationContent.body = Self.inviteAvailableNotificationBody - let notificationIdentifier = Self.notificationIdentitier + let notificationIdentifier = Self.notificationIdentifier let request = UNNotificationRequest(identifier: notificationIdentifier, content: notificationContent, trigger: nil) UNUserNotificationCenter.current().add(request) diff --git a/LocalPackages/Waitlist/Sources/WaitlistMocks/TestWaitlist.swift b/LocalPackages/Waitlist/Sources/WaitlistMocks/TestWaitlist.swift index 9bc1059a64..51236cdf43 100644 --- a/LocalPackages/Waitlist/Sources/WaitlistMocks/TestWaitlist.swift +++ b/LocalPackages/Waitlist/Sources/WaitlistMocks/TestWaitlist.swift @@ -42,7 +42,7 @@ public struct TestWaitlist: Waitlist { public static var backgroundTaskName: String = "BG Task" public static var backgroundRefreshTaskIdentifier: String = "bgtask" - public static var notificationIdentitier: String = "notification" + public static var notificationIdentifier: String = "notification" public static var inviteAvailableNotificationTitle: String = "Title" public static var inviteAvailableNotificationBody: String = "Body" } diff --git a/PacketTunnelProvider/NetworkProtection/NetworkProtectionPacketTunnelProvider.swift b/PacketTunnelProvider/NetworkProtection/NetworkProtectionPacketTunnelProvider.swift index 1c998bf732..63809c0af8 100644 --- a/PacketTunnelProvider/NetworkProtection/NetworkProtectionPacketTunnelProvider.swift +++ b/PacketTunnelProvider/NetworkProtection/NetworkProtectionPacketTunnelProvider.swift @@ -17,16 +17,21 @@ // limitations under the License. // +#if NETWORK_PROTECTION + import Foundation -import NetworkProtection import Common +import Combine import Core import Networking import NetworkExtension +import NetworkProtection // Initial implementation for initial Network Protection tests. Will be fleshed out with https://app.asana.com/0/1203137811378537/1204630829332227/f final class NetworkProtectionPacketTunnelProvider: PacketTunnelProvider { + private var cancellables = Set() + // MARK: - PacketTunnelProvider.Event reporting private static var packetTunnelProviderEvents: EventMapping = .init { event, _, _, _ in @@ -137,6 +142,10 @@ final class NetworkProtectionPacketTunnelProvider: PacketTunnelProvider { pixelError = error case .failedToRetrieveAuthToken: return + case .failedToFetchLocationList: + return + case .failedToParseLocationListResponse: + return } DailyPixel.fireDailyAndCount(pixel: pixelEvent, error: pixelError, withAdditionalParameters: params) } @@ -169,15 +178,22 @@ final class NetworkProtectionPacketTunnelProvider: PacketTunnelProvider { errorEvents: nil) let errorStore = NetworkProtectionTunnelErrorStore() let notificationsPresenter = NetworkProtectionUNNotificationPresenter() + let notificationsSettingsStore = NetworkProtectionNotificationsSettingsUserDefaultsStore(userDefaults: .networkProtectionGroupDefaults) + let nofificationsPresenterDecorator = NetworkProtectionNotificationsPresenterTogglableDecorator( + notificationSettingsStore: notificationsSettingsStore, + wrappee: notificationsPresenter + ) notificationsPresenter.requestAuthorization() - super.init(notificationsPresenter: notificationsPresenter, + super.init(notificationsPresenter: nofificationsPresenterDecorator, tunnelHealthStore: NetworkProtectionTunnelHealthStore(), controllerErrorStore: errorStore, keychainType: .dataProtection(.unspecified), tokenStore: tokenStore, debugEvents: Self.networkProtectionDebugEvents(controllerErrorStore: errorStore), - providerEvents: Self.packetTunnelProviderEvents) + providerEvents: Self.packetTunnelProviderEvents, + tunnelSettings: TunnelSettings(defaults: .networkProtectionGroupDefaults)) startMonitoringMemoryPressureEvents() + observeServerChanges() APIRequest.Headers.setUserAgent(DefaultUserAgentManager.duckDuckGoUserAgent) } @@ -204,4 +220,14 @@ final class NetworkProtectionPacketTunnelProvider: PacketTunnelProvider { source.resume() } } + + private func observeServerChanges() { + lastSelectedServerInfoPublisher.sink { server in + let location = server?.serverLocation ?? "Unknown Location" + UserDefaults.networkProtectionGroupDefaults.set(location, forKey: NetworkProtectionUserDefaultKeys.lastSelectedServer) + } + .store(in: &cancellables) + } } + +#endif diff --git a/PacketTunnelProvider/PacketTunnelProvider.entitlements b/PacketTunnelProvider/PacketTunnelProvider.entitlements index 86c503b63c..5e171bb76b 100644 --- a/PacketTunnelProvider/PacketTunnelProvider.entitlements +++ b/PacketTunnelProvider/PacketTunnelProvider.entitlements @@ -9,6 +9,7 @@ com.apple.security.application-groups $(GROUP_ID_PREFIX).apptp + $(GROUP_ID_PREFIX).netp
diff --git a/PacketTunnelProvider/UserText.swift b/PacketTunnelProvider/UserText.swift index aa236db2e1..820a4a6ebf 100644 --- a/PacketTunnelProvider/UserText.swift +++ b/PacketTunnelProvider/UserText.swift @@ -28,7 +28,13 @@ final class UserText { static let networkProtectionConnectionSuccessNotificationBody = NSLocalizedString("network.protection.success.notification.body", value: "Network Protection is On. Your location and online activity are protected.", comment: "The body of the notification shown when Network Protection reconnects successfully") - static func networkProtectionConnectionSuccessNotificationBody(serverLocation: String) -> String { NSLocalizedString("network.protection.success.notification.subtitle.including.serverLocation", value: "Routing device traffic through \(serverLocation).", comment: "The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter") + static func networkProtectionConnectionSuccessNotificationBody(serverLocation: String) -> String { + let localized = NSLocalizedString( + "network.protection.success.notification.subtitle.including.serverLocation", + value: "Routing device traffic through %@.", + comment: "The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter" + ) + return String(format: localized, serverLocation) } static let networkProtectionConnectionInterruptedNotificationBody = NSLocalizedString("network.protection.interrupted.notification.body", value: "Network Protection was interrupted. Attempting to reconnect now...", comment: "The body of the notification shown when Network Protection's connection is interrupted") diff --git a/PacketTunnelProvider/bg.lproj/InfoPlist.strings b/PacketTunnelProvider/bg.lproj/InfoPlist.strings new file mode 100644 index 0000000000..0c817afd04 --- /dev/null +++ b/PacketTunnelProvider/bg.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "PacketTunnelProvider"; + +/* Bundle name */ +"CFBundleName" = "PacketTunnelProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2023 DuckDuckGo. Всички права запазени."; + diff --git a/PacketTunnelProvider/bg.lproj/Localizable.strings b/PacketTunnelProvider/bg.lproj/Localizable.strings index a71a759fe3..f27738834d 100644 --- a/PacketTunnelProvider/bg.lproj/Localizable.strings +++ b/PacketTunnelProvider/bg.lproj/Localizable.strings @@ -1,7 +1,15 @@ -/* - Localizable.strings - DuckDuckGo +/* The body of the notification shown when Network Protection fails to reconnect */ +"network.protection.failure.notification.body" = "Неуспешно свързване на мрежовата защита. Моля, опитайте отново по-късно."; + +/* The body of the notification shown when Network Protection's connection is interrupted */ +"network.protection.interrupted.notification.body" = "Мрежовата защита е прекъсната. Извършва се опит за повторно свързване..."; + +/* The title of the notifications shown from Network Protection */ +"network.protection.notification.title" = "DuckDuckGo"; + +/* The body of the notification shown when Network Protection reconnects successfully */ +"network.protection.success.notification.body" = "Мрежовата защита е включена. Вашето местоположение и онлайн активност са защитени."; + +/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.including.serverLocation" = "Трафикът на устройството се маршрутизира през %@."; - Created by Graeme Arthur on 29/09/2023. - Copyright © 2023 DuckDuckGo. All rights reserved. -*/ diff --git a/PacketTunnelProvider/cs.lproj/InfoPlist.strings b/PacketTunnelProvider/cs.lproj/InfoPlist.strings new file mode 100644 index 0000000000..d2e68f59e8 --- /dev/null +++ b/PacketTunnelProvider/cs.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "PacketTunnelProvider"; + +/* Bundle name */ +"CFBundleName" = "PacketTunnelProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2023 DuckDuckGo. Všechna práva vyhrazena."; + diff --git a/PacketTunnelProvider/cs.lproj/Localizable.strings b/PacketTunnelProvider/cs.lproj/Localizable.strings index a71a759fe3..145708747e 100644 --- a/PacketTunnelProvider/cs.lproj/Localizable.strings +++ b/PacketTunnelProvider/cs.lproj/Localizable.strings @@ -1,7 +1,15 @@ -/* - Localizable.strings - DuckDuckGo +/* The body of the notification shown when Network Protection fails to reconnect */ +"network.protection.failure.notification.body" = "Ochrana sítě se nepřipojila. Zkus to znovu později."; + +/* The body of the notification shown when Network Protection's connection is interrupted */ +"network.protection.interrupted.notification.body" = "Ochrana sítě je přerušená. Probíhá pokus o opětovné připojení..."; + +/* The title of the notifications shown from Network Protection */ +"network.protection.notification.title" = "DuckDuckGo"; + +/* The body of the notification shown when Network Protection reconnects successfully */ +"network.protection.success.notification.body" = "Ochrana sítě je zapnutá. Tvoje poloha a online aktivita jsou chráněné."; + +/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.including.serverLocation" = "Směrování provozu zařízení přes %@."; - Created by Graeme Arthur on 29/09/2023. - Copyright © 2023 DuckDuckGo. All rights reserved. -*/ diff --git a/PacketTunnelProvider/da.lproj/InfoPlist.strings b/PacketTunnelProvider/da.lproj/InfoPlist.strings new file mode 100644 index 0000000000..4f7da7af43 --- /dev/null +++ b/PacketTunnelProvider/da.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "PacketTunnelProvider"; + +/* Bundle name */ +"CFBundleName" = "PacketTunnelProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2023 DuckDuckGo. Alle rettigheder forbeholdes."; + diff --git a/PacketTunnelProvider/da.lproj/Localizable.strings b/PacketTunnelProvider/da.lproj/Localizable.strings index a71a759fe3..22eaacd202 100644 --- a/PacketTunnelProvider/da.lproj/Localizable.strings +++ b/PacketTunnelProvider/da.lproj/Localizable.strings @@ -1,7 +1,15 @@ -/* - Localizable.strings - DuckDuckGo +/* The body of the notification shown when Network Protection fails to reconnect */ +"network.protection.failure.notification.body" = "Netværksbeskyttelse kunne ikke oprette forbindelse. Prøv igen senere."; + +/* The body of the notification shown when Network Protection's connection is interrupted */ +"network.protection.interrupted.notification.body" = "Netværksbeskyttelse blev afbrudt. Forsøger at genoprette forbindelse ..."; + +/* The title of the notifications shown from Network Protection */ +"network.protection.notification.title" = "DuckDuckGo"; + +/* The body of the notification shown when Network Protection reconnects successfully */ +"network.protection.success.notification.body" = "Netværksbeskyttelse er TIL. Din placering og onlineaktivitet er beskyttet."; + +/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.including.serverLocation" = "Router enhedens trafik gennem %@."; - Created by Graeme Arthur on 29/09/2023. - Copyright © 2023 DuckDuckGo. All rights reserved. -*/ diff --git a/PacketTunnelProvider/de.lproj/InfoPlist.strings b/PacketTunnelProvider/de.lproj/InfoPlist.strings new file mode 100644 index 0000000000..58524ab765 --- /dev/null +++ b/PacketTunnelProvider/de.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "PacketTunnelProvider"; + +/* Bundle name */ +"CFBundleName" = "PacketTunnelProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2023 DuckDuckGo. Alle Rechte vorbehalten."; + diff --git a/PacketTunnelProvider/de.lproj/Localizable.strings b/PacketTunnelProvider/de.lproj/Localizable.strings index a71a759fe3..70c3d9ce7b 100644 --- a/PacketTunnelProvider/de.lproj/Localizable.strings +++ b/PacketTunnelProvider/de.lproj/Localizable.strings @@ -1,7 +1,15 @@ -/* - Localizable.strings - DuckDuckGo +/* The body of the notification shown when Network Protection fails to reconnect */ +"network.protection.failure.notification.body" = "Network Protection konnte keine Verbindung herstellen. Bitte versuche es zu einem späteren Zeitpunkt erneut."; + +/* The body of the notification shown when Network Protection's connection is interrupted */ +"network.protection.interrupted.notification.body" = "Network Protection wurde unterbrochen. Versuche jetzt, die Verbindung wiederherzustellen ..."; + +/* The title of the notifications shown from Network Protection */ +"network.protection.notification.title" = "DuckDuckGo"; + +/* The body of the notification shown when Network Protection reconnects successfully */ +"network.protection.success.notification.body" = "Network Protection ist aktiviert.Dein Standort und deine Online-Aktivitäten sind geschützt."; + +/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.including.serverLocation" = "Geräteverkehr wird über %@ geleitet."; - Created by Graeme Arthur on 29/09/2023. - Copyright © 2023 DuckDuckGo. All rights reserved. -*/ diff --git a/PacketTunnelProvider/el.lproj/InfoPlist.strings b/PacketTunnelProvider/el.lproj/InfoPlist.strings new file mode 100644 index 0000000000..bed0624dae --- /dev/null +++ b/PacketTunnelProvider/el.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "PacketTunnelProvider"; + +/* Bundle name */ +"CFBundleName" = "PacketTunnelProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Πνευματικά δικαιώματα © 2023 DuckDuckGo. Με επιφύλαξη κάθε νόμιμου δικαιώματος."; + diff --git a/PacketTunnelProvider/el.lproj/Localizable.strings b/PacketTunnelProvider/el.lproj/Localizable.strings index a71a759fe3..3a9739872d 100644 --- a/PacketTunnelProvider/el.lproj/Localizable.strings +++ b/PacketTunnelProvider/el.lproj/Localizable.strings @@ -1,7 +1,15 @@ -/* - Localizable.strings - DuckDuckGo +/* The body of the notification shown when Network Protection fails to reconnect */ +"network.protection.failure.notification.body" = "Η σύνδεση της Προστασίας δικτύου απέτυχε. Ξαναδοκιμάστε αργότερα."; + +/* The body of the notification shown when Network Protection's connection is interrupted */ +"network.protection.interrupted.notification.body" = "Η Προστασία δικτύου διακόπηκε. Γίνεται προσπάθεια επανασύνδεσης τώρα..."; + +/* The title of the notifications shown from Network Protection */ +"network.protection.notification.title" = "DuckDuckGo"; + +/* The body of the notification shown when Network Protection reconnects successfully */ +"network.protection.success.notification.body" = "Η Προστασία δικτύου είναι Ενεργή. Η τοποθεσία και η διαδικτυακή δραστηριότητά σας προστατεύονται."; + +/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.including.serverLocation" = "Δρομολόγηση κυκλοφορίας της συσκευής μέσω %@."; - Created by Graeme Arthur on 29/09/2023. - Copyright © 2023 DuckDuckGo. All rights reserved. -*/ diff --git a/PacketTunnelProvider/en.lproj/Localizable.strings b/PacketTunnelProvider/en.lproj/Localizable.strings index 378141dfd9..1a6d5fe4de 100644 --- a/PacketTunnelProvider/en.lproj/Localizable.strings +++ b/PacketTunnelProvider/en.lproj/Localizable.strings @@ -11,5 +11,5 @@ "network.protection.success.notification.body" = "Network Protection is On. Your location and online activity are protected."; /* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ -"network.protection.success.notification.subtitle.including.serverLocation" = "Routing device traffic through \(serverLocation)."; +"network.protection.success.notification.subtitle.including.serverLocation" = "Routing device traffic through %@."; diff --git a/PacketTunnelProvider/es.lproj/InfoPlist.strings b/PacketTunnelProvider/es.lproj/InfoPlist.strings new file mode 100644 index 0000000000..3463c009cc --- /dev/null +++ b/PacketTunnelProvider/es.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "PacketTunnelProvider"; + +/* Bundle name */ +"CFBundleName" = "PacketTunnelProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2023 DuckDuckGo. Todos los derechos reservados."; + diff --git a/PacketTunnelProvider/es.lproj/Localizable.strings b/PacketTunnelProvider/es.lproj/Localizable.strings index a71a759fe3..38b8cad08c 100644 --- a/PacketTunnelProvider/es.lproj/Localizable.strings +++ b/PacketTunnelProvider/es.lproj/Localizable.strings @@ -1,7 +1,15 @@ -/* - Localizable.strings - DuckDuckGo +/* The body of the notification shown when Network Protection fails to reconnect */ +"network.protection.failure.notification.body" = "No se ha podido conectar la protección de red. Inténtalo de nuevo más tarde."; + +/* The body of the notification shown when Network Protection's connection is interrupted */ +"network.protection.interrupted.notification.body" = "La protección de red se ha interrumpido. Intentando volver a conectar ahora..."; + +/* The title of the notifications shown from Network Protection */ +"network.protection.notification.title" = "DuckDuckGo"; + +/* The body of the notification shown when Network Protection reconnects successfully */ +"network.protection.success.notification.body" = "La protección de red está activada. Tu ubicación y actividad en línea están protegidas."; + +/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.including.serverLocation" = "Redirigiendo el tráfico de dispositivos a través de %@."; - Created by Graeme Arthur on 29/09/2023. - Copyright © 2023 DuckDuckGo. All rights reserved. -*/ diff --git a/PacketTunnelProvider/et.lproj/InfoPlist.strings b/PacketTunnelProvider/et.lproj/InfoPlist.strings new file mode 100644 index 0000000000..e08ac131bf --- /dev/null +++ b/PacketTunnelProvider/et.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "PacketTunnelProvider"; + +/* Bundle name */ +"CFBundleName" = "PacketTunnelProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Autoriõigus © 2023 DuckDuckGo. Kõik õigused kaitstud."; + diff --git a/PacketTunnelProvider/et.lproj/Localizable.strings b/PacketTunnelProvider/et.lproj/Localizable.strings index a71a759fe3..d860747f57 100644 --- a/PacketTunnelProvider/et.lproj/Localizable.strings +++ b/PacketTunnelProvider/et.lproj/Localizable.strings @@ -1,7 +1,15 @@ -/* - Localizable.strings - DuckDuckGo +/* The body of the notification shown when Network Protection fails to reconnect */ +"network.protection.failure.notification.body" = "Network Protectioni ühenduse loomine nurjus. Proovi hiljem uuesti."; + +/* The body of the notification shown when Network Protection's connection is interrupted */ +"network.protection.interrupted.notification.body" = "Network Protection katkestati. Proovin uuesti ühendust luua ..."; + +/* The title of the notifications shown from Network Protection */ +"network.protection.notification.title" = "DuckDuckGo"; + +/* The body of the notification shown when Network Protection reconnects successfully */ +"network.protection.success.notification.body" = "Network Protection on sisse lülitatud. Sinu asukoht ja võrgutegevus on kaitstud."; + +/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.including.serverLocation" = "Seadme liiklus suunatakse läbi %@."; - Created by Graeme Arthur on 29/09/2023. - Copyright © 2023 DuckDuckGo. All rights reserved. -*/ diff --git a/PacketTunnelProvider/fi.lproj/InfoPlist.strings b/PacketTunnelProvider/fi.lproj/InfoPlist.strings new file mode 100644 index 0000000000..00e71c25a6 --- /dev/null +++ b/PacketTunnelProvider/fi.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "PacketTunnelProvider"; + +/* Bundle name */ +"CFBundleName" = "PacketTunnelProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2023 DuckDuckGo. Kaikki oikeudet pidätetään."; + diff --git a/PacketTunnelProvider/fi.lproj/Localizable.strings b/PacketTunnelProvider/fi.lproj/Localizable.strings index a71a759fe3..7229a827ff 100644 --- a/PacketTunnelProvider/fi.lproj/Localizable.strings +++ b/PacketTunnelProvider/fi.lproj/Localizable.strings @@ -1,7 +1,15 @@ -/* - Localizable.strings - DuckDuckGo +/* The body of the notification shown when Network Protection fails to reconnect */ +"network.protection.failure.notification.body" = "Network Protection ei onnistunut muodostamaan yhteyttä. Yritä myöhemmin uudelleen."; + +/* The body of the notification shown when Network Protection's connection is interrupted */ +"network.protection.interrupted.notification.body" = "Network Protection keskeytettiin. Yritetään muodostaa yhteys uudelleen..."; + +/* The title of the notifications shown from Network Protection */ +"network.protection.notification.title" = "DuckDuckGo"; + +/* The body of the notification shown when Network Protection reconnects successfully */ +"network.protection.success.notification.body" = "Network Protection on käytössä. Sijaintisi ja verkkotoimintasi on suojattu."; + +/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.including.serverLocation" = "Laitteen liikenne reititetään %@ kautta."; - Created by Graeme Arthur on 29/09/2023. - Copyright © 2023 DuckDuckGo. All rights reserved. -*/ diff --git a/PacketTunnelProvider/fr.lproj/InfoPlist.strings b/PacketTunnelProvider/fr.lproj/InfoPlist.strings new file mode 100644 index 0000000000..a5fb93d3c5 --- /dev/null +++ b/PacketTunnelProvider/fr.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "FournisseurTunnelPaquets"; + +/* Bundle name */ +"CFBundleName" = "FournisseurTunnelPaquets"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2023 DuckDuckGo. Tous droits réservés."; + diff --git a/PacketTunnelProvider/fr.lproj/Localizable.strings b/PacketTunnelProvider/fr.lproj/Localizable.strings index a71a759fe3..379cb2eb53 100644 --- a/PacketTunnelProvider/fr.lproj/Localizable.strings +++ b/PacketTunnelProvider/fr.lproj/Localizable.strings @@ -1,7 +1,15 @@ -/* - Localizable.strings - DuckDuckGo +/* The body of the notification shown when Network Protection fails to reconnect */ +"network.protection.failure.notification.body" = "Échec de la connexion à Network Protection. Veuillez réessayer plus tard."; + +/* The body of the notification shown when Network Protection's connection is interrupted */ +"network.protection.interrupted.notification.body" = "Interruption de Network Protection. Tentative de reconnexion en cours…"; + +/* The title of the notifications shown from Network Protection */ +"network.protection.notification.title" = "DuckDuckGo"; + +/* The body of the notification shown when Network Protection reconnects successfully */ +"network.protection.success.notification.body" = "Network Protection est activé. Votre emplacement et votre activité en ligne sont protégés."; + +/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.including.serverLocation" = "Routage du trafic de l'appareil via %@."; - Created by Graeme Arthur on 29/09/2023. - Copyright © 2023 DuckDuckGo. All rights reserved. -*/ diff --git a/PacketTunnelProvider/hr.lproj/InfoPlist.strings b/PacketTunnelProvider/hr.lproj/InfoPlist.strings new file mode 100644 index 0000000000..7c5fb5319f --- /dev/null +++ b/PacketTunnelProvider/hr.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "PacketTunnelProvider"; + +/* Bundle name */ +"CFBundleName" = "PacketTunnelProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Autorska prava © 2023. DuckDuckGo. Sva prava pridržana."; + diff --git a/PacketTunnelProvider/hr.lproj/Localizable.strings b/PacketTunnelProvider/hr.lproj/Localizable.strings index a71a759fe3..a77b81dfb9 100644 --- a/PacketTunnelProvider/hr.lproj/Localizable.strings +++ b/PacketTunnelProvider/hr.lproj/Localizable.strings @@ -1,7 +1,15 @@ -/* - Localizable.strings - DuckDuckGo +/* The body of the notification shown when Network Protection fails to reconnect */ +"network.protection.failure.notification.body" = "Mrežna zaštita nije se uspjela povezati. Pokušaj ponovno nešto kasnije."; + +/* The body of the notification shown when Network Protection's connection is interrupted */ +"network.protection.interrupted.notification.body" = "Mrežna zaštita je prekinuta. Sada se pokušava ponovno povezati..."; + +/* The title of the notifications shown from Network Protection */ +"network.protection.notification.title" = "DuckDuckGo"; + +/* The body of the notification shown when Network Protection reconnects successfully */ +"network.protection.success.notification.body" = "Uključena je mrežna zaštita. Tvoja lokacija i online aktivnost su zaštićeni."; + +/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.including.serverLocation" = "Usmjeravanje prometa uređaja kroz %@."; - Created by Graeme Arthur on 29/09/2023. - Copyright © 2023 DuckDuckGo. All rights reserved. -*/ diff --git a/PacketTunnelProvider/hu.lproj/InfoPlist.strings b/PacketTunnelProvider/hu.lproj/InfoPlist.strings new file mode 100644 index 0000000000..171950422a --- /dev/null +++ b/PacketTunnelProvider/hu.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "PacketTunnelProvider"; + +/* Bundle name */ +"CFBundleName" = "PacketTunnelProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Szerzői jog © 2023 DuckDuckGo. Minden jog fenntartva."; + diff --git a/PacketTunnelProvider/hu.lproj/Localizable.strings b/PacketTunnelProvider/hu.lproj/Localizable.strings index a71a759fe3..ed29f50ed8 100644 --- a/PacketTunnelProvider/hu.lproj/Localizable.strings +++ b/PacketTunnelProvider/hu.lproj/Localizable.strings @@ -1,7 +1,15 @@ -/* - Localizable.strings - DuckDuckGo +/* The body of the notification shown when Network Protection fails to reconnect */ +"network.protection.failure.notification.body" = "A hálózatvédelmi funkció csatlakozása sikertelen. Próbálkozz újra később."; + +/* The body of the notification shown when Network Protection's connection is interrupted */ +"network.protection.interrupted.notification.body" = "A hálózatvédelmi funkció kapcsolata megszakadt. Újracsatlakozási kísérlet folyamatban…"; + +/* The title of the notifications shown from Network Protection */ +"network.protection.notification.title" = "DuckDuckGo"; + +/* The body of the notification shown when Network Protection reconnects successfully */ +"network.protection.success.notification.body" = "A hálózatvédelem be van kapcsolva. A tartózkodási helyed és online tevékenységed védelme aktív."; + +/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.including.serverLocation" = "Az eszköz forgalmának átirányítási helyszíne: %@."; - Created by Graeme Arthur on 29/09/2023. - Copyright © 2023 DuckDuckGo. All rights reserved. -*/ diff --git a/PacketTunnelProvider/it.lproj/InfoPlist.strings b/PacketTunnelProvider/it.lproj/InfoPlist.strings new file mode 100644 index 0000000000..e0743b97ec --- /dev/null +++ b/PacketTunnelProvider/it.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "PacketTunnelProvider"; + +/* Bundle name */ +"CFBundleName" = "PacketTunnelProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2023 DuckDuckGo. Tutti i diritti riservati."; + diff --git a/PacketTunnelProvider/it.lproj/Localizable.strings b/PacketTunnelProvider/it.lproj/Localizable.strings index a71a759fe3..a0253dd368 100644 --- a/PacketTunnelProvider/it.lproj/Localizable.strings +++ b/PacketTunnelProvider/it.lproj/Localizable.strings @@ -1,7 +1,15 @@ -/* - Localizable.strings - DuckDuckGo +/* The body of the notification shown when Network Protection fails to reconnect */ +"network.protection.failure.notification.body" = "Connessione di Network Protection non riuscita. Riprova più tardi."; + +/* The body of the notification shown when Network Protection's connection is interrupted */ +"network.protection.interrupted.notification.body" = "La funzione Network Protection è stata interrotta. Tentativo di riconnessione in corso..."; + +/* The title of the notifications shown from Network Protection */ +"network.protection.notification.title" = "DuckDuckGo"; + +/* The body of the notification shown when Network Protection reconnects successfully */ +"network.protection.success.notification.body" = "Network Protection è attiva. La tua posizione e le tue attività online sono protette."; + +/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.including.serverLocation" = "Instradamento del traffico del dispositivo tramite %@."; - Created by Graeme Arthur on 29/09/2023. - Copyright © 2023 DuckDuckGo. All rights reserved. -*/ diff --git a/PacketTunnelProvider/lt.lproj/InfoPlist.strings b/PacketTunnelProvider/lt.lproj/InfoPlist.strings new file mode 100644 index 0000000000..8cfd5f6ad6 --- /dev/null +++ b/PacketTunnelProvider/lt.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "PacketTunnelProvider"; + +/* Bundle name */ +"CFBundleName" = "PacketTunnelProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Autorių teisės © „DuckDuckGo“, 2023. Visos teisės saugomos."; + diff --git a/PacketTunnelProvider/lt.lproj/Localizable.strings b/PacketTunnelProvider/lt.lproj/Localizable.strings index a71a759fe3..64a0b1caff 100644 --- a/PacketTunnelProvider/lt.lproj/Localizable.strings +++ b/PacketTunnelProvider/lt.lproj/Localizable.strings @@ -1,7 +1,15 @@ -/* - Localizable.strings - DuckDuckGo +/* The body of the notification shown when Network Protection fails to reconnect */ +"network.protection.failure.notification.body" = "Nepavyko prisijungti prie tinklo apsaugos. Pabandykite dar kartą vėliau."; + +/* The body of the notification shown when Network Protection's connection is interrupted */ +"network.protection.interrupted.notification.body" = "Tinklo apsauga buvo nutraukta. Dabar bandome prisijungti..."; + +/* The title of the notifications shown from Network Protection */ +"network.protection.notification.title" = "„DuckDuckGo“"; + +/* The body of the notification shown when Network Protection reconnects successfully */ +"network.protection.success.notification.body" = "Tinklo apsauga įjungta. Jūsų vieta ir veikla internete yra apsaugota."; + +/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.including.serverLocation" = "Nukreipiamas įrenginio srautas per %@."; - Created by Graeme Arthur on 29/09/2023. - Copyright © 2023 DuckDuckGo. All rights reserved. -*/ diff --git a/PacketTunnelProvider/lv.lproj/InfoPlist.strings b/PacketTunnelProvider/lv.lproj/InfoPlist.strings new file mode 100644 index 0000000000..b9f147c4d7 --- /dev/null +++ b/PacketTunnelProvider/lv.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "PacketTunnelProvider"; + +/* Bundle name */ +"CFBundleName" = "PacketTunnelProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Autortiesības © 2023. g. DuckDuckGo. Visas tiesības aizsargātas."; + diff --git a/PacketTunnelProvider/lv.lproj/Localizable.strings b/PacketTunnelProvider/lv.lproj/Localizable.strings index a71a759fe3..6d6aea9404 100644 --- a/PacketTunnelProvider/lv.lproj/Localizable.strings +++ b/PacketTunnelProvider/lv.lproj/Localizable.strings @@ -1,7 +1,15 @@ -/* - Localizable.strings - DuckDuckGo +/* The body of the notification shown when Network Protection fails to reconnect */ +"network.protection.failure.notification.body" = "Tīkla aizsardzībai neizdevās izveidot savienojumu. Lūdzu, mēģini vēlreiz vēlāk."; + +/* The body of the notification shown when Network Protection's connection is interrupted */ +"network.protection.interrupted.notification.body" = "Tīkla aizsardzība tika pārtraukta. Tagad tiek mēģināts atkārtoti izveidot savienojumu..."; + +/* The title of the notifications shown from Network Protection */ +"network.protection.notification.title" = "DuckDuckGo"; + +/* The body of the notification shown when Network Protection reconnects successfully */ +"network.protection.success.notification.body" = "Tīkla aizsardzība ir ieslēgta. Tava atrašanās vieta un darbības tiešsaistē ir aizsargātas."; + +/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.including.serverLocation" = "Ierīces datplūsma tiek maršrutēta caur: %@."; - Created by Graeme Arthur on 29/09/2023. - Copyright © 2023 DuckDuckGo. All rights reserved. -*/ diff --git a/PacketTunnelProvider/nb.lproj/InfoPlist.strings b/PacketTunnelProvider/nb.lproj/InfoPlist.strings new file mode 100644 index 0000000000..89920e30c9 --- /dev/null +++ b/PacketTunnelProvider/nb.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "PacketTunnelProvider"; + +/* Bundle name */ +"CFBundleName" = "PacketTunnelProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2023 DuckDuckGo. Med enerett."; + diff --git a/PacketTunnelProvider/nb.lproj/Localizable.strings b/PacketTunnelProvider/nb.lproj/Localizable.strings index a71a759fe3..6622a5a33f 100644 --- a/PacketTunnelProvider/nb.lproj/Localizable.strings +++ b/PacketTunnelProvider/nb.lproj/Localizable.strings @@ -1,7 +1,15 @@ -/* - Localizable.strings - DuckDuckGo +/* The body of the notification shown when Network Protection fails to reconnect */ +"network.protection.failure.notification.body" = "Nettverksbeskyttelse kunne ikke koble til. Prøv igjen senere."; + +/* The body of the notification shown when Network Protection's connection is interrupted */ +"network.protection.interrupted.notification.body" = "Nettverksbeskyttelse ble avbrutt. Prøver å koble til igjen nå..."; + +/* The title of the notifications shown from Network Protection */ +"network.protection.notification.title" = "DuckDuckGo"; + +/* The body of the notification shown when Network Protection reconnects successfully */ +"network.protection.success.notification.body" = "Nettverksbeskyttelse er på. Plasseringen og nettaktiviteten din er beskyttet."; + +/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.including.serverLocation" = "Omdirigerer enhetstrafikk gjennom %@."; - Created by Graeme Arthur on 29/09/2023. - Copyright © 2023 DuckDuckGo. All rights reserved. -*/ diff --git a/PacketTunnelProvider/nl.lproj/InfoPlist.strings b/PacketTunnelProvider/nl.lproj/InfoPlist.strings new file mode 100644 index 0000000000..35059ab586 --- /dev/null +++ b/PacketTunnelProvider/nl.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "PacketTunnelProvider"; + +/* Bundle name */ +"CFBundleName" = "PacketTunnelProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2023 DuckDuckGo. Alle rechten voorbehouden."; + diff --git a/PacketTunnelProvider/nl.lproj/Localizable.strings b/PacketTunnelProvider/nl.lproj/Localizable.strings index a71a759fe3..e02130059a 100644 --- a/PacketTunnelProvider/nl.lproj/Localizable.strings +++ b/PacketTunnelProvider/nl.lproj/Localizable.strings @@ -1,7 +1,15 @@ -/* - Localizable.strings - DuckDuckGo +/* The body of the notification shown when Network Protection fails to reconnect */ +"network.protection.failure.notification.body" = "De netwerkbeveiliging kan geen verbinding maken. Probeer het later opnieuw."; + +/* The body of the notification shown when Network Protection's connection is interrupted */ +"network.protection.interrupted.notification.body" = "De netwerkbeveiliging is onderbroken. Er wordt geprobeerd opnieuw verbinding te maken ..."; + +/* The title of the notifications shown from Network Protection */ +"network.protection.notification.title" = "DuckDuckGo"; + +/* The body of the notification shown when Network Protection reconnects successfully */ +"network.protection.success.notification.body" = "De netwerkbeveiliging is ingeschakeld. Je locatie en online-activiteiten zijn beschermd."; + +/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.including.serverLocation" = "Apparaatverkeer routeren via %@."; - Created by Graeme Arthur on 29/09/2023. - Copyright © 2023 DuckDuckGo. All rights reserved. -*/ diff --git a/PacketTunnelProvider/pl.lproj/InfoPlist.strings b/PacketTunnelProvider/pl.lproj/InfoPlist.strings new file mode 100644 index 0000000000..e5aa3fd5a4 --- /dev/null +++ b/PacketTunnelProvider/pl.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "PacketTunnelProvider"; + +/* Bundle name */ +"CFBundleName" = "PacketTunnelProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2023 DuckDuckGo. Wszelkie prawa zastrzeżone."; + diff --git a/PacketTunnelProvider/pl.lproj/Localizable.strings b/PacketTunnelProvider/pl.lproj/Localizable.strings index a71a759fe3..109655cedc 100644 --- a/PacketTunnelProvider/pl.lproj/Localizable.strings +++ b/PacketTunnelProvider/pl.lproj/Localizable.strings @@ -1,7 +1,15 @@ -/* - Localizable.strings - DuckDuckGo +/* The body of the notification shown when Network Protection fails to reconnect */ +"network.protection.failure.notification.body" = "Niepowodzenie połączenia usługi ochrony sieci.Spróbuj ponownie później."; + +/* The body of the notification shown when Network Protection's connection is interrupted */ +"network.protection.interrupted.notification.body" = "Usługa ochrony sieci została przerwana. Próba ponownego połączenia..."; + +/* The title of the notifications shown from Network Protection */ +"network.protection.notification.title" = "DuckDuckGo"; + +/* The body of the notification shown when Network Protection reconnects successfully */ +"network.protection.success.notification.body" = "Usługa ochrony sieci jest włączona. Twoja lokalizacja i aktywność w Internecie są chronione."; + +/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.including.serverLocation" = "Przekierowanie ruchu urządzenia przez %@."; - Created by Graeme Arthur on 29/09/2023. - Copyright © 2023 DuckDuckGo. All rights reserved. -*/ diff --git a/PacketTunnelProvider/pt.lproj/InfoPlist.strings b/PacketTunnelProvider/pt.lproj/InfoPlist.strings new file mode 100644 index 0000000000..d91092d4ff --- /dev/null +++ b/PacketTunnelProvider/pt.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "PacketTunnelProvider"; + +/* Bundle name */ +"CFBundleName" = "PacketTunnelProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2023 DuckDuckGo. Todos os direitos reservados."; + diff --git a/PacketTunnelProvider/pt.lproj/Localizable.strings b/PacketTunnelProvider/pt.lproj/Localizable.strings index a71a759fe3..0ca58d3bbd 100644 --- a/PacketTunnelProvider/pt.lproj/Localizable.strings +++ b/PacketTunnelProvider/pt.lproj/Localizable.strings @@ -1,7 +1,15 @@ -/* - Localizable.strings - DuckDuckGo +/* The body of the notification shown when Network Protection fails to reconnect */ +"network.protection.failure.notification.body" = "Falha na ligação da Network Protection. Tenta novamente mais tarde."; + +/* The body of the notification shown when Network Protection's connection is interrupted */ +"network.protection.interrupted.notification.body" = "A Network Protection foi interrompida. A tentar ligar novamente..."; + +/* The title of the notifications shown from Network Protection */ +"network.protection.notification.title" = "DuckDuckGo"; + +/* The body of the notification shown when Network Protection reconnects successfully */ +"network.protection.success.notification.body" = "A Network Protection está ativada. A tua localização e atividade online estão protegidas."; + +/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.including.serverLocation" = "A encaminhar o tráfego do dispositivo através de %@."; - Created by Graeme Arthur on 29/09/2023. - Copyright © 2023 DuckDuckGo. All rights reserved. -*/ diff --git a/PacketTunnelProvider/ro.lproj/InfoPlist.strings b/PacketTunnelProvider/ro.lproj/InfoPlist.strings new file mode 100644 index 0000000000..626fb797f9 --- /dev/null +++ b/PacketTunnelProvider/ro.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "PacketTunnelProvider"; + +/* Bundle name */ +"CFBundleName" = "PacketTunnelProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2023 DuckDuckGo. Toate drepturile rezervate."; + diff --git a/PacketTunnelProvider/ro.lproj/Localizable.strings b/PacketTunnelProvider/ro.lproj/Localizable.strings index a71a759fe3..af14a25999 100644 --- a/PacketTunnelProvider/ro.lproj/Localizable.strings +++ b/PacketTunnelProvider/ro.lproj/Localizable.strings @@ -1,7 +1,15 @@ -/* - Localizable.strings - DuckDuckGo +/* The body of the notification shown when Network Protection fails to reconnect */ +"network.protection.failure.notification.body" = "Network Protection nu a reușit să se conecteze. Încearcă din nou mai târziu."; + +/* The body of the notification shown when Network Protection's connection is interrupted */ +"network.protection.interrupted.notification.body" = "Network Protection a fost întreruptă. Se încearcă reconectarea acum..."; + +/* The title of the notifications shown from Network Protection */ +"network.protection.notification.title" = "DuckDuckGo"; + +/* The body of the notification shown when Network Protection reconnects successfully */ +"network.protection.success.notification.body" = "Network Protection este activată. Locația și activitatea ta online sunt protejate."; + +/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.including.serverLocation" = "Dirijarea traficului dispozitivului prin %@."; - Created by Graeme Arthur on 29/09/2023. - Copyright © 2023 DuckDuckGo. All rights reserved. -*/ diff --git a/PacketTunnelProvider/ru.lproj/InfoPlist.strings b/PacketTunnelProvider/ru.lproj/InfoPlist.strings new file mode 100644 index 0000000000..ea22b697db --- /dev/null +++ b/PacketTunnelProvider/ru.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "PacketTunnelProvider"; + +/* Bundle name */ +"CFBundleName" = "PacketTunnelProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "© 2023 DuckDuckGo. Все права защищены."; + diff --git a/PacketTunnelProvider/ru.lproj/Localizable.strings b/PacketTunnelProvider/ru.lproj/Localizable.strings index a71a759fe3..f79b5934d8 100644 --- a/PacketTunnelProvider/ru.lproj/Localizable.strings +++ b/PacketTunnelProvider/ru.lproj/Localizable.strings @@ -1,7 +1,15 @@ -/* - Localizable.strings - DuckDuckGo +/* The body of the notification shown when Network Protection fails to reconnect */ +"network.protection.failure.notification.body" = "Функции защиты сети (Network Protection) не удалось установить соединение. Повторите попытку позже."; + +/* The body of the notification shown when Network Protection's connection is interrupted */ +"network.protection.interrupted.notification.body" = "Прервана работа функции защиты сети (Network Protection). Выполняется попытка повторного подключения..."; + +/* The title of the notifications shown from Network Protection */ +"network.protection.notification.title" = "DuckDuckGo"; + +/* The body of the notification shown when Network Protection reconnects successfully */ +"network.protection.success.notification.body" = "Защита сети (Network Protection) включена. Ваши геопозиция и онлайн-активность скрыты от посторонних глаз."; + +/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.including.serverLocation" = "Трафик устройства направляется через: %@"; - Created by Graeme Arthur on 29/09/2023. - Copyright © 2023 DuckDuckGo. All rights reserved. -*/ diff --git a/PacketTunnelProvider/sk.lproj/InfoPlist.strings b/PacketTunnelProvider/sk.lproj/InfoPlist.strings new file mode 100644 index 0000000000..16282e9cff --- /dev/null +++ b/PacketTunnelProvider/sk.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "PacketTunnelProvider"; + +/* Bundle name */ +"CFBundleName" = "PacketTunnelProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2023 DuckDuckGo. Všetky práva vyhradené."; + diff --git a/PacketTunnelProvider/sk.lproj/Localizable.strings b/PacketTunnelProvider/sk.lproj/Localizable.strings index a71a759fe3..a80c5e11da 100644 --- a/PacketTunnelProvider/sk.lproj/Localizable.strings +++ b/PacketTunnelProvider/sk.lproj/Localizable.strings @@ -1,7 +1,15 @@ -/* - Localizable.strings - DuckDuckGo +/* The body of the notification shown when Network Protection fails to reconnect */ +"network.protection.failure.notification.body" = "Ochrana siete sa nepripojila. Prosím, skúste to neskôr znova."; + +/* The body of the notification shown when Network Protection's connection is interrupted */ +"network.protection.interrupted.notification.body" = "Ochrana siete bola prerušená. Prebieha pokus o opätovné pripojenie..."; + +/* The title of the notifications shown from Network Protection */ +"network.protection.notification.title" = "DuckDuckGo"; + +/* The body of the notification shown when Network Protection reconnects successfully */ +"network.protection.success.notification.body" = "Ochrana siete je zapnutá. Vaša poloha a online aktivita sú chránené."; + +/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.including.serverLocation" = "Smerovanie komunikácie zariadenia cez %@."; - Created by Graeme Arthur on 29/09/2023. - Copyright © 2023 DuckDuckGo. All rights reserved. -*/ diff --git a/PacketTunnelProvider/sl.lproj/InfoPlist.strings b/PacketTunnelProvider/sl.lproj/InfoPlist.strings new file mode 100644 index 0000000000..4e8f6b44f9 --- /dev/null +++ b/PacketTunnelProvider/sl.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "PacketTunnelProvider"; + +/* Bundle name */ +"CFBundleName" = "PacketTunnelProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Avtorske pravice © 2023 DuckDuckGo. Vse pravice pridržane."; + diff --git a/PacketTunnelProvider/sl.lproj/Localizable.strings b/PacketTunnelProvider/sl.lproj/Localizable.strings index a71a759fe3..595f11f8e1 100644 --- a/PacketTunnelProvider/sl.lproj/Localizable.strings +++ b/PacketTunnelProvider/sl.lproj/Localizable.strings @@ -1,7 +1,15 @@ -/* - Localizable.strings - DuckDuckGo +/* The body of the notification shown when Network Protection fails to reconnect */ +"network.protection.failure.notification.body" = "Zaščita omrežja se ni uspela povezati. Poskusite znova pozneje."; + +/* The body of the notification shown when Network Protection's connection is interrupted */ +"network.protection.interrupted.notification.body" = "Zaščita omrežja je bila prekinjena. Poskušam se znova povezati zdaj ..."; + +/* The title of the notifications shown from Network Protection */ +"network.protection.notification.title" = "DuckDuckGo"; + +/* The body of the notification shown when Network Protection reconnects successfully */ +"network.protection.success.notification.body" = "Zaščita omrežja je vklopljena. Vaša lokacija in spletna dejavnost sta zaščiteni."; + +/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.including.serverLocation" = "Usmerjanje prometa naprave prek kraja %@."; - Created by Graeme Arthur on 29/09/2023. - Copyright © 2023 DuckDuckGo. All rights reserved. -*/ diff --git a/PacketTunnelProvider/sv.lproj/InfoPlist.strings b/PacketTunnelProvider/sv.lproj/InfoPlist.strings new file mode 100644 index 0000000000..8c3ce8a81b --- /dev/null +++ b/PacketTunnelProvider/sv.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "PacketTunnelProvider"; + +/* Bundle name */ +"CFBundleName" = "PacketTunnelProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Copyright © 2023 DuckDuckGo. Alla rättigheter förbehållna."; + diff --git a/PacketTunnelProvider/sv.lproj/Localizable.strings b/PacketTunnelProvider/sv.lproj/Localizable.strings index a71a759fe3..9d9aec7f48 100644 --- a/PacketTunnelProvider/sv.lproj/Localizable.strings +++ b/PacketTunnelProvider/sv.lproj/Localizable.strings @@ -1,7 +1,15 @@ -/* - Localizable.strings - DuckDuckGo +/* The body of the notification shown when Network Protection fails to reconnect */ +"network.protection.failure.notification.body" = "Nätverksskyddet kunde inte ansluta. Försök igen senare."; + +/* The body of the notification shown when Network Protection's connection is interrupted */ +"network.protection.interrupted.notification.body" = "Nätverksskyddet avbröts. Försöker återuppta kontakten nu..."; + +/* The title of the notifications shown from Network Protection */ +"network.protection.notification.title" = "DuckDuckGo"; + +/* The body of the notification shown when Network Protection reconnects successfully */ +"network.protection.success.notification.body" = "Nätverksskydd är På. Din plats och onlineaktivitet är skyddad."; + +/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.including.serverLocation" = "Dirigera enhetstrafik genom %@."; - Created by Graeme Arthur on 29/09/2023. - Copyright © 2023 DuckDuckGo. All rights reserved. -*/ diff --git a/PacketTunnelProvider/tr.lproj/InfoPlist.strings b/PacketTunnelProvider/tr.lproj/InfoPlist.strings new file mode 100644 index 0000000000..429bb2ee19 --- /dev/null +++ b/PacketTunnelProvider/tr.lproj/InfoPlist.strings @@ -0,0 +1,9 @@ +/* Bundle display name */ +"CFBundleDisplayName" = "PacketTunnelProvider"; + +/* Bundle name */ +"CFBundleName" = "PacketTunnelProvider"; + +/* Copyright (human-readable) */ +"NSHumanReadableCopyright" = "Telif Hakkı © 2023 DuckDuckGo. Tüm hakları saklıdır."; + diff --git a/PacketTunnelProvider/tr.lproj/Localizable.strings b/PacketTunnelProvider/tr.lproj/Localizable.strings index a71a759fe3..2bbd374209 100644 --- a/PacketTunnelProvider/tr.lproj/Localizable.strings +++ b/PacketTunnelProvider/tr.lproj/Localizable.strings @@ -1,7 +1,15 @@ -/* - Localizable.strings - DuckDuckGo +/* The body of the notification shown when Network Protection fails to reconnect */ +"network.protection.failure.notification.body" = "Ağ Koruması bağlanamadı. Lütfen daha sonra tekrar deneyin."; + +/* The body of the notification shown when Network Protection's connection is interrupted */ +"network.protection.interrupted.notification.body" = "Ağ Koruması kesintiye uğradı. Şimdi yeniden bağlanmaya çalışılıyor..."; + +/* The title of the notifications shown from Network Protection */ +"network.protection.notification.title" = "DuckDuckGo"; + +/* The body of the notification shown when Network Protection reconnects successfully */ +"network.protection.success.notification.body" = "Ağ Koruması Açık. Konumunuz ve çevrim içi etkinliğiniz korunuyor."; + +/* The body of the notification shown when Network Protection connects successfully with the city + state/country as formatted parameter */ +"network.protection.success.notification.subtitle.including.serverLocation" = "Cihaz trafiği %@ üzerinden yönlendiriliyor."; - Created by Graeme Arthur on 29/09/2023. - Copyright © 2023 DuckDuckGo. All rights reserved. -*/ diff --git a/Widgets/Assets.xcassets/vpn-off.imageset/Contents.json b/Widgets/Assets.xcassets/vpn-off.imageset/Contents.json new file mode 100644 index 0000000000..839c6e2214 --- /dev/null +++ b/Widgets/Assets.xcassets/vpn-off.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "vpn-off.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Widgets/Assets.xcassets/vpn-off.imageset/vpn-off.pdf b/Widgets/Assets.xcassets/vpn-off.imageset/vpn-off.pdf new file mode 100644 index 0000000000..8438e0ef8e Binary files /dev/null and b/Widgets/Assets.xcassets/vpn-off.imageset/vpn-off.pdf differ diff --git a/Widgets/Assets.xcassets/vpn-on.imageset/Contents.json b/Widgets/Assets.xcassets/vpn-on.imageset/Contents.json new file mode 100644 index 0000000000..ec33d453f4 --- /dev/null +++ b/Widgets/Assets.xcassets/vpn-on.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "vpn-on.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Widgets/Assets.xcassets/vpn-on.imageset/vpn-on.pdf b/Widgets/Assets.xcassets/vpn-on.imageset/vpn-on.pdf new file mode 100644 index 0000000000..09c2cae805 Binary files /dev/null and b/Widgets/Assets.xcassets/vpn-on.imageset/vpn-on.pdf differ diff --git a/Widgets/DeepLinks.swift b/Widgets/DeepLinks.swift index c3b6a4798d..0bdf850738 100644 --- a/Widgets/DeepLinks.swift +++ b/Widgets/DeepLinks.swift @@ -29,4 +29,5 @@ struct DeepLinks { static let addFavorite = AppDeepLinkSchemes.addFavorite.url + static let openVPN = AppDeepLinkSchemes.openVPN.url } diff --git a/Widgets/VPNWidget.swift b/Widgets/VPNWidget.swift new file mode 100644 index 0000000000..c7a7681d5b --- /dev/null +++ b/Widgets/VPNWidget.swift @@ -0,0 +1,301 @@ +// +// VPNWidget.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import AppIntents +import Core +import DesignResourcesKit +import SwiftUI +import WidgetKit +import NetworkExtension +import NetworkProtection + +#if ALPHA + +enum VPNStatus { + case status(NEVPNStatus) + case error + case notConfigured +} +struct VPNStatusTimelineEntry: TimelineEntry { + let date: Date + let status: VPNStatus + let location: String + + internal init(date: Date, status: VPNStatus = .notConfigured, location: String) { + self.date = date + self.status = status + self.location = location + } +} + +class VPNStatusTimelineProvider: TimelineProvider { + + typealias Entry = VPNStatusTimelineEntry + + func placeholder(in context: Context) -> VPNStatusTimelineEntry { + return VPNStatusTimelineEntry(date: Date(), status: .status(.connected), location: "Los Angeles, CA") + } + + func getSnapshot(in context: Context, completion: @escaping (VPNStatusTimelineEntry) -> Void) { + let entry = VPNStatusTimelineEntry(date: Date(), status: .status(.connected), location: "Los Angeles, CA") + completion(entry) + } + + func getTimeline(in context: Context, completion: @escaping (Timeline) -> Void) { + NETunnelProviderManager.loadAllFromPreferences { managers, error in + let defaults = UserDefaults.networkProtectionGroupDefaults + let location = defaults.string(forKey: NetworkProtectionUserDefaultKeys.lastSelectedServer) ?? "Unknown Location" + let expiration = Date().addingTimeInterval(TimeInterval.minutes(5)) + + if error != nil { + let entry = VPNStatusTimelineEntry(date: expiration, status: .error, location: location) + let timeline = Timeline(entries: [entry], policy: .atEnd) + completion(timeline) + return + } + + guard let manager = managers?.first else { + let entry = VPNStatusTimelineEntry(date: expiration, status: .notConfigured, location: location) + let timeline = Timeline(entries: [entry], policy: .atEnd) + completion(timeline) + return + } + + let status = manager.connection.status + let entry = VPNStatusTimelineEntry(date: expiration, status: .status(status), location: location) + let timeline = Timeline(entries: [entry], policy: .atEnd) + + completion(timeline) + } + } +} + +extension NEVPNStatus { + var description: String { + switch self { + case .connected: return "Connected" + case .connecting: return "Connecting" + case .disconnected: return "Disconnected" + case .disconnecting: return "Disconnecting" + case .invalid: return "Invalid" + case .reasserting: return "Reasserting" + default: return "Unknown Status" + } + } + + var isConnected: Bool { + switch self { + case .connected, .connecting, .reasserting: return true + case .disconnecting, .disconnected: return false + default: return false + } + } +} + +@available(iOSApplicationExtension 17.0, *) +struct VPNStatusView: View { + @Environment(\.widgetFamily) var family: WidgetFamily + var entry: VPNStatusTimelineProvider.Entry + + @ViewBuilder + var body: some View { + Group { + switch entry.status { + case .status(let status): + HStack { + connectionView(with: status) + .padding([.leading, .trailing], 16) + + Spacer() + } + case .error: + Text("Error") + .foregroundStyle(Color.black) + case .notConfigured: + Text("VPN Not Configured") + .foregroundStyle(Color.black) + } + } + .frame(maxWidth: .infinity, maxHeight: .infinity) + .containerBackground(for: .widget) { + switch entry.status { + case .status(let status): + switch status { + case .connecting, .connected, .reasserting: + Color.vpnWidgetBackgroundColor + case .disconnecting, .disconnected, .invalid: + Color.white + @unknown default: + Color.white + } + case .error, .notConfigured: + Color.white + } + } + } + + private func connectionView(with status: NEVPNStatus) -> some View { + HStack { + VStack(alignment: .leading, spacing: 0) { + Image(headerImageName(with: status)) + .frame(width: 50, height: 54) + .padding(.top, 15) + + Spacer() + + Text(title(with: status)) + .font(.system(size: 16, weight: .semibold)) + .fontWeight(.semibold) + .foregroundStyle(status.isConnected ? Color.white : Color.black) + + Text(status.isConnected ? entry.location : "VPN is Off") + .font(.system(size: 12, weight: .medium)) + .foregroundStyle(status.isConnected ? Color.white : Color.black) + .opacity(status.isConnected ? 0.8 : 0.6) + + switch status { + case .connected, .connecting, .reasserting: + Button(intent: DisableVPNIntent()) { + Text("Disconnect") + .font(.system(size: 15, weight: .medium)) + .fontWeight(.semibold) + } + .foregroundStyle(Color.vpnWidgetBackgroundColor) + .buttonStyle(.borderedProminent) + .tint(.white) + .disabled(status != .connected) + .padding(.top, 6) + .padding(.bottom, 16) + case .disconnected, .disconnecting: + Button(intent: EnableVPNIntent()) { + Text("Connect") + .font(.system(size: 15, weight: .medium)) + .fontWeight(.semibold) + } + .foregroundStyle(.white) + .buttonStyle(.borderedProminent) + .tint(Color.vpnWidgetBackgroundColor) + .disabled(status != .disconnected) + .padding(.top, 6) + .padding(.bottom, 16) + default: + Spacer() + } + } + } + } + + private func headerImageName(with status: NEVPNStatus) -> String { + switch status { + case .connecting, .connected, .reasserting: return "vpn-on" + case .disconnecting, .disconnected: return "vpn-off" + case .invalid: return "vpn-off" + @unknown default: return "vpn-off" + } + } + + private func title(with status: NEVPNStatus) -> String { + switch status { + case .connecting, .connected, .reasserting: return "Protected" + case .disconnecting, .disconnected: return "Unprotected" + case .invalid: return "Invalid" + @unknown default: return "Unknown" + } + } + +} + +@available(iOSApplicationExtension 17.0, *) +struct VPNStatusWidget: Widget { + let kind: String = "VPNStatusWidget" + + var body: some WidgetConfiguration { + StaticConfiguration(kind: kind, provider: VPNStatusTimelineProvider()) { entry in + VPNStatusView(entry: entry).widgetURL(DeepLinks.openVPN) + } + .configurationDisplayName("VPN Status") + .description("View and manage the VPN connection") + .supportedFamilies([.systemSmall]) + .contentMarginsDisabled() + } +} + +struct VPNStatusView_Previews: PreviewProvider { + + static let connectedState = VPNStatusTimelineProvider.Entry( + date: Date(), + status: .status(.connected), + location: "Paoli, PA" + ) + + static let disconnectedState = VPNStatusTimelineProvider.Entry( + date: Date(), + status: .status(.disconnected), + location: "Paoli, PA" + ) + + static let notConfiguredState = VPNStatusTimelineProvider.Entry( + date: Date(), + status: .notConfigured, + location: "Paoli, PA" + ) + + static var previews: some View { + if #available(iOSApplicationExtension 17.0, *) { + VPNStatusView(entry: connectedState) + .previewContext(WidgetPreviewContext(family: .systemSmall)) + .environment(\.colorScheme, .light) + + VPNStatusView(entry: connectedState) + .previewContext(WidgetPreviewContext(family: .systemSmall)) + .environment(\.colorScheme, .dark) + + VPNStatusView(entry: disconnectedState) + .previewContext(WidgetPreviewContext(family: .systemSmall)) + .environment(\.colorScheme, .light) + + VPNStatusView(entry: disconnectedState) + .previewContext(WidgetPreviewContext(family: .systemSmall)) + .environment(\.colorScheme, .dark) + + VPNStatusView(entry: notConfiguredState) + .previewContext(WidgetPreviewContext(family: .systemSmall)) + .environment(\.colorScheme, .light) + + VPNStatusView(entry: notConfiguredState) + .previewContext(WidgetPreviewContext(family: .systemSmall)) + .environment(\.colorScheme, .dark) + } else { + Text("iOS 17 required") + } + } + +} + +extension Color { + + static var vpnWidgetBackgroundColor: Color { + let color = UIColor(designSystemColor: .accent).resolvedColor(with: UITraitCollection(userInterfaceStyle: .light)) + return Color(color) + } + +} + +#endif diff --git a/Widgets/Widgets.swift b/Widgets/Widgets.swift index 1822daa082..39ab7b0057 100644 --- a/Widgets/Widgets.swift +++ b/Widgets/Widgets.swift @@ -25,6 +25,7 @@ import CoreData import Kingfisher import Bookmarks import Persistence +import NetworkExtension struct Favorite { @@ -112,7 +113,7 @@ class Provider: TimelineProvider { if maxFavorites > 0, let db = bookmarksDB { - let model = FavoritesListViewModel(bookmarksDatabase: db) + let model = FavoritesListViewModel(bookmarksDatabase: db, favoritesDisplayMode: fetchFavoritesDisplayMode()) os_log("model created") let dbFavorites = model.favorites os_log("dbFavorites loaded %d", dbFavorites.count) @@ -127,6 +128,16 @@ class Provider: TimelineProvider { } } + private func fetchFavoritesDisplayMode() -> FavoritesDisplayMode { + let userDefaults = UserDefaults(suiteName: "group.com.duckduckgo.bookmarks") + let displayModeDescription = userDefaults?.string(forKey: "com.duckduckgo.ios.favoritesDisplayMode") + + if let displayModeDescription, let displayMode = FavoritesDisplayMode(displayModeDescription) { + return displayMode + } + return .displayNative(.mobile) + } + private func loadImageFromCache(forDomain domain: String?) -> UIImage? { guard let domain = domain else { return nil } @@ -192,6 +203,12 @@ struct Widgets: WidgetBundle { SearchWidget() FavoritesWidget() +#if ALPHA + if #available(iOSApplicationExtension 17.0, *) { + VPNStatusWidget() + } +#endif + if #available(iOSApplicationExtension 16.0, *) { SearchLockScreenWidget() VoiceSearchLockScreenWidget() diff --git a/Widgets/WidgetsExtension.entitlements b/Widgets/WidgetsExtension.entitlements index 2dd2c82001..4d39b3e562 100644 --- a/Widgets/WidgetsExtension.entitlements +++ b/Widgets/WidgetsExtension.entitlements @@ -6,6 +6,7 @@ $(GROUP_ID_PREFIX).bookmarks $(GROUP_ID_PREFIX).database + $(GROUP_ID_PREFIX).netp
diff --git a/WidgetsExtensionAlpha.entitlements b/WidgetsExtensionAlpha.entitlements index d5324a15bd..4d39b3e562 100644 --- a/WidgetsExtensionAlpha.entitlements +++ b/WidgetsExtensionAlpha.entitlements @@ -4,8 +4,9 @@ com.apple.security.application-groups - group.com.duckduckgo.alpha.database - group.com.duckduckgo.alpha.bookmarks + $(GROUP_ID_PREFIX).bookmarks + $(GROUP_ID_PREFIX).database + $(GROUP_ID_PREFIX).netp diff --git a/fastlane/Fastfile b/fastlane/Fastfile index a4eef3a73b..755788c816 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -157,18 +157,6 @@ lane :increment_build_number_for_version do |options| }) end -desc 'Setup Maestro e2e tests' -lane :setup_e2e_tests do |options| - shared_folder = 'shared' - e2e_test_folders = [ - 'release_tests', - 'privacy_tests', - 'ad_click_detection_flow_tests' - ] - - e2e_test_folders.each { | test_folder | FileUtils.copy_entry "../.maestro/#{shared_folder}", "../.maestro/#{test_folder}/#{shared_folder}" } -end - ################################################# # Private lanes ################################################# diff --git a/fastlane/README.md b/fastlane/README.md index 452c9fec13..5ca5e832ea 100644 --- a/fastlane/README.md +++ b/fastlane/README.md @@ -85,14 +85,6 @@ Makes Alpha release build and uploads it to TestFlight Increment build number based on version in App Store Connect -### setup_e2e_tests - -```sh -[bundle exec] fastlane setup_e2e_tests -``` - -Setup Maestro e2e tests - ---- This README.md is auto-generated and will be re-generated every time [_fastlane_](https://fastlane.tools) is run. diff --git a/fastlane/metadata/default/release_notes.txt b/fastlane/metadata/default/release_notes.txt index 4942898476..318421afc6 100644 --- a/fastlane/metadata/default/release_notes.txt +++ b/fastlane/metadata/default/release_notes.txt @@ -1,3 +1,4 @@ +We fixed a rare issue that caused text selection in the address bar to not work as expected. Bug fixes and other improvements. Join our fully distributed team and help raise the standard of trust online! https://duckduckgo.com/hiring diff --git a/package-lock.json b/package-lock.json index 85e777a32f..a096c1ea3b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "name": "ios", "version": "1.0.0", "dependencies": { - "@duckduckgo/autoconsent": "^6.0.0" + "@duckduckgo/autoconsent": "^6.4.0" }, "devDependencies": { "@rollup/plugin-json": "^4.1.0", @@ -135,9 +135,9 @@ } }, "node_modules/@duckduckgo/autoconsent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@duckduckgo/autoconsent/-/autoconsent-6.0.0.tgz", - "integrity": "sha512-RcvC2sD8JY/QnMWdKQnMZMb1aCw6lEgJsT9OF7y/rZRUQKOIhDq6U8dJ///vZQjeVkmmJnR/T2ljLN0Qfvyy1g==" + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@duckduckgo/autoconsent/-/autoconsent-6.4.0.tgz", + "integrity": "sha512-WhQnE0Zy8ArMyo78tTZSOXVn9IQ0qut4INLHvt7kyPkzQVhfrckVwSlehlTETFu22NkuX1jUW1GaugFtxnYJWg==" }, "node_modules/@eslint/eslintrc": { "version": "0.4.3", diff --git a/package.json b/package.json index 664bbde3b5..bd1ed62a18 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,6 @@ "rollup-plugin-terser": "^7.0.2" }, "dependencies": { - "@duckduckgo/autoconsent": "^6.0.0" + "@duckduckgo/autoconsent": "^6.4.0" } } diff --git a/scripts/update_embedded.sh b/scripts/update_embedded.sh index 892183b2c3..88cde7f816 100755 --- a/scripts/update_embedded.sh +++ b/scripts/update_embedded.sh @@ -60,5 +60,6 @@ performUpdate() { rm -f "$TEMP_ETAG_FILENAME" } +# The following URLs shall match the ones in AppURLs.swift. Danger checks that the URLs match on every PR. If the code changes, the regex that Danger uses may need an update. performUpdate 'https://staticcdn.duckduckgo.com/trackerblocking/v5/current/ios-tds.json' "${base_dir}/Core/AppTrackerDataSetProvider.swift" "${base_dir}/Core/trackerData.json" -performUpdate 'https://staticcdn.duckduckgo.com/trackerblocking/config/v2/ios-config.json' "${base_dir}/Core/AppPrivacyConfigurationDataProvider.swift" "${base_dir}/Core/ios-config.json" +performUpdate 'https://staticcdn.duckduckgo.com/trackerblocking/config/v4/ios-config.json' "${base_dir}/Core/AppPrivacyConfigurationDataProvider.swift" "${base_dir}/Core/ios-config.json" diff --git a/submodules/privacy-reference-tests b/submodules/privacy-reference-tests index 0d23f76801..a3acc21947 160000 --- a/submodules/privacy-reference-tests +++ b/submodules/privacy-reference-tests @@ -1 +1 @@ -Subproject commit 0d23f76801c2e73ae7d5ed7daa4af4aca5beec73 +Subproject commit a3acc2194758bec0f01f57dd0c5f106de01a354e